AOP通知以及程式設計式AOP 侵立刪
1.處理AOP配置的通知基本步驟:
(1).獲取AOP配置的通知Advice:
從上一篇部落格《建立AOP代理物件並對目標物件切面攔截》對Spring中採用JDK和CGLIB兩種方式建立AOP動態代理的原始碼分析中,我們瞭解到,在AOP動態代理物件的回撥方法中,都需要使用以下方式獲取AOP配置的通知,並將獲取到的通知和目標物件、代理物件等一起封裝為ReflectiveMethodInvocation物件:
[java] view plain copy //獲取AOP配置的通知 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); …… //根據獲取的通知、目標物件等建立ReflectiveMethodInvocation //如果是CGLIB方式,則建立CglibMethodInvocation物件: //new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy); invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); //沿著獲取的通知鏈,遞迴呼叫所有配置的AOP通知 retVal = invocation.proceed();
(2).建立ReflectiveMethodInvocation物件:
a.建立CglibMethodInvocation物件:
[java] view plain copy //CglibMethodInvocation的構造方法 public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) { //呼叫父類ReflectiveMethodInvocation的構造方法 super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); this.methodProxy = methodProxy; this.protectedMethod = Modifier.isProtected(method.getModifiers()); }
CglibMethodInvocation繼承ReflectiveMethodInvocation類,在建立時首先呼叫父類的初始化方法。
b.建立ReflectiveMethodInvocation物件:
[java] view plain copy //ReflectiveMethodInvocation的構造方法 protected ReflectiveMethodInvocation( Object proxy, Object target, Method method, Object[] arguments, Class targetClass, List<Object> interceptorsAndDynamicMethodMatchers) { this.proxy = proxy; this.target = target; this.targetClass = targetClass; this.method = BridgeMethodResolver.findBridgedMethod(method); this.arguments = arguments; //將獲取到的AOP通知賦值給成員變數 this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; }
(3).處理AOP配置的通知器:
Spring通過呼叫ReflectiveMethodInvocation類來處理AOP配置的通知,CglibMethodInvocation繼承ReflectiveMethodInvocation,因此JDK和CGLIB方式都是通過呼叫ReflectiveMethodInvocation的proceed()方法來處理通知的,處理通知的原始碼如下:
[java] view plain copy //處理AOP配置的通知 public Object proceed() throws Throwable { //如果攔截器鏈中通知已經呼叫完畢 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //這個方法呼叫AopUtils.invokeJoinpointUsingReflection方法, //通過反射機制直接呼叫目標物件方法 return invokeJoinpoint(); } //獲取AOP配置的通知,在ReflectiveMethodInvocation初始化構方法中將獲 //取到的AOP通知賦值給interceptorsAndDynamicMethodMatchers變數 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //如果獲取的通知器或通知是動態匹配方法攔截器型別 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { //動態匹配方法攔截器 InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { //如果匹配,呼叫攔截器的方法 return dm.interceptor.invoke(this); } else { //如果不匹配,遞迴呼叫proceed()方法,知道攔截器鏈被全部呼叫為止 return proceed(); } } else { //如果不是動態匹配方法攔截器,則切入點在構造物件之前進行靜態匹配,呼叫 //攔截器的方法 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
2.AOP代理建立輔助類AdvisedSupport獲取通知:
在1中我們看到,獲取AOP通知的的方法如下:
[java] view plain copy List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
該方法會通過AOP代理建立輔助類AdvisedSupport獲取AOP配置的通知,獲取通知的過程如下:
(1).AdvisedSupport獲取給定方法的通知:
[java] view plain copy //獲取通知 public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { //使用cache快取 MethodCacheKey cacheKey = new MethodCacheKey(method); //首先從快取中獲取 List<Object> cached = this.methodCache.get(cacheKey); //如果快取中沒有,即第一次呼叫 if (cached == null) { //從通知器鏈容器中獲取通知,這裡使用的DefaultAdvisorChainFactory cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass); //快取獲取到的通知 this.methodCache.put(cacheKey, cached); } return cached; }
通過上面的原始碼我們看到,AdvisedSupport第一次獲取通知時,會從通知鏈容器DefaultAdvisorChainFactory中通過getInterceptorsAndDynamicInterceptionAdvice方法獲取指定的通知。
(2).DefaultAdvisorChainFactory獲取指類,指定方法的通知:
DefaultAdvisorChainFactory通過getInterceptorsAndDynamicInterceptionAdvice方法獲取指定類中指定方法的通知,原始碼如下:
[java] view plain copy //獲取通知 public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, Class targetClass) { //根據AOP中配置的通知器建立一個保持獲取到通知的集合 List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); //判斷目標類中是否引入了配置的通知 boolean hasIntroductions = hasMatchingIntroductions(config, targetClass); //獲取通知介面卡註冊單態模式物件 AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); //遍歷AOP配置的通知 for (Advisor advisor : config.getAdvisors()) { //如果通知器的型別是切入點通知器 if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; //如果AOP配置對通知已經過濾,即只包含符合條件的通知器,或者 //獲取當前切入點的類過濾器匹配目標類 if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) { //獲取通知器中的方法攔截器列表 MethodInterceptor[] interceptors = registry.getInterceptors(advisor); //獲取當前通知器切入點的方法匹配器 MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); //如果目標類的方法匹配切入點 if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) { //如果方法匹配器是執行時動態匹配的 if (mm.isRuntime()) { //將通知器中的方法攔截器和方法匹配器封裝後新增到返回的通知器集合中 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; //如果AOP通知配置是預過濾的,或者目標類符合當前通知器的類過濾器 if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) { //獲取通知器中所有的方法攔截器,即通知 Interceptor[] interceptors = registry.getInterceptors(advisor); //將通知新增到要返回的通知集合中 interceptorList.addAll(Arrays.asList(interceptors)); } } //如果通知型別既不是切入點通知器,又不是引入通知器 else { Interceptor[] interceptors = registry.getInterceptors(advisor); //直接將通知新增到要返回的通知集合中 interceptorList.addAll(Arrays.asList(interceptors)); } } return interceptorList; } //檢查目標類和AOP通知配置是否匹配AOP引入規則 private static boolean hasMatchingIntroductions(Advised config, Class targetClass) { //遍歷所有的通知器 for (int i = 0; i < config.getAdvisors().length; i++) { Advisor advisor = config.getAdvisors()[i]; //如果通知器是引入通知器 if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; //使用當前通知器的類過濾器匹配目標類 if (ia.getClassFilter().matches(targetClass)) { return true; } } } return false; }
通過上面DefaultAdvisorChainFactory獲取通知過程原始碼的分析,我們看到通過AdvisorAdapterRegistry的getInterceptors方法獲取通知器的通知,AdvisorAdapterRegistry是一個介面,具體的實現交由其實現類DefaultAdvisorAdapterRegistry提供。
3.DefaultAdvisorAdapterRegistry獲取通知器的通知:
DefaultAdvisorAdapterRegistry的原始碼如下:
[java] view plain copy public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable { //持有通知介面卡的集合 private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3); //構造方法,為通知介面卡集合新增Spring的3種類型通知介面卡 public DefaultAdvisorAdapterRegistry() { registerAdvisorAdapter(new MethodBeforeAdviceAdapter()); registerAdvisorAdapter(new AfterReturningAdviceAdapter()); registerAdvisorAdapter(new ThrowsAdviceAdapter()); } //將通知封裝為通知器 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) { //將方法攔截器型別的通知封裝為預設切入點通知器 return new DefaultPointcutAdvisor(advice); } for (AdvisorAdapter adapter : this.adapters) { //檢查通知介面卡是否支援給定的通知 if (adapter.supportsAdvice(advice)) { return new DefaultPointcutAdvisor(advice); } } throw new UnknownAdviceTypeException(advice); } //獲取通知器的通知 public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3); //通知通知器的通知 Advice advice = advisor.getAdvice(); //如果通知是方法攔截器型別,則不需要適配,直接新增到通知集合中 if (advice instanceof MethodInterceptor) { interceptors.add((MethodInterceptor) advice); } //對通知進行適配,從介面卡中獲取封裝好AOP編制功能的攔截器 for (AdvisorAdapter adapter : this.adapters) { if (adapter.supportsAdvice(advice)) { interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advisor.getAdvice()); } return interceptors.toArray(new MethodInterceptor[interceptors.size()]); } //註冊通知介面卡 public void registerAdvisorAdapter(AdvisorAdapter adapter) { this.adapters.add(adapter); } }
從上述程式碼獲取通知原始碼分析中我們看到,DefaultAdvisorAdapterRegistry的getInterceptors方法中,需要將AOP配置的通知封裝為通知介面卡,下面我們繼續分析通知介面卡的主要原始碼和功能。
4.通知介面卡:
通知介面卡AdvisorAdapter對通知進行封裝,為通知提供Spring AOP的增強功能,下面我們以MethodBeforeAdviceAdapter為例,分析通知介面卡的具體功能:
(1).MethodBeforeAdviceAdapter原始碼:
[java] view plain copy class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { //檢查通知是否為通知介面卡適配的通知型別 public boolean supportsAdvice(Advice advice) { return (advice instanceof MethodBeforeAdvice); } //獲取通知攔截器 public MethodInterceptor getInterceptor(Advisor advisor) { //獲取通知器的通知 MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice(); //將通知封裝為方法攔截器 return new MethodBeforeAdviceInterceptor(advice); } }
通過對MethodBeforeAdviceAdapter的原始碼分析,我們看到通知介面卡在獲取到通知器的通知後,將通知封裝為方法攔截器,我們接下來分析MethodBeforeAdviceInterceptor是如何將通知封裝為方法攔截器的。
(2).MethodBeforeAdviceInterceptor封裝通知:
MethodBeforeAdviceInterceptor封裝通知實現了Spring AOP的織入功能,其原始碼如下:
[java] view plain copy public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { private MethodBeforeAdvice advice; //攔截器構造方法,初始化通知 public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } //攔截器的回撥方法,會在代理物件的方法呼叫前觸發回撥 public Object invoke(MethodInvocation mi) throws Throwable { //在目標方法呼叫之前,先呼叫前置通知的before方法 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); //繼續呼叫通知攔截鏈,呼叫ReflectiveMethodInvocation的proceed方法 return mi.proceed(); } }
通過對MethodBeforeAdviceInterceptor攔截器的原始碼分析,我們看到,Spring對AOP處理的基本流程:
首先,為目標物件物件建立AOP代理物件,根據AOP配置獲取通知器,通知器中持有切入點和通知。
其次,根據通知器中的切入點,獲取目標類目標方法的通知,並將通知封裝為攔截器,新增到代理攔截鏈中。
最後,呼叫目標物件目標方法時,呼叫AOP代理物件,根據通知在呼叫目標物件方法時觸發呼叫通知鏈中通知攔截器的回撥方法。
5.方法攔截器:
在4中我們已經分析了MethodBeforeAdviceInterceptor原始碼,瞭解到方法攔截器主要作用是根據通知型別在呼叫目標方法時觸發通知的回撥,我們接下來分析AfterReturningAdviceInterceptor和ThrowsAdviceInterceptor攔截器的原始碼,瞭解對後置通知和異常通知的處理實現:
(1).AfterReturningAdviceInterceptor:
[java] view plain copy public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { private final AfterReturningAdvice advice; //後置通知攔截器構造方法,初始化通知 public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } //後置通知攔截器回撥方法 public Object invoke(MethodInvocation mi) throws Throwable { //呼叫ReflectiveMethodInvocation的proceed方法,呼叫通知鏈 Object retVal = mi.proceed(); //通知鏈呼叫最後才呼叫後置通知的返回之後回撥方法 this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; } }
(2).ThrowsAdviceInterceptor:
異常通知攔截器ThrowsAdviceInterceptor和前置通知攔截器MethodBeforeAdviceInterceptor、後置通知攔截器AfterReturningAdviceInterceptor類似,不同之處在於需要維護異常處理器,因此更加複雜,原始碼如下:
[java] view plain copy public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice { private static final String AFTER_THROWING = "afterThrowing"; private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class); private final Object throwsAdvice; //key為類,value為method的異常處理map private final Map<Class, Method> exceptionHandlerMap = new HashMap<Class, Method>(); //異常通知攔截器構造方法 public ThrowsAdviceInterceptor(Object throwsAdvice) { Assert.notNull(throwsAdvice, "Advice must not be null"); //初始化異常通知 this.throwsAdvice = throwsAdvice; //獲取異常通知的方法 Method[] methods = throwsAdvice.getClass().getMethods(); //遍歷所有配置異常通知的方法 for (Method method : methods) { //如果方法名為"afterThrowing",且方法引數為1或4,且方法引數的父類為 //Throwable,說明異常通知中有異常處理器 if (method.getName().equals(AFTER_THROWING) && (method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) && Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1]) ) { //新增異常處理器 this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method); if (logger.isDebugEnabled()) { logger.debug("Found exception handler method: " + method); } } } //沒有配置異常處理器 if (this.exceptionHandlerMap.isEmpty()) { throw new IllegalArgumentException( "At least one handler method must be found in class [" + throwsAdvice.getClass() + "]"); } } //獲取異常處理器數目 public int getHandlerMethodCount() { return this.exceptionHandlerMap.size(); } //獲取異常處理器 private Method getExceptionHandler(Throwable exception) { //獲取給定異常類 Class exceptionClass = exception.getClass(); if (logger.isTraceEnabled()) { logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]"); } //從異常處理器map中獲取給定異常類的處理器 Method handler = this.exceptionHandlerMap.get(exceptionClass); //如果異常處理器為null,且異常類不是Throwable while (handler == null && !exceptionClass.equals(Throwable.class)) { //獲取異常類的基類 exceptionClass = exceptionClass.getSuperclass(); //獲取基類的異常處理器 handler = this.exceptionHandlerMap.get(exceptionClass); } if (handler != null && logger.isDebugEnabled()) { logger.debug("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler); } return handler; } //異常通知的回撥方法 public Object invoke(MethodInvocation mi) throws Throwable { //把目標物件方法呼叫放入try/catch中 try { return mi.proceed(); } catch (Throwable ex) { //在catch中觸發異常通知的回撥 Method handlerMethod = getExceptionHandler(ex); if (handlerMethod != null) { //使用異常處理器處理異常 invokeHandlerMethod(mi, ex, handlerMethod); } //將異常向上丟擲 throw ex; } } //異常處理器處理異常 private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable { Object[] handlerArgs; //如果方法只有一個引數 if (method.getParameterTypes().length == 1) { handlerArgs = new Object[] { ex }; } //獲取方法的引數列表 else { handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex}; } try { //使用JDK反射機制,呼叫異常通知的異常處理方法 method.invoke(this.throwsAdvice, handlerArgs); } catch (InvocationTargetException targetEx) { throw targetEx.getTargetException(); } } }
6.ProxyFactory實現程式設計式AOP:
在上一篇部落格《建立AOP代理物件並對目標物件切面攔截》中,我們以ProxyFactoryBean 為例,分析了Spring建立AOP代理物件以及對目標物件進行切面攔截的實現過程,在Spring中還有另一個建立AOP代理物件的容器——ProxyFactory,兩個建立AOP代理物件容器的區別如下:
a.ProxyFactory:建立程式設計式的Spring AOP應用。
b.ProxyFactoryBean:建立宣告式的Spring AOP應用。
我們現在通過分析ProxyFactory原始碼,瞭解Spring程式設計式AOP應用的具體實現:
(1).使用ProxyFactory程式設計式AOP應用的簡單例子:
[java] view plain copy //獲取目標物件 TargetImpl target = new TargetImpl(); //建立目標物件的代理工廠 ProxyFactory aopFactory = new ProxyFactory(target); //為目標物件代理工廠新增通知器 aopFactory.addAdvisor(要新增的Advisor); //為目標物件代理工廠新增通知 aopFactory.addAdvice(要新增的Advice); //獲取目標物件的代理 TargetImpl targetProxy = (TargetImpl)aopFactory.getProxy();
(2).ProxyFactory原始碼:
[java] view plain copy public class ProxyFactory extends ProxyCreatorSupport { //建立一個空的AOP代理容器 public ProxyFactory() { } //為目標物件建立AOP代理容器 public ProxyFactory(Object target) { Assert.notNull(target, "Target object must not be null"); setInterfaces(ClassUtils.getAllInterfaces(target)); setTarget(target); } //根據代理介面建立AOP代理容器 public ProxyFactory(Class[] proxyInterfaces) { setInterfaces(proxyInterfaces); } //為指定代理介面和攔截器建立AOP代理容器 public ProxyFactory(Class proxyInterface, Interceptor interceptor) { //為代理物件新增代理介面 addInterface(proxyInterface); //為代理物件新增攔截器 addAdvice(interceptor); } //根據指定目標源和代理介面建立代理容器 public ProxyFactory(Class proxyInterface, TargetSource targetSource) { addInterface(proxyInterface); //為通知設定目標源 setTargetSource(targetSource); } //獲取AOP代理 public Object getProxy() { return createAopProxy().getProxy(); } //使用指定類載入器建立AOP代理 public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); } //獲取指定代理介面和攔截器的AOP代理 public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) { return (T) new ProxyFactory(proxyInterface, interceptor).getProxy(); } //獲取指定代理介面和目標源的AOP代理 public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) { return (T) new ProxyFactory(proxyInterface, targetSource).getProxy(); } //為指定目標源建立AOP代理 public static Object getProxy(TargetSource targetSource) { if (targetSource.getTargetClass() == null) { throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class"); } ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTargetSource(targetSource); proxyFactory.setProxyTargetClass(true); return proxyFactory.getProxy(); } }
通過上述對ProxyFactory原始碼的分析可以看出,獲取代理物件的getProxy()方法中,呼叫ProxyCreatorSupport的createAopProxy()方法獲得DefaultAopProxyFactory物件,通過呼叫DefaultAopProxyFactory類的createAopProxy方法來呼叫JDK或者CGLIB建立AOP代理物件,與ProxyFactoryBean實現原理相同,通知的配置以及方法的通知鏈攔截呼叫等都與ProxyFactoryBean完全相同,這裡不再贅述,請實現參考上一篇部落格對ProxyFactoryBean的分析。