Spring AOPソース分析(七)ProxyFactoryBean紹介

9914 ワード

この文章の中でSpringの自分のAOPを話して、どの方式がSpringの自分で実現するAOPかをはっきりさせて、どの方式がSpringがaspectjのAOPを導入するのですか?Spring自身のAOPが実現したのはProxyFactoryBeanです.まず使用例を見てください.インターフェースAService、実現類AServiceImpl、MyBefore Adviceを教えます.

public interface AService {

	public void fooA(String _msg);  
	  
    public void barA(); 
}

public class AServiceImpl implements AService{

	@Override
	public void fooA(String _msg) {
		 System.out.println("AServiceImpl.fooA(msg:"+_msg+")");
	}

	@Override
	public void barA() {
		 System.out.println("AServiceImpl.barA()");  
	}

}

public class MyBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] args, Object target)
			throws Throwable {
		System.out.println("run my before advice");
	}

}
そしてxmlの設定です.

<bean id="aServiceImpl" class="com.lg.aop.service.impl.AServiceImpl"/>
	<bean id="myBeforAdvice" class="com.lg.aop.MyBeforeAdvice"/>
	
	<bean class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="interfaces" value="com.lg.aop.service.AService"/>
		<property name="target">
			<ref bean="aServiceImpl"/>
		</property>
		 <property name="interceptorNames">  
            <list>  
                <value>myBeforAdvice</value>  
            </list>  
        </property>  
	</bean>
そして使用できます.

 @Autowired
	private AService aService;
	
	@Test
	public void testAOP(){
		aService.barA();
	}
このユニットのテストを実行して、次のエラーが表示されます.

No qualifying bean of type [com.lg.aop.service.AService] is defined: expected single matching bean but found 2: aServiceImpl,org.springframework.aop.framework.ProxyFactoryBean#0
その理由は、インターフェイスAServiceに対して、aServiceImplとProxyFactoryBenの2つのプロキシがあるからです.だから私たちは@Autowiredを使用できません.だから名前通りに注入するために、ProxyFactoryBenで生成されたエージェントの名前をどうやって取得しますか?実はProxyFactoryBeanの配置の名前です.ProxyFactoryBenはFactoryBeanインターフェースを実現しているので、このようなインターフェースはコンテナからこのbeanを取得し、取得したものではなく彼のgetObject方法から戻った値を取得し、FactoryBenの文書を参照してください.

/**
 * Interface to be implemented by objects used within a {@link BeanFactory}
 * which are themselves factories. If a bean implements this interface,
 * it is used as a factory for an object to expose, not directly as a bean
 * instance that will be exposed itself.
 *
 * <p><b>NB: A bean that implements this interface cannot be used as a
 * normal bean.</b> A FactoryBean is defined in a bean style, but the
 * object exposed for bean references ({@link #getObject()} is always
 * the object that it creates.
だから、beanNameを通じてProxyFactoryBeanを見つけましたが、このオブジェクトに戻るのではなく、彼のgetObject方法の戻り値を返します.ですから、ProxyFactoryBenのidを通じて、その生成されたプロキシオブジェクトを取得することができます.

<bean  id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
   
注入する時は名前で注入します.

@Resource(name="aServiceImplProxy")
	private AService aService;
そして正常に運行できます.

run my before advice
AServiceImpl.barA()
それから、私たちはソースコードの分析を行います.まず、プロキシオブジェクトはどうやって生成されますか?ProxyFactoryBeanのgetObject方法では、

public Object getObject() throws BeansException {
//  1
		initializeAdvisorChain();
		if (isSingleton()) {
//  2
			return getSingletonInstance();
		}
		else {
			if (this.targetName == null) {
				logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
						"Enable prototype proxies by setting the 'targetName' property.");
			}
			return newPrototypeInstance();
		}
	}
ポイント1:私たちが配置したインターフェースNamesによって対応のbeanを取得し、Advisorに転化します.以下のとおりです

private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
		if (this.advisorChainInitialized) {
			return;
		}

		if (!ObjectUtils.isEmpty(this.interceptorNames)) {
			if (this.beanFactory == null) {
				throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
						"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
			}

			// Globals can't be last unless we specified a targetSource using the property...
			if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
					this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
				throw new AopConfigException("Target required after globals");
			}

			// Materialize interceptor chain from bean names.
			for (String name : this.interceptorNames) {
				if (logger.isTraceEnabled()) {
					logger.trace("Configuring advisor or advice '" + name + "'");
				}

				if (name.endsWith(GLOBAL_SUFFIX)) {
					if (!(this.beanFactory instanceof ListableBeanFactory)) {
						throw new AopConfigException(
								"Can only use global advisors or interceptors with a ListableBeanFactory");
					}
					addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
							name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
				}

				else {
					// If we get here, we need to add a named interceptor.
					// We must check if it's a singleton or prototype.
					Object advice;
					if (this.singleton || this.beanFactory.isSingleton(name)) {
						// Add the real Advisor/Advice to the chain.
						advice = this.beanFactory.getBean(name);
					}
					else {
						// It's a prototype Advice or Advisor: replace with a prototype.
						// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
						advice = new PrototypePlaceholderAdvisor(name);
					}
					addAdvisorOnChainCreation(advice, name);
				}
			}
		}

		this.advisorChainInitialized = true;
	}
this.advisor CharinInitialized:表示はすでに初期化されていますか?初期化するなら、初期化は行われません.そしてinterceptorNamesをAdvisorに変換します.interceptorNamesに含まれている文字列を容器に検索します.*が含まれている場合は、一定のマッチングが行われ、該当するものは全部含まれます.公式文書のように、

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="service"/>
    <property name="interceptorNames">
        <list>
            <value>global*</value>
        </list>
    </property>
</bean>

<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
この中間ページはAdviceからAdvisorへの変換を経て、次のようになります.

private void addAdvisorOnChainCreation(Object next, String name) {
		// We need to convert to an Advisor if necessary so that our source reference
		// matches what we find from superclass interceptors.
		Advisor advisor = namedBeanToAdvisor(next);
		if (logger.isTraceEnabled()) {
			logger.trace("Adding advisor with name '" + name + "'");
		}
		addAdvisor(advisor);
	}

private Advisor namedBeanToAdvisor(Object next) {
		try {
			return this.advisorAdapterRegistry.wrap(next);
		}
		}
	}

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
		if (adviceObject instanceof Advisor) {
			return (Advisor) adviceObject;
		}
		if (!(adviceObject instanceof Advice)) {
			throw new UnknownAdviceTypeException(adviceObject);
		}
		Advice advice = (Advice) adviceObject;
		if (advice instanceof MethodInterceptor) {
			// So well-known it doesn't even need an adapter.
			return new DefaultPointcutAdvisor(advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			// Check that it is supported.
			if (adapter.supportsAdvice(advice)) {
				return new DefaultPointcutAdvisor(advice);
			}
		}
		throw new UnknownAdviceTypeException(advice);
	}
この小包の過程はもう何回も見ました.アダプターを採用したモデルです.その後は他のAOP方式と接続し、実現するインターフェースとパラメータを設定し、DefaultAopProxyFactoryを使ってまずAopProxyを作成し、JdkDynamicAopProxyかCglibAopProxyかを設定し、AopProxyのgetProxy方法を呼び出して代理対象を取得することができます.このプロセスは前のブログhttp://lgbolgger.iteye.com/blog/2119810を参照してください.このような方式で実現されるAOPはやはり面倒くさいです.同時に一つのProxyFactoryBeanを配置すると一つのターゲットに対してだけブロックできます.複数のターゲットをブロックするには複数のProxyFactoryBenを配置する必要があります.やはりSpringで導入したaspectjのAOP方式を使ってAOPプログラムを行うことが多いです.転載したいなら出典を明記してください.http://lgbolgger.iteye.com/blog/2122993著者:iteyの卓球狂魔です.