Spring AOP自動プロキシ生成の仕組みと実装詳細

はじめに

これまでの解説で、Spring AOPの三本柱であるアドバイス、アスペクト、プロキシについて理解を深めてきた。これでAOP機能そのものは実現可能だが、各オブジェクトに対して手動でプロキシを作成する必要があり、実用的ではない。そこで登場するのが、プロキシ生成の自動化メカニズムである。この仕組みはSpringコンテナの管理機能を活用し、Bean生成プロセス内でプロキシが必要かどうかを判定、自動的にプロキシを生成する。この自動プロキシ生成コンポーネントによって、AOP機能とSpringコンテナが統合され、フレームワークの基盤が確立される。

自動プロキシ生成のコンポーネント構造

ProxyConfigクラスから二つの系統が派生している。AdvisedSupport系はプロキシ生成機能を提供し、ProxyProcessorSupport系はその機能をBeanFactoryのエコシステムに統合する。特に注目すべきは、AbstractAutoProxyCreatorが内部的にProxyFactoryを使用している点で、手動プロキシ生成が自動化の基盤となっている。

  • AopInfrastructureBean: マーカーインターフェース。AOPインフラストラクチャコンポーネントを示し、自身はプロキシ対象外となる。
  • ProxyProcessorSupport: サブクラス向けのユーティリティ機能を提供。
  • AbstractAutoProxyCreator: 自動プロキシ生成の中核クラス。ProxyFactoryを通じてプロキシを生成する。
  • AbstractAdvisorAutoProxyCreator: Springコンテナ内のAdvisorインスタンスからインターセプタチェーンを構築。
  • InfrastructureAdvisorAutoProxyCreator: カスタムAdvisorを除外し、Spring組み込みコンポーネントのみを使用。

AbstractAutoProxyCreatorの実装詳細

AbstractAutoProxyCreatorInstantiationAwareBeanPostProcessorインターフェースを実装し、Bean処理能力を獲得すると同時に、AOPとIOCコンテナを連携させる。

  • processedBeans: 処理済みBeanをキャッシュし、重複処理を防止
  • earlyProxyCache: 循環依存解決用の早期プロキシ参照をキャッシュ
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport 
        implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
    
    private BeanFactory beanFactory;
    private AdvisorAdapterRegistry adapterRegistry = new DefaultAdvisorAdapterRegistry();
    
    // 処理済みBeanのキャッシュ
    private final Map<Object, Boolean> processedBeans = new ConcurrentHashMap<>(256);
    // 循環依存解決用キャッシュ
    private final Map<Object, Object> earlyProxyCache = new ConcurrentHashMap<>(16);
}

このクラスは3つのコールバックメソッドを実装し、それぞれ異なるタイミングでプロキシ生成を行う。

  • postProcessAfterInitialization: 初期化後に実行。プロキシ生成のメインルート。
  • getEarlyBeanReference: プロパティ設定前に実行。プロキシの循環依存を解決。
  • postProcessBeforeInstantiation: インスタンス化前に実行。特殊なプロキシ生成方式。

初期化後のプロキシ生成フロー

postProcessAfterInitializationはBeanの初期化完了後に呼び出される。この時点でインスタンスは生成され、依存関係も解決済みであり、プロキシ生成に最適なタイミングである。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean == null) {
        return null;
    }
    
    Object key = StringUtils.hasText(beanName) ? beanName : bean.getClass();
    
    // 早期プロキシ参照の確認(循環依存対策)
    if (this.earlyProxyCache.remove(key) != bean) {
        return createProxyIfRequired(bean, beanName, key);
    }
    return null;
}

createProxyIfRequiredメソッドがプロキシ生成の核心となる。このメソッドは3つのステップで構成される。

private Object createProxyIfRequired(Object bean, String beanName, Object key) {
    // ステップ1: 前提条件チェック
    if (Boolean.FALSE.equals(this.processedBeans.get(key))) {
        return bean;
    }
    
    Class<?> beanType = bean.getClass();
    if (isAopInfrastructureClass(beanType)) {
        this.processedBeans.put(key, Boolean.FALSE);
        return bean;
    }
    
    // ステップ2: 適用可能なインターセプタを取得
    Object[] interceptors = resolveInterceptorsForBean(bean.getClass(), beanName, null);
    
    if (interceptors != null) {
        this.processedBeans.put(key, Boolean.TRUE);
        
        // ステップ3: プロキシ生成
        Object proxyInstance = buildProxy(bean.getClass(), beanName, 
                                          interceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(key, proxyInstance.getClass());
        return proxyInstance;
    }
    
    this.processedBeans.put(key, Boolean.FALSE);
    return bean;
}

インターセプタ(Advisor)の解決

resolveInterceptorsForBeanAbstractAdvisorAutoProxyCreatorで実装されるテンプレートメソッドである。

@Override
protected Object[] resolveInterceptorsForBean(Class<?> beanType, String beanName, 
                                               TargetSource targetSource) {
    List<Advisor> matchedAdvisors = findMatchingAdvisors(beanType);
    
    if (matchedAdvisors.isEmpty()) {
        return null;
    }
    return matchedAdvisors.toArray();
}

protected List<Advisor> findMatchingAdvisors(Class<?> beanType) {
    // 1. 候補Advisorの検索
    List<Advisor> candidates = discoverCandidateAdvisors();
    
    // 2. 適用可能なAdvisorのフィルタリング
    List<Advisor> matched = AopUtils.filterApplicableAdvisors(candidates, beanType);
    
    // 3. ソート
    if (!matched.isEmpty()) {
        AnnotationAwareOrderComparator.sort(matched);
    }
    return matched;
}

候補Advisorの検索では、Springコンテナ内の全Advisor型Beanを取得し、フィルタリングを行う。

protected List<Advisor> discoverCandidateAdvisors() {
    List<String> advisorNames = getBeanFactory().getBeanNamesForType(Advisor.class);
    List<Advisor> advisors = new LinkedList<>();
    
    for (String name : advisorNames) {
        if (isEligibleAdvisor(name)) {
            advisors.add(getBeanFactory().getBean(name, Advisor.class));
        }
    }
    return advisors;
}

InfrastructureAdvisorAutoProxyCreatorでは、組み込みコンポーネントのみを対象とするようフィルタリング条件をオーバーライドする。

@Override
protected boolean isEligibleAdvisor(String beanName) {
    ConfigurableBeanFactory factory = (ConfigurableBeanFactory) getBeanFactory();
    return factory.containsBeanDefinition(beanName) 
        && factory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE;
}

適用判定はAopUtilsで行われる。PointcutAdvisorの場合、クラスフィルタとメソッドマッチャーの両方で判定を行う。

public static List<Advisor> filterApplicableAdvisors(List<Advisor> candidates, Class<?> targetClass) {
    List<Advisor> applicable = new LinkedList<>();
    
    for (Advisor advisor : candidates) {
        if (isApplicable(advisor, targetClass)) {
            applicable.add(advisor);
        }
    }
    return applicable;
}

private static boolean isApplicable(Advisor advisor, Class<?> targetClass) {
    if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pa = (PointcutAdvisor) advisor;
        return matchesPointcut(pa.getPointcut(), targetClass);
    }
    // ポイントカットがない場合は全クラスに適用
    return true;
}

private static boolean matchesPointcut(Pointcut pointcut, Class<?> targetClass) {
    if (!pointcut.getClassFilter().matches(targetClass)) {
        return false;
    }
    
    MethodMatcher matcher = pointcut.getMethodMatcher();
    if (matcher == MethodMatcher.TRUE) {
        return true;
    }
    
    Set<Class<?>> types = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    types.add(targetClass);
    
    for (Class<?> type : types) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(type);
        for (Method method : methods) {
            if (matcher.matches(method, targetClass)) {
                return true;
            }
        }
    }
    return false;
}

プロキシオブジェクトの構築

インターセプタが見つかった場合、buildProxyメソッドで実際のプロキシを生成する。各プロキシ対象に対して専用のProxyFactoryインスタンスが作成される。

protected Object buildProxy(Class<?> beanType, String beanName, 
                            Object[] interceptors, TargetSource targetSource) {
    ProxyFactory factory = new ProxyFactory();
    factory.copyFrom(this);
    
    if (!factory.isProxyTargetClass()) {
        analyzeProxyInterfaces(beanType, factory);
    }
    
    Advisor[] advisors = convertToAdvisors(interceptors);
    for (Advisor advisor : advisors) {
        factory.addAdvisor(advisor);
    }
    
    factory.setTargetSource(targetSource);
    return factory.getProxy();
}

自動プロキシクリエーターの登録

AopConfigUtilsユーティリティクラスは、InfrastructureAdvisorAutoProxyCreatorの登録を簡素化する。

public class AopConfigUtils {
    public static final String AUTO_PROXY_CREATOR_BEAN = 
        "org.springframework.aop.config.internalAutoProxyCreator";
    
    public static void registerAutoProxyCreator(BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN)) {
            RootBeanDefinition definition = new RootBeanDefinition(
                InfrastructureAdvisorAutoProxyCreator.class);
            definition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
            definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN, definition);
        }
    }
    
    public static void enforceClassProxying(BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN)) {
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN);
            definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
        }
    }
}

動作検証

検証用に、カスタムアノテーションと Advisor を定義する。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AuditLog {
}
public class AuditInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("監査ログ: " + invocation.getThis().getClass().getSimpleName() 
            + "#" + invocation.getMethod().getName());
        return invocation.proceed();
    }
}
public class AuditPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return AnnotatedElementUtils.findMergedAnnotation(method, AuditLog.class) != null;
    }
}
public class AuditAdvisor extends AbstractGenericPointcutAdvisor {
    public AuditAdvisor() {
        setAdvice(new AuditInterceptor());
    }
    
    @Override
    public Pointcut getPointcut() {
        return new AuditPointcut();
    }
}
public class ServiceBean {
    @AuditLog
    public void execute() {
        System.out.println("execute処理を実行...");
    }
    
    public void process() {
        System.out.println("process処理を実行...");
    }
}

テストコードでは、Advisorを登録し、自動プロキシ生成を有効にする。

@Test
public void verifyAutoProxyMechanism() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    
    // Advisorを組み込みコンポーネントとして登録
    RootBeanDefinition advisorDef = new RootBeanDefinition(AuditAdvisor.class);
    advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    factory.registerBeanDefinition("auditAdvisor", advisorDef);
    
    // 自動プロキシ生成器を登録
    InfrastructureAdvisorAutoProxyCreator creator = new InfrastructureAdvisorAutoProxyCreator();
    creator.setBeanFactory(factory);
    factory.addBeanPostProcessor(creator);
    
    // ターゲットBeanを登録
    factory.registerBeanDefinition("serviceBean", new RootBeanDefinition(ServiceBean.class));
    
    ServiceBean service = factory.getBean("serviceBean", ServiceBean.class);
    service.execute();
    service.process();
}

実行結果:

監査ログ: ServiceBean#execute
execute処理を実行...
process処理を実行...

タグ: Spring Framework AOP 自動プロキシ BeanPostProcessor Advisor

7月28日 04:13 投稿