public static Teacher newTeacher() {
    Teacher teacher = new Teacher();
    teacher.setName("teacher");
    teacher.setAge(36);
    teacher.setSex((byte) 1);
    teacher.setHeight(188.0);
    teacher.setWeight(120F);
    teacher.setHandsome(true);
    teacher.setHouse(ImmutableMap.of("home", "bj"));
    teacher.setLover(new Teacher("lover", 36));
    teacher.setStudents(newArrayList(newMaleStudent(), newFemaleStudent()));
    return teacher;
} public static TeacherEntity newTeacherEntity() {
    TeacherEntity teacher = new TeacherEntity();
    teacher.setUsername("teacher");
    teacher.setAge(36);
    teacher.setSex((byte) 1);
    teacher.setHeight(188.0);
    teacher.setWeight(120L);
    teacher.setHandsome(true);
    teacher.setHobbits(newArrayList("说", "学"));
    teacher.setHouse(ImmutableMap.of("home", "bj"));
    teacher.setLover(new TeacherEntity("lover", 36));
    teacher.setStudents(newArrayList(newMaleStudentEntity(), newFemaleStudentEntity()));
    return teacher;
} public final class Copiers {

    private Copiers() {
        throw new UnsupportedOperationException();
    }

    /**
     * 基于 Orika 实现的简单拷贝，满足基本需求
     *
     * @param sourceClass 源对象类型
     * @param targetClass 目标对象类型
     * @return copier
     */
    public static <F, T> Copier<F, T> create(Class<F> sourceClass, Class<T> targetClass) {
        return CopierFactory.getOrCreateOrikaCopier(sourceClass, targetClass);
    }

    /**
     * 基于 Orika 实现的高级拷贝，满足复杂需求
     *
     * @param sourceClass 源对象类型
     * @param targetClass 目标对象类型
     * @return copier
     */
    public static <F, T> OrikaCopier.Builder<F, T> createOrika(Class<F> sourceClass, Class<T> targetClass) {
        return new OrikaCopier.Builder<>(sourceClass, targetClass);
    }

    /**
     * 基于 Cglib 实现的拷贝，不支持 Converter
     *
     * @param sourceClass 源对象类型
     * @param targetClass 目标对象类型
     * @return copier
     */
    public static <F, T> Copier<F, T> createCglib(Class<F> sourceClass, Class<T> targetClass) {
        return CopierFactory.getOrCreateCglibCopier(sourceClass, targetClass);
    }

    /**
     * 基于 Cglib 实现的拷贝，支持 Converter
     *
     * @param sourceClass 源对象类型
     * @param targetClass 目标对象类型
     * @param converter   转换器
     * @return copier
     */
    public static <F, T> Copier<F, T> createCglib(Class<F> sourceClass, Class<T> targetClass, Converter converter) {
        return CopierFactory.getOrCreateCglibCopier(sourceClass, targetClass, converter);
    }

} public interface Copier<F, T> extends BeanCopier<F, T>, ArrayCopier<F, T>, ListCopier<F, T>, SetCopier<F, T> {

} @Slf4j(topic = "copiers")
public class OrikaCopier<F, T> extends AbstractCopier<BoundMapperFacade<F, T>, F, T> {

    /**
     * 创建默认的 OrikaCopier
     *
     * @param sourceClass 源类型
     * @param targetClass 目标类型
     */
    private OrikaCopier(Class<F> sourceClass, Class<T> targetClass) {
        super(sourceClass, targetClass,
                OrikaMapperFactory.getMapperFactory().getMapperFacade(sourceClass, targetClass));
    }

    @Override
    public T copy(F source) {
        checkNotNull(source, "source bean cannot be null!");
        try {
            return copier.map(source);
        } catch (Exception e) {
            throw new CopierException("create object fail, class: " + targetClass.getName(), e);
        }
    }

    @Override
    public void copy(F source, T target) {
        checkNotNull(source, "source bean cannot be null!");
        checkNotNull(target, "target bean cannot be null!");
        try {
            copier.map(source, target);
        } catch (Exception e) {
            throw new CopierException("create object fail, class: " + targetClass.getName(), e);
        }
    }

    public static class Builder<F, T> {

        /**
         * 自定义 Copier
         */
        private ClassMapBuilder<F, T> builder;

        public Builder(Class<F> sourceClass, Class<T> targetClass) {
            this.builder = OrikaMapperFactory.getMapperFactory().classMap(sourceClass, targetClass);
        }

        /**
         * 自定义属性映射
         *
         * @param sourceField 源对象属性名称
         * @param targetField 目标对象属性名称
         * @return this
         */
        public OrikaCopier.Builder<F, T> field(String sourceField, String targetField) {
            builder.field(sourceField, targetField);
            return this;
        }

        /**
         * 自定义属性映射
         *
         * @param sourceField 源对象属性名称
         * @param targetField 目标对象属性名称
         * @param converterId 自定义转换规则
         * @return this
         */
        public OrikaCopier.Builder<F, T> field(String sourceField, String targetField, String converterId) {
            builder.fieldMap(sourceField, targetField)
                    .converter(converterId)
                    .add();
            return this;
        }

        /**
         * 自定义属性映射
         *
         * @param sourceField 源对象属性名称
         * @param targetField 目标对象属性名称
         * @param sourceType  源对象属性类型
         * @param targetType  目标对象属性类型
         * @return this
         */
        public OrikaCopier.Builder<F, T> field(String sourceField, String targetField, Class<?> sourceType, Class<?> targetType) {
            builder.fieldMap(sourceField, targetField)
                    .aElementType(sourceType)
                    .bElementType(targetType)
                    .add();
            return this;
        }

        /**
         * 自定义属性映射
         *
         * @param sourceField 源对象属性名称
         * @param targetField 目标对象属性名称
         * @param sourceType  源对象属性类型
         * @param targetType  目标对象属性类型
         * @param converterId 自定义转换规则
         * @return this
         */
        public OrikaCopier.Builder<F, T> field(String sourceField, String targetField, Class<?> sourceType, Class<?> targetType, String converterId) {
            builder.fieldMap(sourceField, targetField)
                    .aElementType(sourceType)
                    .bElementType(targetType)
                    .converter(converterId)
                    .add();
            return this;
        }

        /**
         * 是否拷贝值为 null 的属性
         *
         * @return this
         */
        public OrikaCopier.Builder<F, T> nulls() {
            builder.mapNulls(true);
            return this;
        }

        /**
         * 排除属性
         *
         * @param fields 要排除的属性名称
         * @return this
         */
        public OrikaCopier.Builder<F, T> skip(String... fields) {
            if (fields != null && fields.length != 0) {
                for (String field : fields) {
                    builder.exclude(field);
                }
                // Orika 默认使用全参构造，这时 skip() 不生效，需要使用不包含 skip 属性的构造方法，
                // 所以 Copiers 将默认值改为了无参构造，用户也可以在调用 skip() 后使用 constructor() 方法自己指定
                // https://github.com/orika-mapper/orika/issues/135
                builder.constructorB();
            }
            return this;
        }

        /**
         * 自定义映射规则
         *
         * @param customizedMapper 自定义映射规则
         * @return this
         */
        public Builder<F, T> customize(Mapper<F, T> customizedMapper) {
            builder.customize(customizedMapper);
            return this;
        }

        /**
         * 自定义构造器
         *
         * @param args 构造参数
         * @return this
         */
        public Builder<F, T> constructor(String... args) {
            builder.constructorB(args);
            return this;
        }

        /**
         * 自定义构造器
         *
         * @param parentSourceClass 源对象父类类型
         * @param parentTargetClass 目标对象父类类型
         * @return this
         */
        public OrikaCopier.Builder<F, T> parent(Class<?> parentSourceClass, Class<?> parentTargetClass) {
            builder.use(parentSourceClass, parentTargetClass);
            return this;
        }

        /**
         * 构建执行拷贝的 Copier
         *
         * @return copier
         */
        public OrikaCopier<F, T> register() {
            builder.byDefault().register();
            Class<F> sourceClass = builder.getAType().getRawType();
            Class<T> targetClass = builder.getBType().getRawType();
            return new OrikaCopier<>(sourceClass, targetClass);
        }

    }

} @Getter
@Setter
@ToString
public class SimpleSource {

    private Integer id;
    private Long time;
    private List<Integer> statusList;
    private List<Double> typeList;
    private Map<String, Object> map;
    private String same;

//    public SimpleSource() {
//    }

    public SimpleSource(Integer id) {
        this.id = id;
    }

    public SimpleSource(Integer id, Long time) {
        this.id = id;
        this.time = time;
    }

} @Getter
@Setter
@ToString
public class SimpleTarget {

    private String id;
    private Date time;
    private String statuses;
    private String types;
    private String name;
    private String same;

//    public SimpleTarget() {
//    }

    public SimpleTarget(String id) {
        this.id = id;
    }

    public SimpleTarget(Date time) {
        this.time = time;
    }

    public SimpleTarget(String id, Date time) {
        this.id = id;
        this.time = time;
    }

} @Data
@Builder
@AllArgsConstructor
public class Student implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private byte sex;
    private double height;
    private float weight;
    private boolean handsome;
    private List<String> hobbits;

    public Student() { }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper("Student").omitNullValues()
                .add("name", name)
                .add("age", age)
                .add("sex", sex)
                .add("height", height)
                .add("weight", weight)
                .add("handsome", handsome)
                .add("hobbits", hobbits)
                .toString();
    }

} @Data
@Builder
@AllArgsConstructor
public class StudentEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private byte sex;
    private double height;
    private float weight;
    private boolean handsome;
    private List<String> hobbits;

    public StudentEntity() { }

    public StudentEntity(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper("Student").omitNullValues()
                .add("name", name)
                .add("age", age)
                .add("sex", sex)
                .add("height", height)
                .add("weight", weight)
                .add("handsome", handsome)
                .add("hobbits", hobbits)
                .toString();
    }

} @Data
@Builder
@AllArgsConstructor
public class Teacher implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private Integer age;
    private Byte sex;
    private Double height;
    private Float weight;
    private Boolean handsome;
    private Map<String, Object> house;
    private Teacher lover;
    private List<Student> students;

    public Teacher() { }

    public Teacher(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper("Teacher").omitNullValues()
                .add("name", name)
                .add("age", age)
                .add("sex", sex)
                .add("height", height)
                .add("weight", weight)
                .add("handsome", handsome)
                .add("house", house)
                .add("lover", lover)
                .add("students", students)
                .toString();
    }

} @Data
@Builder
@AllArgsConstructor
public class TeacherEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    private String username;
    private Integer age;
    private Byte sex;
    private Double height;
    private Long weight;
    private Boolean handsome;
    private List<String> hobbits;
    private Map<String, Object> house;
    private TeacherEntity lover;
    private List<StudentEntity> students;

    public TeacherEntity() { }

    public TeacherEntity(String name, Integer age) {
        this.username = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper("Teacher").omitNullValues()
                .add("username", username)
                .add("age", age)
                .add("sex", sex)
                .add("height", height)
                .add("weight", weight)
                .add("handsome", handsome)
                .add("hobbits", hobbits)
                .add("house", house)
                .add("lover", lover)
                .add("students", students)
                .toString();
    }

}
import static com.github.trang.copiers.test.util.MockUtils.newTeacher;
import static com.github.trang.copiers.test.util.MockUtils.newTeacherEntity;
import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Ignore;
import org.junit.Test;
import com.github.trang.copiers.Copiers;
import com.github.trang.copiers.base.Copier;
import com.github.trang.copiers.orika.OrikaCopier;
import com.github.trang.copiers.test.bean.SimpleSource;
import com.github.trang.copiers.test.bean.SimpleTarget;
import com.github.trang.copiers.test.bean.Student;
import com.github.trang.copiers.test.bean.StudentEntity;
import com.github.trang.copiers.test.bean.Teacher;
import com.github.trang.copiers.test.bean.TeacherEntity;
import ma.glasnost.orika.CustomMapper;
import ma.glasnost.orika.MappingContext;
package com.github.trang.copiers.test.coveralls;





/**
 * OrikaCopierTest
 * <p>
 * Write the code. Change the world.
 *
 * @author trang
 * @date 2018/6/21
 */
@SuppressWarnings("Duplicates")
public class OrikaCopierTest {

    private static Copier<Teacher, TeacherEntity> copier = Copiers.create(Teacher.class, TeacherEntity.class);
    private static Copier<TeacherEntity, Teacher> reverseCopier = Copiers.create(TeacherEntity.class, Teacher.class);

    // orika 支持级联拷贝，当 pojo 中有级联对象时需要提前注册映射关系，否则会抛出异常
    private static Copier<Student, StudentEntity> useless1 = Copiers.create(Student.class, StudentEntity.class);
    private static Copier<StudentEntity, Student> useless2 = Copiers.create(StudentEntity.class, Student.class);

    @Test
    public void copyTest() {
        Teacher teacher = newTeacher();
        TeacherEntity target = copier.copy(teacher);
        // 名称不同，忽略转换
        assertThat(target.getUsername()).isNull();
        assertThat(target.getAge()).isEqualTo(teacher.getAge());
        assertThat(target.getSex()).isEqualTo(teacher.getSex());
        assertThat(target.getHeight()).isEqualTo(teacher.getHeight());
        // 类型不同，由默认转换器转换
        assertThat(target.getWeight()).isEqualTo(teacher.getWeight().longValue());
        assertThat(target.getHandsome()).isEqualTo(teacher.getHandsome());
        assertThat(target.getHouse()).isEqualTo(teacher.getHouse());
        // 名称不同，忽略转换
        assertThat(target.getLover().getUsername()).isNull();
        // 相同类型的级联不需要提前注册映射关系
        assertThat(target.getLover().getAge()).isEqualTo(teacher.getLover().getAge());
        // 支持级联拷贝，可以转换
        assertThat(target.getStudents().toString()).isEqualTo(teacher.getStudents().toString());
    }

    @Test
    public void reverseCopyTest() {
        TeacherEntity teacher = newTeacherEntity();