Spring AOP 概述
AOP是OOP的延續,是Aspect Oriented Programming的縮寫,意思是面向切面編程。可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。AOP設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP 可以說也是這種目標的一種實現。我們現在做的一些非業務,如:日志、事務、安全等都會寫在業務代碼中(也即是說,這些非業務類橫切於業務類),但這些代碼往往是重復,復制——粘貼式的代碼會給程序的維護帶來不便,AOP 就實現了把這些業務需求與系統需求分開來做。這種解決的方式也稱代理機制。
AOP里面的幾個概念
1.切面(Aspect)
官方的抽象定義為“一個關注點的模塊化,這個關注點可能會橫切多個對象”。“切面”在ApplicationContext 中<aop:aspect>來配置。連接點(Joinpoint) :程序執行過程中的某一行為,例如,MemberService.get 的調用或者MemberService.delete 拋出異常等行為。
2、通知(Advice)
“切面”對於某個“連接點”所產生的動作。其中,一個“切面”可以包含多個“Advice”。
3、切入點(Pointcut)
匹配連接點的斷言,在 AOP 中通知和一個切入點表達式關聯。切面中的所有通知所關注的連接點,都由切入點表達式來決定。
4、目標對象(Target Object)
被一個或者多個切面所通知的對象。當然在實際運行時,SpringAOP 采用代理實現,實際 AOP 操作的是 TargetObject 的代理對象。
5、AOP 代理(AOP Proxy)
在 Spring AOP 中有兩種代理方式,JDK 動態代理和 CGLib 代理。默認情況下,TargetObject 實現了接口時,則采用 JDK 動態代理,反之,采用 CGLib 代理。強制使用 CGLib 代理需要將 <aop:config>的 proxy-target-class 屬性設為 true。
通知類型:
6、前置通知(Before Advice)
在某連接點(JoinPoint)之前執行的通知,但這個通知不能阻止連接點前的執行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素進行聲明。
7、后置通知(After Advice)
當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext 中在<aop:aspect>里面使用<aop:after>元素進行聲明。
8、返回后通知(After Return Advice)
在某連接點正常完成后執行的通知,不包括拋出異常的情況。ApplicationContext 中在<aop:aspect>里面使用<after-returning>元素進行聲明。
9、環繞通知(Around Advice)
包圍一個連接點的通知,類似 Web 中 Servlet 規范中的 Filter 的 doFilter 方法。可以在方法的調用前后完成自定義的行為,也可以選擇不執行。ApplicationContext 中在<aop:aspect>里面使用<aop:around>元素進行聲明。例如,ServiceAspect 中的 around 方法。
10、異常通知(After Throwing Advice)
在 方 法 拋 出 異 常 退 出 時 執 行 的 通 知 。 ApplicationContext 中 在 <aop:aspect> 里 面 使 用<aop:after-throwing>元素進行聲明。
注:可以將多個通知應用到一個目標對象上,即可以將多個切面織入到同一目標對象。使用 Spring AOP 可以基於兩種方式,一種是比較方便和強大的注解方式,另一種則是中規中矩的 xml配置方式。
先說注解,使用注解配置 Spring AOP 總體分為兩步,第一步是在 xml 文件中聲明激活自動掃描組件功能,同時激活自動代理功能:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="com.stu"/> <context:annotation-config /> </beans>
第二步是為 Aspect 切面類添加注解:
//聲明這是一個組件 @Component //聲明這是一個切面Bean,AnnotaionAspect是一個面,由框架實現的 @Aspect public class AnnotaionAspect { private final static Logger log = Logger.getLogger(AnnotaionAspect.class); //配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點 //切點的集合,這個表達式所描述的是一個虛擬面(規則) //就是為了Annotation掃描時能夠拿到注解中的內容 @Pointcut("execution(* com.stu.aop.service..*(..))") public void aspect(){} /* * 配置前置通知,使用在方法aspect()上注冊的切入點 * 同時接受JoinPoint切入點對象,可以沒有該參數 */ @Before("aspect()") public void before(JoinPoint joinPoint){ log.info("before " + joinPoint); } //配置后置通知,使用在方法aspect()上注冊的切入點 @After("aspect()") public void after(JoinPoint joinPoint){ log.info("after " + joinPoint); } //配置環繞通知,使用在方法aspect()上注冊的切入點 @Around("aspect()") public void around(JoinPoint joinPoint){ long start = System.currentTimeMillis(); try { ((ProceedingJoinPoint) joinPoint).proceed(); long end = System.currentTimeMillis(); log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!"); } catch (Throwable e) { long end = System.currentTimeMillis(); log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage()); } } //配置后置返回通知,使用在方法aspect()上注冊的切入點 @AfterReturning("aspect()") public void afterReturn(JoinPoint joinPoint){ log.info("afterReturn " + joinPoint); } //配置拋出異常后通知,使用在方法aspect()上注冊的切入點 @AfterThrowing(pointcut="aspect()", throwing="ex") public void afterThrow(JoinPoint joinPoint, Exception ex){ log.info("afterThrow " + joinPoint + "\t" + ex.getMessage()); } }
測試代碼
@ContextConfiguration(locations = {"classpath*:application-context.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class AnnotationTest { @Autowired MemberService memberService; @Test public void test(){ System.out.println("================"); memberService.save(new Member()); System.out.println("================"); try { memberService.delete(1L); } catch (Exception e) { //e.printStackTrace(); } } }
測試結果
下面說下xml的簡單使用
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 注解驅動加上這句話 --> <!--<aop:aspectj-autoproxy proxy-target-class="true"/>--> <bean id="xmlAspect" class="com.stu.aop.aspect.XmlAspect"></bean> <!--AOP配置 --> <aop:config> <!--聲明一個切面,並注入切面Bean,相當於@Aspect --> <aop:aspect ref="xmlAspect" > <!--配置一個切入點,相當於@Pointcut --> <aop:pointcut expression="execution(* com.stu.aop.service..*(..))" id="simplePointcut"/> <!--配置通知,相當於@Before、@After、@AfterReturn、@Around、@AfterThrowing --> <aop:before pointcut-ref="simplePointcut" method="before"/> <aop:after pointcut-ref="simplePointcut" method="after"/> <aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/> <aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/> <aop:around pointcut-ref="simplePointcut" method="around"/> </aop:aspect> </aop:config> </beans>
XmlAspect就是一個切面類,具體實現就不貼出來了
通常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?
modifiers-pattern:方法的操作權限
ret-type-pattern:返回值
declaring-type-pattern:方法所在的包
name-pattern:方法名
parm-pattern:參數名
throws-pattern:異常
其 中 , 除 ret-type-pattern 和 name-pattern 之 外 , 其 他 都 是 可 選 的 。execution(* com.spring.service.*.*(..))表示 com.spring.service 包下,返回值為任意類型;方法名任意;參數不限制的所有方法。
在上面的 Aspect 切面 Bean 中已經看到了,每個通知方法第一個參數都是 JoinPoint。其實,在 Spring中,任何通知(Advice)方法都可以將第一個參數定義為org.aspectj.lang.JoinPoint 類型用以接受當前連接點對象。JoinPoint 接口提供了一系列有用的方法, 比如 getArgs() (返回方法參數)、getThis() (返回代理對象)、getTarget() (返回目標)、getSignature() (返回正在被通知的方法相關信息)和 toString() (打印出正在被通知的方法的有用信息)。
Spring AOP 源碼分析
尋找入口
Spring 的 AOP 是通過接入 BeanPostProcessor 后置處理器開始的,它是 Spring IOC 容器經常使用到的一個特性,這個 Bean 后置處理器是一個監聽器,可以監聽容器觸發的 Bean 聲明周期事件。后置處
理器向容器注冊以后,容器中管理的 Bean 就具備了接收 IOC 容器事件回調的能力。
1、BeanPostProcessor 源碼
public interface BeanPostProcessor { //為在Bean的初始化前提供回調入口 @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } //為在Bean的初始化之后提供回調入口 @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
這兩個回調的入口都是和容器管理的 Bean 的生命周期事件緊密相關,可以為用戶提供在 Spring IOC容器初始化 Bean 過程中自定義的處理操作。
2、AbstractAutowireCapableBeanFactory 類對容器生成的 Bean 添加后置處理器
BeanPostProcessor 后置處理器的調用發生在 Spring IOC 容器完成對 Bean 實例對象的創建和屬性的依賴注入完成之后,在對 Spring 依賴注入的源碼分析過程中我們知道,當應用程序第一次調用 getBean()
方法(lazy-init 預實例化除外)向 Spring IOC 容器索取指定 Bean 時觸發 Spring IOC 容器創建 Bean 實例 對 象 並 進 行 依 賴 注 入 的 過 程 , 其 中 真 正 實 現 創 建 Bean 對 象 並 進 行 依 賴 注 入 的 方 法 是AbstractAutowireCapableBeanFactory 類的 doCreateBean()方法,主要源碼如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ....// Initialize the bean instance. //Bean對象的初始化,依賴注入在此觸發 //這個exposedObject在初始化完成之后返回作為依賴注入完成后的Bean Object exposedObject = bean; try { //將Bean實例對象封裝,並且Bean定義中配置的屬性值賦值給實例對象 populateBean(beanName, mbd, instanceWrapper); //初始化Bean對象 exposedObject = initializeBean(beanName, exposedObject, mbd); } ....
return exposedObject; }
從上面的代碼中我們知道,為 Bean 實例對象添加 BeanPostProcessor 后置處理器的入口的是initializeBean()方法。
3、initializeBean()方法為容器產生的 Bean 實例對象添加 BeanPostProcessor 后置處理器
同樣在 AbstractAutowireCapableBeanFactory 類中,initializeBean()方法實現為容器創建的 Bean實例對象添加 BeanPostProcessor 后置處理器,源碼如下:
//初始容器創建的Bean實例對象,為其添加BeanPostProcessor后置處理器 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { //JDK的安全機制驗證權限 if (System.getSecurityManager() != null) { //實現PrivilegedAction接口的匿名內部類 AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { //為Bean實例對象包裝相關屬性,如名稱,類加載器,所屬容器等信息 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; //對BeanPostProcessor后置處理器的postProcessBeforeInitialization //回調方法的調用,為Bean實例初始化前做一些處理 if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } //調用Bean實例對象初始化的方法,這個初始化方法是在Spring Bean定義配置 //文件中通過init-method屬性指定的 try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } //對BeanPostProcessor后置處理器的postProcessAfterInitialization //回調方法的調用,為Bean實例初始化之后做一些處理 if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; } //調用BeanPostProcessor后置處理器實例對象初始化之前的處理方法 public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; //遍歷容器為所創建的Bean添加的所有BeanPostProcessor后置處理器 for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { //調用Bean實例所有的后置處理中的初始化前處理方法,為Bean實例對象在 //初始化之前做一些自定義的處理操作 Object current = beanProcessor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } //調用BeanPostProcessor后置處理器實例對象初始化之后的處理方法 public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; //遍歷容器為所創建的Bean添加的所有BeanPostProcessor后置處理器 for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { //調用Bean實例所有的后置處理中的初始化后處理方法,為Bean實例對象在 //初始化之后做一些自定義的處理操作 Object current = beanProcessor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
BeanPostProcessor 是一個接口,其初始化前的操作方法和初始化后的操作方法均委托其實現子類來實現,在 Spring 中,BeanPostProcessor 的實現子類非常的多,分別完成不同的操作,如:AOP 面向切面編程的注冊通知適配器、Bean 對象的數據校驗、Bean 繼承屬性、方法的合並等等,我們以最簡單的AOP 切面織入來簡單了解其主要的功能。下面我們來分析其中一個創建 AOP 代理對象的子類AbstractAutoProxyCreator 類。該類重寫了 postProcessAfterInitialization()方法。選擇代理策略進入 postProcessAfterInitialization()方法,我們發現調到了一個非常核心的方法 wrapIfNecessary(),其源碼如下:
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 判斷是否不應該代理這個 bean if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } /** * 判斷是否是一些 InfrastructureClass 或者是否應該跳過這個 bean。 * 所謂 InfrastructureClass 就是指 Advice/PointCut/Advisor 等接口的實現類。 * shouldSkip 默認實現為返回 false,由於是 protected 方法, 子類可以覆蓋。 */ if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 獲取這個 bean 的 advice // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } return proxyFactory.getProxy(getProxyClassLoader()); }
整個過程跟下來,我發現最終調用的是 proxyFactory.getProxy()方法。到這里我們大概能夠猜到proxyFactory 有 JDK 和 CGLib 的,那么我們該如何選擇呢?最終調用的是 DefaultAopProxyFactory
的 createAopProxy()方法:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
上面我們已經了解到 Spring 提供了兩種方式來生成代理方式有 JDKProxy 和 CGLib。下面我們來研究一下 Spring 如何使用 JDK 來生成代理對象,具體的生成代碼放在 JdkDynamicAopProxy 這個類中,
直接上相關代碼:
@Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
通過注釋我們應該已經看得非常明白代理對象的生成過程,此處不再贅述。下面的問題是,代理對象生成了,那切面是如何織入的?我們知道 InvocationHandler 是 JDK 動態代理的核心,生成的代理對象的方法調用都會委托到InvocationHandler.invoke()方法。而從 JdkDynamicAopProxy 的源碼我們可以看到這個類其實也實現了 InvocationHandler,下面我們分析 Spring AOP 是如何織入切面的,直接上源碼看 invoke()方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { //eqauls()方法,具目標對象未實現此方法 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } //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); } //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; } // Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. //獲得目標對象的類 target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // Get the interception chain for this method. //獲取可以應用到此方法上的Interceptor列表 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. //如果沒有可以應用到此方法的通知(Interceptor),此直接反射調用 method.invoke(target, args) 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); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... //創建MethodInvocation 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 != Object.class && 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); } } }
主要實現思路可以簡述為:首先獲取應用到此方法上的通知鏈(Interceptor Chain)。如果有通知,應用通知,並執行 JoinPoint;如果沒有通知,則直接反射執行 JoinPoint。而這里的關鍵是通知鏈是如
何獲取的以及它又是如何執行的呢?現在來逐一分析。首先,從上面的代碼可以看到,Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取的,的實現邏輯:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); List<Object> cached = this.methodCache.get(cacheKey); if (cached == null) { cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass); this.methodCache.put(cacheKey, cached); } return cached; }
這個方法執行完成后,Advised 中配置能夠應用到連接點(JoinPoint)或者目標類(Target Object)的 Advisor 全部被轉化成了 MethodInterceptor,接下來我們再看下得到的攔截器鏈是怎么起作用的
//如果沒有可以應用到此方法的通知(Interceptor),此直接反射調用 method.invoke(target, args) 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); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... //創建MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }
從這 段 代 碼 可 以 看 出 , 如 果 得 到 的 攔 截 器 鏈 為 空 , 則 直 接 反 射 調 用 目 標 方 法 , 否 則 創 建MethodInvocation,調用其 proceed()方法,觸發攔截器鏈的執行,來看下具體代碼:
public Object proceed() throws Throwable { // We start with an index of -1 and increment early. //如果Interceptor執行完了,則執行joinPoint if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //如果要動態匹配joinPoint if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; //動態匹配:運行時參數是否滿足匹配條件 if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. //動態匹配失敗時,略過當前Intercetpor,調用下一個Interceptor return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. //執行當前Intercetpor return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
至此,通知鏈就完美地形成了。我們再往下來看 invokeJoinpointUsingReflection()方法,其實就是反射調用:
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args) throws Throwable { // Use reflection to invoke the method. try { ReflectionUtils.makeAccessible(method); return method.invoke(target, args); } catch (InvocationTargetException ex) { // Invoked method threw a checked exception. // We must rethrow it. The client won't see the interceptor. throw ex.getTargetException(); } catch (IllegalArgumentException ex) { throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" + method + "] on target [" + target + "]", ex); } catch (IllegalAccessException ex) { throw new AopInvocationException("Could not access method [" + method + "]", ex); } }
Spring AOP運行時序圖