Spring原始碼分析之ProxyFactoryBean方式實現Aop功能的分析
阿新 • • 發佈:2018-12-29
實現Aop功能有兩種方式,
1. ProxyFactoryBean方式: 這種方式是通過配置實現
2. ProxyFactory方式:這種方式是通過程式設計實現
這裡只說ProxyFactoryBean方式
首先說下具體的配置,一個例子如下:
<bean id="testAdvisor" class="com.abc.TestAdvisor" <property name="pointcut" ref="bookPointcut"/> <property name="advice" ref="aroundMethod"></property> </bean> <bean id="testAop" class="org.springframeword.aop.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.test.AbcInterface</value> </property> <property name="target"> <bean class="com.abc.TestTarget"/> </property> <property name="interceptorNames"> <list> <value>testAdvisor</value> </list> </property> </bean>
上述配置中,testAdvisor是配置了一個通知器,該通知器配置了pointcut,即執行該通知需要滿足的條件,還配置了匹配條件時要執行的方法,target配置的是要被增強的目標物件,interceptorNames配置的是一些通知,用來增強目標物件。proxyInterfaces配置的是需要代理的介面名的字串陣列。如果沒有提供,將為目標類使用一個CGLIB代理,即這個介面的配置將會影響是用JDK還是CGLIB來建立目標物件的代理物件。
首先看下ProxyFactoryBean的getObject方法
@Override public Object getObject() throws BeansException { initializeAdvisorChain(); //生成代理物件時,因為Spring中有singleton型別和prototype型別這兩種不同的Bean,所以要對代理物件的生成做一個區分 if (isSingleton()) { //生成singleton的代理物件,這個方法是ProxyFactoryBean生成AOPProxy代理物件的呼叫入口 //代理物件會封裝對target目標物件的呼叫,也就是說針對target物件的方法呼叫行為會被這裡生成的代理物件所攔截 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(); } }
這個方法其實就是用來為目標物件生成代理物件的
initializeAdvisorChain是初始化通知器鏈,即從上述配置中讀取interceptorNames引數的值就可以拿到所有為目標物件配置的通知器,該方法的程式碼如下:
/** * Create the advisor (interceptor) chain. Advisors that are sourced * from a BeanFactory will be refreshed each time a new prototype instance * is added. Interceptors added programmatically through the factory API * are unaffected by such changes. * 初始化通知器鏈,通知器鏈封裝了一系列的攔截器,這些攔截器都需要從配置中讀取,然後為代理物件的生成做好準備 */ private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException { //這個標誌位是用來表示通知器鏈是否已經初始化,初始化的工作發生在應用第一次通過ProxyFactoryBean去獲取代理物件的時候 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. //這裡是新增Advisor鏈的呼叫,是通過interceptorNames屬性進行配置的 //this.interceptorNames就是配置中配置的所有通知器 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. //如果程式在這裡被呼叫,那麼需要加入命名的攔截器advice,並且需要檢查這個Bean是singleton還是prototype Object advice; if (this.singleton || this.beanFactory.isSingleton(name)) { // Add the real Advisor/Advice to the chain. //取得advisor的地方,是通過beanFactory取得的,把intercepNames這個List中的interceptor的名字交給BeanFactory,然後通過getBean去獲取 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; }
其他的getSingletonInstance方法和newPrototypeInstance類其實就是構造代理物件
其中getSingletonInstance方法的程式碼如下:
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
//根據AOP框架來判斷需要代理的介面
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
//設定代理物件的介面
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
//createAopProxy()方法可能會返回ObjenesisCglibAopProxy物件,也可能會返回JdkDynamicAopProxy物件
//然後getProxy方法會根據ObjenesisCglibAopProxy或者JdkDynamicAopProxy物件的getProxy方法來生成最終的代理物件
//這就是所謂的,Spring生成代理物件的兩種方式,一種是CGLIB,一種是JDK
this.singletonInstance = getProxy(createAopProxy()); //這裡的方法會使用ProxyFactory來生成需要的Proxy,通過createAopProxy返回的AopProxy來得到代理物件
}
return this.singletonInstance;
}
注意this.singletonInstance = getProxy(createAopProxy());這行程式碼
createAopProxy()方法可能會返回ObjenesisCglibAopProxy物件,也可能會返回JdkDynamicAopProxy物件,這個邏輯是在DefaultAopProxyFactory
類中實現的,邏輯如下:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
//config裡面封裝了想要生成的代理物件的資訊
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass(); //首先要從AdvisedSupport物件中取得配置的目標物件,如果目標物件為空,則直接丟擲異常,因為連目標物件都沒有,還為誰建立代理物件
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
//關於AopProxy代理物件的生成,需要考慮使用哪種生成方式,如果目標物件是介面類,那麼適合使用JDK來生成代理物件,否則spring會使用CGLIB來生成目標物件的代理物件
//對於具體的AopProxy代理物件的生成,最終並不是由DefaultAopProxyFactory來完成,而是分別由JdkDynamicAopProxy和ObjenesisCglibAopProxy完成
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config); //使用JDK來生成AOPProxy代理物件
}
return new ObjenesisCglibAopProxy(config); //使用第三方CGLIB來生成AOPProxy代理物件
}
else {
return new JdkDynamicAopProxy(config);
}
}
可以看到,它會根據目標類是不是介面等資訊來判定使用ObjenesisCglibAopProxy還是JdkDynamicAopProxy
然後getProxy方法會根據ObjenesisCglibAopProxy或者JdkDynamicAopProxy物件的getProxy方法來生成最終的代理物件 這就是所謂的,Spring生成代理物件的兩種方式,一種是CGLIB,一種是JDK
下面說說用JdkDynamicAopProxy方式生成的代理物件的攔截方式,它實際用的就是JDK的動態代理
我們知道,動態代理攔截的入口是實現了InvocationHandler介面後的invoke方法,即所有對目標方法的呼叫首先會被invoke方法攔截
而JdkDynamicAopProxy方式實現的動態代理的攔截入口也是該類的invoke方法,該類的部分方法如下:
/**
* JDK-based {@link AopProxy} implementation for the Spring AOP framework
*InvocationHandler介面的invoke方法就是攔截回撥的入口,即對目標方法的呼叫會先被invoke方法攔截,並在invoke方法裡面來呼叫目標方法
*/
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
//首先從advised物件中取得代理物件的代理介面配置
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//第三個引數需要實現InvocationHandler介面和invoke方法,這個invoke方法是Proxy代理物件的回撥方法
//這種方式其實就是用JDK的動態代理來為目標物件建立代理物件,對目標物件方法的呼叫就是由這個代理物件來呼叫的
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
* 攔截回撥入口
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
//如果目標物件沒有實現Object類的基本方法:equals
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
//如果目標物件沒有實現Object類的基本方法:hashcode
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
//根據代理物件的配置來呼叫服務
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
//得到目標物件
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// Get the interception chain for this method. 獲取方法method的攔截器鏈
// 攔截器鏈實際就是由一系列的Advice通知物件組成的
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
//如果沒有定義攔截器鏈,就直接呼叫target物件的對應方法
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); //適配引數
//呼叫target物件的對應方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
//如果有攔截器的設定,那麼需要呼叫攔截器之後才呼叫目標物件的相應方法,通過構造一個ReflectiveMethodInvocation來實現
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
//沿著攔截器鏈繼續前進
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
}
上述getProxy方法其實就是JdkDynamicAopProxy用來給目標物件生成程式碼物件的方法
而invoke就是對目標方法呼叫時的攔截入口
其中的
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
這行程式碼就是獲取到目標物件所有的攔截器,為什麼這裡是獲取攔截器?其實在上面初始化通知器鏈的時候拿到的都是配置的通知器,這個方法是要將這些通知器用對應的介面卡
適配成對應的攔截器,至於為什麼要做這個步驟,在我的另外一篇部落格中說的很清楚了,地址如下:
http://blog.csdn.net/u011734144/article/details/73436539
這裡轉換成攔截器後,也並不是直接就要將該攔截器加入最終要執行的攔截器鏈中,還需要判斷對應的通知是否應該執行,對應的程式碼片段如下:
//對配置的advisor通知器進行逐個遍歷,這個通知器鏈都是配置在interceptorNames中的
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
//registry.getInterceptors(advisor)是對從ProxyFactoryBean配置中得到的通知進行適配,從而得到相應的攔截器,再把它加入到前面設定好的list中去
//從而完成所謂的攔截器註冊過程
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
這裡需要判斷通知器中配置的切入點是否匹配當前要被呼叫的方法,即MethodMatchers.matches是否為true,只有匹配的通知才會將對應的攔截器加入到最終待執行的攔截器鏈中
接下來invoke方法中比較核心的就是如下程式碼:
retVal = invocation.proceed();
這個方法其實就是啟動攔截器鏈的執行,依次執行每一個攔截器鏈,在每一個攔截器裡面都會根據通知的型別來決定是先執行通知的方法還是先繼續執行下一個攔截器,
攔截器中具體的執行邏輯也請參考我上面說的我的另外一篇文章