はじめに
これまでの解説で、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の実装詳細
AbstractAutoProxyCreatorはInstantiationAwareBeanPostProcessorインターフェースを実装し、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)の解決
resolveInterceptorsForBeanはAbstractAdvisorAutoProxyCreatorで実装されるテンプレートメソッドである。
@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処理を実行...