rainsister
  • 신비한 비 Blog
  • Design Pattern
    • Adapter Pattern
    • Proxy Pattern
    • Mediator Pattern
    • Visitor Pattern
    • State Pattern
    • Memento Pattern
    • Factory Pattern
    • Template Pattern
    • Strategy Pattern
    • Bridge Pattern
  • springboot 2.x
    • 메시징 - 큐
      • RabbitMQ 기본설정
      • RabbitMQ 지연 큐(delayed queue)
    • Log
      • 기본 Log 설정 및 Logback 설정
      • Log4j2 사용해보기
      • tinylog 모듈 사용해보기
    • 기타 Database 에 대한 기본가이드
      • PostgreSQL
      • InfluxDB
      • MongoDB
    • 수행속도 UP! 각종 cache 어노테이션 사용법
      • Redis를 사용하여 Cache 관리하기
      • EhCache 사용해보기
      • Thread내캐쉬사용 및 Cache 어노테이션 사용법
    • Database Connection
      • Springboot 2.5 이후 data init script 초기화에 대한 변경
      • JTA 로 JPA 다중 DataSource 트랜잭션 처리 하기
      • Flyway 로 DataBase 형상 관리해보자
      • 트랜잭션 기초읽기
      • MyBatis 의 다중 DataSource
      • Spring Data JPA 다중 DataSource
      • JdbcTemplate 다중 DataSource
      • XML 로 Mybatis 설정하기
      • Mybatis 로 Mysql 연결하기
      • ORM(Spring data jpa)
      • Druid datasource 연결
      • Hikari 설정
      • JdbcTemplate 로 db 접근
    • rest api
      • XML에 대한 요청 및 응답 처리
      • SpringFox 3 및 Swagger 설정
      • 프로젝트 구동 시 RequestMappingHandler 로그 설정
      • Swagger 의 api들을 분류하는 법
      • 간단한 Restful API 만들고 테스트 코드 작성
      • Swagger2 구성하여 API 문서 자동화하기
      • JSR-303 그리고 validation
    • 설정
      • 시작
      • 멀티환경구성에 대한 새로운 방법
      • 멀티환경구성에 대한 새로운 include
      • 프로젝트 설정파일
      • 민감한 정보에 대한 암호화
  • java버전별차이
    • JAVA18
    • JAVA9
    • JAVA10
    • JAVA11
    • JAVA14
    • JAVA15
    • JAVA17
    • JAVA16
  • spring노하우
    • BeanUtils 권장하지 않는이유
    • 개인정보 암호화
    • Springboot 3가지 CROS 설정
    • Springboot 내장된 유용한 Utils
    • Spring Security WebSecurityConfigurerAdapter 가 deprecated 된 이슈해결하기
    • 아직도 HttpUtil ? SpringBoot 3.0의 HTTP Client Util 을 사용해보라
    • JDBC 소스를 뽀개기
    • spring-boot-configuration-processor 는 뭐하는놈임?
    • Apache BeanUtils vs Spring BeanUtils 성능비교
  • Effetive Java 3th
    • Finalizer & Cleaner
Powered by GitBook
On this page
  • 들어가면서...
  • 예시코드
  • mapstruct 는 ?
  1. spring노하우

BeanUtils 권장하지 않는이유

Previousspring노하우Next개인정보 암호화

Last updated 8 months ago

들어가면서...

지난번에 올렸던 비교 문장을 일단 읽어보았을것이다. 그 문장에 이어 오늘은 BeanUtils 에 대해서 다시 언급해볼까 한다.

예전에 도org.apache.commons.beanutils.BeanUtils 을 일반적으로 많이 사용해왔다.

하지만 Spring내의 BeanUtils 이 성능이 좋다고 들었다. 이 클래스의 공식적 영어설명은 아래와 같았다.

Static convenience methods for JavaBeans: for instantiating beans, checking bean property types, copying bean properties, etc.

Mainly for use within the framework, but to some degree also useful for application classes.

Spring BeanUtils 의 문제를 파헤쳐보자

예시코드

import lombok.Data;
import java.util.List;

@Data
public class A {
    private String name;
    private List<Integer> ids;
}
@Data
public class B {
    private String name;
    private List<String> ids;
}
import org.springframework.beans.BeanUtils;

import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        BeanUtils.copyProperties(first, second);
        for (String each : second.getIds()) {// 에러발생
            System.out.println(each);
        }
    }
}

Run time 시 exception 이 발생하는걸 알수 있다.

디버깅 찍고 확인 해보면 아래와 같이 property copy 후에도 B 클래스의 second 객체내의 ids 는 여전희 Integer 형이다.

문자열로 캐스팅 하는 과정업이 print 하면 바로 예외가 발생한다.

그리고 CGlib 사용시 Convert를 지정하지 않으면 마찬가지로 동일한 문제가 발생한다.

import org.easymock.cglib.beans.BeanCopier;
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        final BeanCopier beanCopier = BeanCopier.create(A.class, B.class, false);
        beanCopier.copy(first,second,null);

        for (String each : second.getIds()) {// 에러 발생
            System.out.println(each);
        }
    }
}

항상 run time 시에 발생하는 에러라 코딩시에는 아무런 문제 없다.

mapstruct 는 ?

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface Converter {
    Converter INSTANCE = Mappers.getMapper(Converter.class);

    B aToB(A car);
}
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = Converter.INSTANCE.aToB(first);
        for (String each : second.getIds()) {// 정상
            System.out.println(each);
        }
    }
}

A 클래스의 List<Integer> 를 B클래스의 List<String> 으로 전환성공!

아래는 컴파일은 성공한 Converter 클래스를 보자

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
@Component
public class ConverterImpl implements Converter {

    @Override
    public B aToB(A car) {
        if ( car == null ) {
            return null;
        }

        B b = new B();

        b.setName( car.getName() );
        b.setIds( integerListToStringList( car.getIds() ) );

        return b;
    }

    protected List<String> integerListToStringList(List<Integer> list) {
        if ( list == null ) {
            return null;
        }

        List<String> list1 = new ArrayList<String>( list.size() );
        for ( Integer integer : list ) {
            list1.add( String.valueOf( integer ) );
        }

        return list1;
    }
}

자동으로 전화해주어서 , 개발자 입장에서 인지하지 못하고 있었음!

만약 A 클래스에 String number 속성 추가 , B 클래스에 Long number 속성 추가후 mapstruect 가 number 를 Integer 외 데이터 타입으로 세팅하려고 시도하면 .NumberFormatException 예외가 발생할것 이다.

 @Override
    public B aToB(A car) {
        if ( car == null ) {
            return null;
        }

        B b = new B();

        b.setName( car.getName() );
        if ( car.getNumber() != null ) { // 문제발생지점
            b.setNumber( Long.parseLong( car.getNumber() ) );
        }
        b.setIds( integerListToStringList( car.getIds() ) );

        return b;
    }

왜냐면 cglib 사용시 number 속성을 매핑하지 않을것이고 B 클래스의 number 가 null 이된다.

수동으로 convert 정의시 ( 예를 들어 IDEA 플러그인 generateO2O) 를 사용한다면.

public final class A2BConverter {

    public static B from(A first) {
        B b = new B();
        b.setName(first.getName());
        b.setIds(first.getIds());
        return b;
    }
}

코딩시 바로 에러에 대하여 감지한다.

결론!

Java의 제너릭은 사실 컴파일 기간 검사이기 때문에 컴파일 후 제러릭은 지워지기 때문에 rum time 시 List<Integer>와 List<String>은 모두 List 타입이므로 정상적으로 값을 할당할 수 있다.이로 인해 많은 속성 매핑 도구를 사용할 때 컴파일 시 오류는 코딩과정에서 쉽게 찾을수 없다.

mapstruct는 주석 프로세서를 사용자 정의하여 컴파일 단계에서 양쪽을 매핑하는 범유형 유형을 읽고 매핑할 수 있습니다.그런데 이런 매핑도 위헙하다고 하는 이유는 때로는 우리가 부주의 등으로 유형을 잘못 정의해서 자동으로 변환을 도와줘서 많은 문제를 가져왔었기때문이다.

다양한 속성 매핑 도구의 성능을 간단히 비교한 결과이니 참고용으로 만사용해보라 .결국은 노가다 getter,setter 가 짱이다.

끝!

apche BeanUtils vs Spring BeanUtils
시간 단위 ms