从简单的 java 对象 (POJO) getter 映射到 setter 的好方法是什么?可以使用反射吗?

What is a good way for getter to setter mapping from simple java objects (POJOs). Can is use reflection for it?

嗨,我有两个 java 对象,它们彼此独立,但共享相同的 gettersetter 方法。例如,我有一个 UserEntityUserDTO 对象,我会将所有 getter 值从 UserEntity 映射到所有 setter 的 UserDTO。

做这类事情的最佳方法是什么?我更喜欢用 java 反射来做,但如果外面有一些有用的框架实现,我现在也想这样做。

使用反射不使用外部库

您好,您可以检查源对象的所有 getter,并尝试在目标对象上找到所有相应的 setter。以下方法应该可以做到:

 /**
 * Maps all getter of the source to all corresponding setter of the target
 * @param target
 *      target object which should be set
 * @param source
 *      source object to read from
 * @return
 *      returns mapped target object or null if some error occurred
 */
private Object getterToSetter(Object target, Object source) {
    try {
        for(Method m: source.getClass().getMethods()){
            // try to find for all getters in the source the corresponding setter on the target
            if(m.getName().startsWith("get")){
                Method setter;
                try {
                    setter = target.getClass().getMethod("set" + m.getName().substring(3), m.getReturnType());
                    setter.invoke(target,m.invoke(source));
                } catch (NoSuchMethodException e) {
                    //ignored target just haven't a setter for this field
                }
            }
        }
        // return value to support builder pattern
        return target;
    }catch (IllegalAccessException| InvocationTargetException e){
        e.printStackTrace();
    }
    return null;
}

这是一个完整的Main.class示例,说明它是如何工作的:

    import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author Andreas Hauschild
 */
public class Main {
    public static void main(String[] args) {
        UserEntity userEntity = new UserEntity().setName("Waltraud").setLastName("Schmidt").setPassword("TopSecret");
        UserDTO dto = (UserDTO) getterToSetter(new UserDTO(), userEntity);
        System.out.println(dto.getName());      //  prints "Waltraud"
        System.out.println(dto.getLastName());  //  prints "Schmidt"
    }

    /**
     * Maps all getter of the source to all corresponding setter of the target
     * @param target
     *      target object which should be set
     * @param source
     *      source object to read from
     * @return
     *      returns mapped target object or null if some error occurred
     */
    private static Object getterToSetter(Object target, Object source) {
        try {
            for(Method m: source.getClass().getMethods()){
                // try to find for all getters in the source the corresponding setter on the target
                if(m.getName().startsWith("get")){
                    Method setter;
                    try {
                        setter = target.getClass().getMethod("set" + m.getName().substring(3), m.getReturnType());
                        setter.invoke(target,m.invoke(source));
                    } catch (NoSuchMethodException e) {
                        //ignored target just haven't a setter for this field
                    }
                }
            }
            // return value to support builder pattern
            return target;
        }catch (IllegalAccessException| InvocationTargetException e){
            e.printStackTrace();
        }
        return null;
    }

    public static class UserEntity {
        String password;
        String name;
        String lastName;

        public String getPassword() {
            return password;
        }

        public UserEntity setPassword(String password) {
            this.password = password;
            return this;
        }

        public String getName() {
            return name;
        }

        public UserEntity setName(String name) {
            this.name = name;
            return this;
        }

        public String getLastName() {
            return lastName;
        }

        public UserEntity setLastName(String lastName) {
            this.lastName = lastName;
            return this;
        }
    }

    public static class UserDTO {
        String name;
        String lastName;

        public String getName() {
            return name;
        }

        public UserDTO setName(String name) {
            this.name = name;
            return this;
        }

        public String getLastName() {
            return lastName;
        }

        public UserDTO setLastName(String lastName) {
            this.lastName = lastName;
            return this;
        }
    }

}

这种需求通常最好用 "converter" 或 "mapper" 模式来实现。我在 Java 中生成映射器的首选工具是 MapStruct,但也存在许多其他工具。

使用反射通常不是最佳选择,因为它速度较慢,而且更难以跟踪和自定义(例如,如果字段需要在 JSON 和 [=10= 中的字符串之间转换) ]s 在 Java).

有一个很棒的映射器库,叫做 MapStruct,可以用来映射。

这里是您的 UserEntity 和 UserDTO 的示例:

@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping
    UserDTO userEntityToUserDTO(UserEntity user); 
}

UserDTO userDto = UserMapper.INSTANCE.userEntityToUserDTO(userEntity);

它还支持兼容类型之间的转换,如果字段名称不同,也可以设置自定义映射。例如:

@Mapping(source = "userName", target = "name")