TIL - 0622

토비의 스프링으로 스프링 들여다보기

AOP 핵심

  • 핵심 기능은 아니지만 필요한 부가기능이면서, 산발적으로 퍼져있는 것을 따로 모은 뒤 API를 통해서 핵심 기능을 가진 타겟 객체에 부가 기능을 적용시키는 것
    • 부가기능의 모듈화

ProxyFactoryBean 사용해서 타겟의 메소드에 어드바이스 적용하기

  • Advise 인스턴스를 타겟마다 만들지않고 재사용할 수 있게 됨 : DI(타겟을 Advise에서 없애버리고 하나의 판을 만들고 advise와 target을 DI 받도록 해서 적용)
  • 문제점 : 프록시를 적용할 타겟마다 ProxyFactoryBean을 사용해서 프록시 객체를 만들고 있음 - 이 자체도 낭비다(중복된 코드)
@Test
public void proxyFactoryBeanTest() {
    ProxyFactoryBean factory = new ProxyFactoryBean();
    factory.setTarget(); // 타겟마다 advisor를 적용해주는 것 자체가 싫다는 것
    
    NameMatcherMethodPointcut pointcut = new NameMatchMethodPointcut();
    pointcut.setMappedName("sayH*");
    factory.addAdvisor(new DefaultPointcutAdvisor(pointcut, new UpperCaseAdvice());
}

ProxyFactoryBean 생성 코드 중복을 막기위해 BeanPostProcessor를 사용한 자동 프록시 생성

  • 빈 후처리기 : 생성된 빈을 찾아와서 후처리를 할 수 있음(커스터마이징) - 필요한 정보를 주입시켜준다던지(우선 빈을 컨테이너에서 찾아와야함)
    • 스프링 컨테이너는 확장 포인트를 제공하는데 그중 하나가 빈 후처리기를 만들어서 제공 : 객체지향을 모든 곳에
  • BeanPostProcessor는 인터페이스로 이를 구현하는 클래스를 만들어야하고, 구현 클래스를 빈으로 만들어야함
    • 스프링에서 제공하는 자동 프록시 생성기(후처리기 구현체) : DefaultAdvisorAutoProxyCreator
    • 포인트컷 : 포인트컷은 어떤 메소드가 어드바이스(순수 부가기능)가 적용되어야할지 알 수 있지만, 인터페이스를 들여다보면 어떤 클래스가 프록시(타겟 객체에 부가기능이 적용되어 생성된 객체) 적용될지 확인하는 메소드도 있음
public interface PointCut {
    ClassFilter getClassFilter(); // 프록시 적용 클래스인지 확인(기존에는 모든 클래스)
    MethodMatcher getMehodMatcher(); // 어드바이스를 적용할 메소드인지
}
  • 적용될 대상(포인트컷)에 대해 정의하고, advise를 합쳐 Advisor(해당 advise는 어떤 포인트컷을 가지는지에 대해서 하나로 묶어버리는 객체)로 만들어서 후처리기를 통해 적용될 대상에만 후처리를 해줘서 자동으로 프록시 객체가 만들어지도록 하는 시나리오?
    • 타겟 설정과 같은 코드를 아예 없애버리고 이미 초기화된 빈 중에 적용될 아이를 찾는거지 - 후처리기를 통해서
    • 포인트컷과 어드바이스를 합쳐 어드바이저(하나로 묶음) 만듬 - 후처리기가 등록되어있으면 스프링빈이 생성될 때마다 후처리기로 생성된 빈을 보냄 - 어드바이저 내 포인트컷으로 빈이 적용 대상인지 확인 - 맞으면 프록시 생성(어드바이저 add) - 생성된 인스턴스를 스프링 컨테이너에 줌(바꿔치기) : 해당 작업을 통해 위의 ProxyFactoryBean을 통해 생성된 인스턴스를 빈으로 등록하지않아도 됨
/* 빈 생성 후 등록 */
@Configuration
public class AutoProxyConfig {

    @Bean(name ="transactionAdvisor")
    public Advisor createTransactionAdvisor() {
        return new DefaultPointcutAdvisor(createNameMatchClassMethodPointcut(), new TransactionAdvice());
    }

    private NameMatchClassMethodPointcut createNameMatchClassMethodPointcut() {
        NameMatchClassMethodPointcut pointcut = new NameMatchClassMethodPointcut();
        pointcut.setMappedClassName("*ServiceImpl");
        pointcut.setMappedName("upgrade*");
        return pointcut;
    }

    @Bean(name = "autoProxyCreator")
    public DefaultAdvisorAutoProxyCreator createAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }
}

/* 포인트컷 커스텀 - 클래스 / 메소드 이름 필터링 */
public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut {

    public void setMappedClassName(String mappedClassName) {
        this.setClassFilter(new SimpleClassFilter(mappedClassName));
    }

    static class SimpleClassFilter implements ClassFilter {

        private String mappedName;

        private SimpleClassFilter(String mappedName) {
            this.mappedName = mappedName;
        }

        @Override
        public boolean matches(Class<?> clazz) {
            return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName());
        }
    }
}
  • DefaultAdvisorProxyCreator : 빈 컨테이너의 확장을 위한 빈 후처리기 생성 후 빈으로 등록, 등록해두면 스프링에서 빈 생성 후 후처리기를 사용
  • DefaultPointcutAdvisor : 포인트컷(필터 - 클래스명, 메소드명)과 어드바이스(순수 부가기능)을 하나로 관리하는 객체(해당 어드바이스는 포인트컷을 가짐을 명확하게 하기위해)
  • 빈 후처리기와 Advisor를 빈으로 등록해두면 생성되는 스프링빈마다 후처리기로 보내고, 후처리기가 Advisor에 따라 부가기능을 적용시킬 것인지 말지를 따진 후 부가기능을 적용시킨 객체는 이전에 생성된 스프링빈과 바꿔치기해서 컨테이너에 등록함(부가기능이 적용된 객체)

빈 후처리기와 어드바이저를 적용해봤더니

  • CGLib 사용한 결과로 나옴
  • JDK 프록시 라이브러리 사용과 CGLib 사용 차이
    • 현재까지는 CGLib은 바이트 코드를 조작하는 것이고, JDK 프록시는 런타임에 API를 사용해서 프록시 객체를 만드는 것
    • 더 알아보기! 왜 스프링부트 2.x부터는 CGLib을 사용하게 된건지
  • 이너클래스 공부하기 : static inner class와 inner class 차이까지

java-ims

  • String -> LocalDateTime 컨버터 만들고 스프링빈으로 설정해두고 사용하기
    • 어떻게 컨버터를 적용하는지 공부하기 : String을 받았을 때 LocalDateTime으로 변경하는 컨버터는 만들고 addConverter 해놓았는데 이게 빈인지…. 그래서 꺼내와야하는지 아니면 사용방법이 따로 있는지 체크할 것
  • 일반적인 역할 분리를 위해서 팩토리 메소드나 스태틱 메소드를 사용하면 되지만, 하나의 인터페이스(call 메소드)에 다양한 방식을 사용하기위해 DI를 씀
    • 방금 든 생각인데 스태틱 메소드에 전략을 전달해서 다양한 전략을 구사하게하는건?