자바 어떻게 우아 한 복사 대상 속성

장면
자바 프로젝트 에 서 는 대상 간 에 속성 을 복사 해 야 하 는 문제 가 자주 발생 합 니 다.그러나 Getter/Stter 방법 을 직접 사용 하 는 것 외 에 우리 에 게 또 다른 방법 이 있 습 니까?
물론 있 습 니 다.예 를 들 어 Apache Common Lang 3 의 BeanUtils 가 있 지만 BeanUtils 는 우리 세대 의 요 구 를 완전히 만족 시 키 지 못 하기 때문에 우 리 는 스스로 하 나 를 봉 하여 참고 할 수 있 도록 공유 합 니 다.
  • 대상 의 속성 을 대량으로 복제 해 야 한다
  • 대상 간 속성 명 이 다 를 수 있 습 니 다
  • 대상 간 의 속성 유형 이 다 를 수 있 습 니 다
  • 목표.
    간단 하고 사용 하기 쉬 운 API
  • copy:복사 할 원본 대상 과 대상 지정
  • prop:지정 한 대상 의 필드 복사
  • props:지정 한 대상 의 여러 필드 복사
  • exec:진정한 복사 작업 수행
  • from:다른 대상 의 속성 추가 다시 시작
  • get:현재 대상 으로 돌아 가기
  • config:복사 정책 설정
  • 사고의 방향
  • 겉면 류 BeanCopyUtil 을 정의 하여 일부 API 를 노출 시 킵 니 다
  • 필드 마다 작업 클래스 BeanCopyField 를 정의 하고 필드 마다 작업 저장
  • BeanCopyConfig 를 정의 하여 복사 속성 을 설정 하 는 정책
  • BeanCopy Operator 를 복사 의 진정한 실현 으로 정의
  • 도해

    이루어지다
    주:반사 부분 의존joor,JDK 1.8 사용joor-java-8
    일부 API 를 노출 시 키 기 위해 서 겉보기 클래스 BeanCopyUtil 을 정의 합 니 다.
    
    /**
     * java bean         
     *
     * @author rxliuli
     */
    public class BeanCopyUtil<F, T> {
      /**
       *    
       */
      private final F from;
      /**
       *     
       */
      private final T to;
      /**
       *          
       */
      private final List<BeanCopyField> copyFieldList = new LinkedList<>();
      /**
       *     
       */
      private BeanCopyConfig config = new BeanCopyConfig();
    
      private BeanCopyUtil(F from, T to) {
        this.from = from;
        this.to = to;
      }
    
      /**
       *                
       *
       * @param from    
       * @param to      
       * @param <F>      
       * @param <T>       
       * @return    {@link BeanCopyUtil}   
       */
      public static <F, T> BeanCopyUtil<F, T> copy(F from, T to) {
        return new BeanCopyUtil<>(from, to);
      }
    
      /**
       *          
       *
       * @param fromField         
       * @param toField           
       * @param converter                       
       * @return    {@code this}
       */
      public BeanCopyUtil<F, T> prop(String fromField, String toField, Function<? super Object, ? super Object> converter) {
        copyFieldList.add(new BeanCopyField(fromField, toField, converter));
        return this;
      }
    
      /**
       *          
       *
       * @param fromField         
       * @param toField           
       * @return    {@code this}
       */
      public BeanCopyUtil<F, T> prop(String fromField, String toField) {
        return prop(fromField, toField, null);
      }
    
      /**
       *          
       *
       * @param field                 
       * @param converter                       
       * @return    {@code this}
       */
      public BeanCopyUtil<F, T> prop(String field, Function<? super Object, ? super Object> converter) {
        return prop(field, field, converter);
      }
    
      /**
       *          
       *
       * @param field               
       * @return    {@code this}
       */
      public BeanCopyUtil<F, T> prop(String field) {
        return prop(field, field, null);
      }
    
      /**
       *            
       *
       * @param fields                 
       * @return    {@code this}
       */
      public BeanCopyUtil<F, T> props(String... fields) {
        for (String field : fields) {
          prop(field);
        }
        return this;
      }
    
      /**
       *          
       *
       * @return    {@code this}
       */
      public BeanCopyUtil<F, T> exec() {
        new BeanCopyOperator<>(from, to, copyFieldList, config).copy();
        return this;
      }
    
      /**
       *              
       *        {@link #exec()}              
       *
       * @param from    
       * @param <R>      
       * @return      {@link BeanCopyUtil}   
       */
      public <R> BeanCopyUtil<R, T> from(R from) {
        return new BeanCopyUtil<>(from, to);
      }
    
      /**
       *          
       *
       * @return        
       */
      public T get() {
        return to;
      }
    
      /**
       *          
       *
       * @param config       
       * @return    {@code this}
       */
      public BeanCopyUtil<F, T> config(BeanCopyConfig config) {
        this.config = config;
        return this;
      }
    }
    필드 마다 작업 클래스 BeanCopyField 를 정의 하고 필드 마다 작업 저장
    
    /**
     *              
     *
     * @author rxliuli
     */
    public class BeanCopyField {
      private String from;
      private String to;
      private Function<? super Object, ? super Object> converter;
    
      public BeanCopyField() {
      }
    
      public BeanCopyField(String from, String to, Function<? super Object, ? super Object> converter) {
        this.from = from;
        this.to = to;
        this.converter = converter;
      }
    
      public String getFrom() {
        return from;
      }
    
      public BeanCopyField setFrom(String from) {
        this.from = from;
        return this;
      }
    
      public String getTo() {
        return to;
      }
    
      public BeanCopyField setTo(String to) {
        this.to = to;
        return this;
      }
    
      public Function<? super Object, ? super Object> getConverter() {
        return converter;
      }
    
      public BeanCopyField setConverter(Function<? super Object, ? super Object> converter) {
        this.converter = converter;
        return this;
      }
    }
    BeanCopyConfig 를 정의 합 니 다.복사 속성 을 설정 하 는 정책 입 니 다.
    
    /**
     *        
     *
     * @author rxliuli
     */
    public class BeanCopyConfig {
      /**
       *          
       */
      private boolean same = true;
      /**
       *        
       */
      private boolean override = true;
      /**
       *    {@code null}       
       */
      private boolean ignoreNull = true;
      /**
       *         
       */
      private boolean converter = true;
    
      public BeanCopyConfig() {
      }
    
      public BeanCopyConfig(boolean same, boolean override, boolean ignoreNull, boolean converter) {
        this.same = same;
        this.override = override;
        this.ignoreNull = ignoreNull;
        this.converter = converter;
      }
    
      public boolean isSame() {
        return same;
      }
    
      public BeanCopyConfig setSame(boolean same) {
        this.same = same;
        return this;
      }
    
      public boolean isOverride() {
        return override;
      }
    
      public BeanCopyConfig setOverride(boolean override) {
        this.override = override;
        return this;
      }
    
      public boolean isIgnoreNull() {
        return ignoreNull;
      }
    
      public BeanCopyConfig setIgnoreNull(boolean ignoreNull) {
        this.ignoreNull = ignoreNull;
        return this;
      }
    
      public boolean isConverter() {
        return converter;
      }
    
      public BeanCopyConfig setConverter(boolean converter) {
        this.converter = converter;
        return this;
      }
    }
    BeanCopy Operator 를 복사 의 진정한 실현 으로 정의 합 니 다.
    
    /**
     *      copy     
     *
     * @author rxliuli
     */
    public class BeanCopyOperator<F, T> {
      private static final Logger log = LoggerFactory.getLogger(BeanCopyUtil.class);
      private final F from;
      private final T to;
      private final BeanCopyConfig config;
      private List<BeanCopyField> copyFieldList;
    
      public BeanCopyOperator(F from, T to, List<BeanCopyField> copyFieldList, BeanCopyConfig config) {
        this.from = from;
        this.to = to;
        this.copyFieldList = copyFieldList;
        this.config = config;
      }
    
      public void copy() {
        //            
        final Map<String, Reflect> fromFields = Reflect.on(from).fields();
        final Reflect to = Reflect.on(this.to);
        final Map<String, Reflect> toFields = to.fields();
        //                  
        if (config.isSame()) {
          final Map<ListUtil.ListDiffState, List<String>> different = ListUtil.different(new ArrayList<>(fromFields.keySet()), new ArrayList<>(toFields.keySet()));
          copyFieldList = Stream.concat(different.get(ListUtil.ListDiffState.common).stream()
              .map(s -> new BeanCopyField(s, s, null)), copyFieldList.stream())
              .collect(Collectors.toList());
        }
        //            
        copyFieldList.stream()
            //    
            .filter(beanCopyField -> !config.isIgnoreNull() || fromFields.get(beanCopyField.getFrom()).get() != null)
            //    
            .filter(beanCopyField -> config.isOverride() || toFields.get(beanCopyField.getTo()).get() == null)
            //       ,         
            .peek(beanCopyField -> {
              if (beanCopyField.getConverter() == null) {
                beanCopyField.setConverter(Function.identity());
              }
            })
            .forEach(beanCopyField -> {
              final String fromField = beanCopyField.getFrom();
              final F from = fromFields.get(fromField).get();
              final String toField = beanCopyField.getTo();
              try {
                to.set(toField, beanCopyField.getConverter().apply(from));
              } catch (ReflectException e) {
                log.warn("Copy field failed, from {} to {}, exception is {}", fromField, toField, e.getMessage());
              }
            });
      }
    }
    쓰다
    사용 흐름 도

    테스트
    코드 를 다 썼 으 니 테스트 해 봅 시다!
    
    public class BeanCopyUtilTest {
      private final Logger log = LoggerFactory.getLogger(getClass());
      private Student student;
      private Teacher teacher;
    
      @Before
      public void before() {
        student = new Student("  ", 10, " ", 4);
        teacher = new Teacher();
      }
    
      @Test
      public void copy() {
        //     (    BeanUtils.copyProperties)
        BeanCopyUtil.copy(student, teacher).exec();
        log.info("teacher: {}", teacher);
        assertThat(teacher)
            .extracting("age")
            .containsOnlyOnce(student.getAge());
      }
    
      @Test
      public void prop() {
        //       
        BeanCopyUtil.copy(student, teacher)
            .prop("sex", "sex", sex -> Objects.equals(sex, " "))
            .prop("realname", "name")
            .exec();
        assertThat(teacher)
            .extracting("name", "age", "sex")
            .containsOnlyOnce(student.getRealname(), student.getAge(), false);
      }
    
      @Test
      public void prop1() {
        //     
        assertThat(BeanCopyUtil.copy(student, teacher)
            .prop("sex", "sex", sex -> Objects.equals(sex, " "))
            .prop("realname", "name2")
            .exec()
            .get())
            .extracting("age", "sex")
            .containsOnlyOnce(student.getAge(), false);
      }
    
      @Test
      public void from() {
        final Teacher lingMeng = new Teacher()
            .setName("  ")
            .setAge(17);
        //   from     
        assertThat(BeanCopyUtil.copy(student, teacher)
            .prop("sex", "sex", sex -> Objects.equals(sex, " "))
            .prop("realname", "name")
            .exec()
            .from(lingMeng)
            .exec()
            .get())
            .extracting("name", "age", "sex")
            .containsOnlyOnce(lingMeng.getName(), lingMeng.getAge(), false);
      }
    
      @Test
      public void get() {
        //   get     
        assertThat(BeanCopyUtil.copy(student, teacher)
            .prop("sex", "sex", sex -> Objects.equals(sex, " "))
            .prop("realname", "name")
            .exec()
            .get())
            .extracting("name", "age", "sex")
            .containsOnlyOnce(student.getRealname(), student.getAge(), false);
      }
    
      @Test
      public void config() {
        //         
        assertThat(BeanCopyUtil.copy(new Student().setAge(15), new Teacher())
            .config(new BeanCopyConfig().setSame(false))
            .exec()
            .get())
            .extracting("age")
            .containsOnlyNulls();
        //         
        assertThat(BeanCopyUtil.copy(new Student().setAge(15), new Teacher().setAge(10))
            .config(new BeanCopyConfig().setOverride(false))
            .exec()
            .get())
            .extracting("age")
            .containsOnlyOnce(10);
        //            
        assertThat(BeanCopyUtil.copy(new Student(), student)
            .config(new BeanCopyConfig().setIgnoreNull(false))
            .exec()
            .get())
            .extracting("realname", "age", "sex", "grade")
            .containsOnlyNulls();
      }
    
      /**
       *      
       */
      private static class Student {
        /**
         *   
         */
        private String realname;
        /**
         *   
         */
        private Integer age;
        /**
         *   , / 
         */
        private String sex;
        /**
         *   ,1 - 6
         */
        private Integer grade;
    
        public Student() {
        }
    
        public Student(String realname, Integer age, String sex, Integer grade) {
          this.realname = realname;
          this.age = age;
          this.sex = sex;
          this.grade = grade;
        }
    
        public String getRealname() {
    
          return realname;
        }
    
        public Student setRealname(String realname) {
          this.realname = realname;
          return this;
        }
    
        public Integer getAge() {
          return age;
        }
    
        public Student setAge(Integer age) {
          this.age = age;
          return this;
        }
    
        public String getSex() {
          return sex;
        }
    
        public Student setSex(String sex) {
          this.sex = sex;
          return this;
        }
    
        public Integer getGrade() {
          return grade;
        }
    
        public Student setGrade(Integer grade) {
          this.grade = grade;
          return this;
        }
    
        @Override
        public String toString() {
          return ToStringBuilder.reflectionToString(this);
        }
      }
    
      /**
       *      
       */
      private static class Teacher {
        /**
         *   
         */
        private String name;
        /**
         *   
         */
        private Integer age;
        /**
         *   ,true  ,false  
         */
        private Boolean sex;
        /**
         *   
         */
        private String post;
    
        public Teacher() {
        }
    
        public Teacher(String name, Integer age, Boolean sex, String post) {
          this.name = name;
          this.age = age;
          this.sex = sex;
          this.post = post;
        }
    
        public String getName() {
          return name;
        }
    
        public Teacher setName(String name) {
          this.name = name;
          return this;
        }
    
        public Integer getAge() {
          return age;
        }
    
        public Teacher setAge(Integer age) {
          this.age = age;
          return this;
        }
    
        public Boolean getSex() {
          return sex;
        }
    
        public Teacher setSex(Boolean sex) {
          this.sex = sex;
          return this;
        }
    
        public String getPost() {
          return post;
        }
    
        public Teacher setPost(String post) {
          this.post = post;
          return this;
        }
    
        @Override
        public String toString() {
          return ToStringBuilder.reflectionToString(this);
        }
      }
    }
    만약 무슨 의외 의 일이 발생 하지 않 았 다 면,모든 것 이 정상적으로 운행 할 수 있 었 을 것 이다!
    자,그럼 자바 에서 우아 한 복사 대상 속성 은 여기까지 입 니 다.
    이상 은 자바 가 어떻게 우아 하 게 대상 속성 을 복사 하 는 지 에 대한 상세 한 내용 입 니 다.자바 복사 대상 속성 에 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!

    좋은 웹페이지 즐겨찾기