자바 어떻게 우아 한 복사 대상 속성
자바 프로젝트 에 서 는 대상 간 에 속성 을 복사 해 야 하 는 문제 가 자주 발생 합 니 다.그러나 Getter/Stter 방법 을 직접 사용 하 는 것 외 에 우리 에 게 또 다른 방법 이 있 습 니까?
물론 있 습 니 다.예 를 들 어 Apache Common Lang 3 의 BeanUtils 가 있 지만 BeanUtils 는 우리 세대 의 요 구 를 완전히 만족 시 키 지 못 하기 때문에 우 리 는 스스로 하 나 를 봉 하여 참고 할 수 있 도록 공유 합 니 다.
간단 하고 사용 하기 쉬 운 API
이루어지다
주:반사 부분 의존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);
}
}
}
만약 무슨 의외 의 일이 발생 하지 않 았 다 면,모든 것 이 정상적으로 운행 할 수 있 었 을 것 이다!자,그럼 자바 에서 우아 한 복사 대상 속성 은 여기까지 입 니 다.
이상 은 자바 가 어떻게 우아 하 게 대상 속성 을 복사 하 는 지 에 대한 상세 한 내용 입 니 다.자바 복사 대상 속성 에 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.