프록시팩토리의 도입으로 실제객체의 인터페이스의 유무에 무관하게 프록시객체를 생성할 수 있게 되고, 어드바이저의 도입으로 포인트컷을 통해 적용할지 여부를 결정하고, 어드바이스로 어떤 로직인지만 정하면 된다. 하지만 프록시객체를 빈으로 등록해야 하므로 수동 빈 등록만 가능하고 Config 에서 모든 빈마다 프록시객체를 생성해줘야하는 번거로움이 있다.
객체를 빈으로 등록하기 직전에 프록시객체로 바꿔줄 수 있는 빈 후처리기에 대해 알아보자.
스프링은 빈으로 등록된 객체를 생성하고 컨테이너에 등록한다. 빈 후처리기는 생성된 객체를 빈으로 등록하기 전에 조작하는 객체이다. BeanPostProcessor 인터페이스를 구현한 뒤 스프링빈으로 추가하면 된다. 빈 후처리기는 다른 빈보다 먼저 컨테이너에 등록되며, 다른 빈들이 등록될 때 후처리를 수행한다.
빈 후처리기의 기능은 막강해서, 빈 객체를 다른 객체로 바꿔치기해서 등록할 수도 있다. 그림을보면 스프링빈으로 추가된 것은 A 객체이지만, 빈 후처리기가 빈 등록 직전에 바꿔치기해서 B객체를 등록해버렸다. 즉 실제객체를 프록시객체로 바꿔치기해서 등록할 수도 있다는 말이다.
A 객체와 B 객체가 있다. 그중에 A 객체만 beanA 라는 이름으로 스프링빈으로 등록하였다.
빈 후처리기는 BeanPostProcessor 를 구현하여 생성한다. postProcessBeforeInitialization 과 postProcessAfterInitialization 두개의 메서드를 오버라이딩할 수 있는데, 빈 초기화 후 후처리기를 적용할지 초기화 전에 적용할지 여부이다. 여기서는 후자를 오버라이딩하였다. 해당 빈 후처리기는 A 타입의 객체를 확인하면 B 객체를 생성하여 반환하도록 하였다.
빈 후처리기를 적용하려면 빈 후처리기도 스프링빈으로 등록해야한다. Config 클래스에 빈 후처리기를 수동빈등록 해주었다.
분명 수동빈 등록에서 A 객체를 빈으로 등록했는데 빈을 조회해보면 B 객체가 조회된다. 이는 생성한 A 객체가 컨테이너에 등록되기 전에, 빈 후처리기가 이를 B 객체로 바꿔치기해서 등록했기 때문이다.
빈 후처리기는 개발자가 등록한 모든 빈을 중간에 조작할 수 있다. 이 기능은 막강하여 개발자가 등록한 것과 전혀 다른 객체를 빈으로 등록할 수도 있다. 이 말은 등록한 빈 객체를 프록시로 교체하여 컨테이너에 등록하는 것도 가능하다는 뜻이다.
이제 Controller, Service, Repository 를 프록시객체로 바꿔치기 해주는 빈 후처리기를 등록해보자. 그러면 자동빈 등록도 프록시가 적용 가능하며, 일일이 프록시객체를 생성할 필요도 없을 것이다.
BeanPostProcessor 를 구현하여 빈 후처리기를 생성한다. 필드로 basePackage 와 advisor 를 주입받아 사용한다.
postPrcoessAfterInitialization 은 파라미터로 빈객체와 빈이름을 갖는데, 이를 통해 패키지이름을 확인하고 basePackage 에 해당하면 프록시객체로 바꿔치기해서 등록하는 방식이다. 프록시팩토리에 의해 Advisor 만 있으면 프록시객체를 생성할 수 있다.
이제 빈 후처리기를 빈으로 등록하면 동작한다. basePackage 에는 프록시객체로 변환할 패키지명을. Advisor 에는 메서드명 매핑 포인트컷과 로그추적기를 추가하는 어드바이스를 담아주었다.
이제 모든 빈마다 프록시객체로 바꿔줄 필요가 없이 프록시객체로 바꿔주는 빈후처리기만 빈으로 등록하면 된다. 또한 컴포넌트스캔 방식에서도 위와같이하면 프록시객체로 변환하여 빈등록이 가능하다.
그런데 basePackage 변수로 적용 여부를 정하는 것은 번거롭고 유연성이 떨어진다. 포인트컷은 어드바이스를 적용시킬지 결정하는 필터라고 하였다.
빈 후처리기가 빈의 클래스, 메서드 정보를 확인하고 포인트컷과 매치가 될때만 프록시 객체를 생성하도록 할 수는 없을까? 스프링은 이를 기본으로 지원한다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
build.gradle 에 의존관계를 추가한다. 포인트컷 작성시 사용하는 aspectJ 관련 라이브러리와 AOP 관련 클래스들을 자동으로 스프링빈으로 등록한다.
위의 설정을 추가하면 AnnotationAwareAspectJAutoProxyCreator 라는 빈 후처리기를 자동으로 스프링 빈으로 등록한다. 해당 빈 후처리기는 빈으로 등록된 Advisor 들을 확인하고, 다른 빈들을 프록시객체로 변경해주는 역할을 수행한다.
즉 개발자는 Advisor 만 스프링빈으로 등록하면 된다.
Advisor 들의 포인트컷은 두번 사용된다.
즉 프록시 자동 생성기는 꼭 필요한 객체만 프록시객체로 만들기 위해, 포인트컷으로 한번 필터링해서 어드바이스가 사용될 가능성이 있는 객체만 프록시객체로 생성한다.
포인트컷을 통해 해당 빈을 프록시객체로 등록할지 확인한다고 하였다.
그런데 지금 포인트컷을 보면 request, order, save 로 시작하는 메서드가 들어있는 모든 객체는 프록시객체로 변화될 것이다. 그 과정에서 스프링 내부에서 사용하는 빈도 프록시객체로 변경된다. 패키지 경로를 지정할 수는 없을까?
이때 AspectJ 라는 포인트컷 표현식을 통해 필터를 설정할 수 있다. 패키지명, 메서드명 등 다양한 것을 정할 수 있는데 추후 포스팅에서 설명하겠다.
하나의 객체가 여러 Advisor 에 포인트컷에 충족되면, 자동 프록시 생성기는 두 Advisor 를 주입한 프록시객체를 하나만 생성한다. 여러 부가기능을 수행한다고 여러개의 프록시객체가 생성되는게 아님에 유의하자.
빈 후처리기의 도입으로 스프링 빈을 컨테이너에 등록하기 전에 처리를 할 수 있게 되었다.
스프링은 AutoProxyCreator 를 빈 후처리기로 자동 등록한다. 자동 프록시 생성기는 빈으로 등록된 Advisor 들을 확인해서 Pointcut 에 해당하는 메서드가 있는 빈을 프록시객체로 변경하여 등록한다.
즉 Pointcut 은 프록시객체를 생성할때와 프록시객체 내부에서 Advice를 적용할 때 두번 사용된다.
자동 프록시 생성기의 존재로 개발자는 Advisor 만 스프링 빈으로 등록하면 된다.