Java, Spring

[스프링 AOP] 팩토리 빈, 프록시 팩토리 빈, 포인트컷

집사킴 2025. 5. 26. 10:30
728x90

팩토리 빈 (Factory Bean)

  • Spring에서 사용되는 특수한 빈으로, 일반적으로 객체를 생성하는 로직을 포함하고 있는 빈
  • Proxy.newProxyInstance() 메소드를 통해서만 생성이 가능한 다이내믹 프록시 오브젝트는 일반적인 방법으로는 스프링 빈으로 등록할 수 없으나, 팩토리 빈을 사용하면 빈 등록 가능.

 

장점

  • 프록시 팩토리 빈을 이용하면 프록시 기법을 빠르고 효과적으로 적용 가능
  • 코드 한 줄 만들지 않고 기존 코드에 부가적인 기능 추가 가능
  • 다이내믹 프록시 + 팩토리 빈 조합 사용 시 번거로운 다이내믹 프록시 생성 코드 제거 가능

단점

  • 프록시로 타깃에 부가기능 제공하는 것은 메소드 단위로 일어나는 일. 한 번에 여러 개 클래스에 공통 부가기능 추가는 불가능
  • 하나의 타깃에 여러 부가기능을 적용하고자 할 때도 프록시 팩토리 빈 설정 xml 코드가 계속 늘어남
  • TransactionHandler 오브젝트가 프록시 팩토리 빈 개수만큼 생성됨

 

다이내믹 프록시와 팩토리빈을 이용한 트랜잭션 부가기능 처리

import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.Proxy;

public class TransactionalUserServiceFactoryBean implements FactoryBean<UserService> {

    private UserService userService;

    public TransactionalUserServiceFactoryBean(UserService userService) {
        this.userService = userService;
    }

    @Override
    public UserService getObject() throws Exception {
        // 다이내믹 프록시를 사용해 UserService의 트랜잭션을 처리
        return (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(),
                new Class[] { UserService.class },
                (proxy, method, args) -> {
                    // 트랜잭션 시작
                    System.out.println("트랜잭션 시작");
                    try {
                        Object result = method.invoke(userService, args);
                        // 트랜잭션 커밋
                        System.out.println("트랜잭션 커밋");
                        return result;
                    } catch (Exception e) {
                        // 트랜잭션 롤백
                        System.out.println("트랜잭션 롤백");
                        throw e;
                    }
                }
        );
    }

    @Override
    public Class<?> getObjectType() {
        return UserService.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

// -----
@Configuration
public class AppConfig {
    @Bean
    public FactoryBean<UserService> transactionalUserService() {
        return new TransactionalUserServiceFactoryBean(new UserServiceImpl());
    }
}

 

 


FactoryBean은 복잡한 객체 생성 과정을 캡슐화할 수 있어 유용하지만, 빈 이름 혼동(& 접두어), 초기화 타이밍, 테스트/프록시 적용의 어려움 등으로 인해 실무에선 신중하게 사용해야 한다. 이러한 한계는 프록시 생성과 같은 상황에서 더욱 두드러지며, 이를 보완하기 위해 Spring은 ProxyFactoryBean과 같은 프록시 전용 추상화도 함께 제공한다.

 

ProxyFactoryBean

Spring이 프록시 객체를 만들 때 사용하는 클래스 기반 팩토리

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="realService" /> # 실제 서비스를 참조
    <property name="interceptorNames"> # 적용할 어드바이스들 (트랜잭션, 로깅 등)
        <list>
            <value>txAdvice</value>
            <value>logAdvice</value>
        </list>
    </property>
</bean>

 

 

요즘(Spring Boot 3 기준) ProxyFactoryBean을 직접 안 쓰는 이유

  • @Transactional, @Async, @Cacheable처럼 Annotation 기반 AOP가 대세
  • 스프링 부트 자동설정 (@EnableTransactionManagement 등)으로 ProxyFactoryBean 이 자동으로 등록됨
  • 직접 ProxyFactoryBean을 설정하는 건 프레임워크 레벨 개발자가 아니면 필요 없음

구성 요소

ProxyFactoryBean이 만들어낸 프록시 객체가 어떤 행동을 해야하는지를 결정하는 데에 필요한 요소들.

  • 어드바이스(Advice): 부가기능 그 자체. 프록시 객체가 해야 할 행동.
  • 포인트컷(PointCut): 부가기능을 적용할 대상. 프록시가 대체할 범위. 메소드 선정 알고리즘
  • 어드바이저(Advisor): 포인트컷 + 어드바이스

문제

부가기능의 적용이 필요한 타깃 오브젝트마다 거의 비슷한 내용의 ProxyFactoryBean을 추가해줘야 한다.

→ 빈 후처리기를 이용한 자동프록시로 해결

  • 스프링은 자동 프록시 생성기를 통해 AOP를 적용합니다.
  • 빈 후처리기를 이용하여 특정 조건을 만족하는 빈에 대해 프록시를 자동으로 생성합니다.
  • 중복 문제 해결을 위해 프록시 생성을 자동화하고 관리합니다.
  • 포인트컷 기능을 확장하여 세밀한 AOP 적용이 가능해졌습니다.

 

포인트컷 표현식

  • 포인트컷(Pointcut)은 어드바이스를 적용할 메서드를 지정하는 역할을 합니다.
  • 스프링 AOP는 AspectJ 스타일의 포인트컷 표현식을 지원합니다.
  • 주요 예시:
    • execution(* com.example.service.*.*(..))
    • → com.example.service 패키지의 모든 클래스의 모든 메서드에 적용
    • within(com.example.service..*)
    • → com.example.service 패키지 및 하위 패키지 전체
728x90