springAOP 設計原理與具體實踐
AOP是OOP的延續,是Aspect Oriented Programming的縮寫,意思是面向切面程式設計。可以通過預編譯方式和執行期動態代理實現在不修改原始碼的情況下給程式動態統一新增功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是呼叫者和被呼叫者之間的解耦,AOP可以說也是這種目標的一種實現。
我們現在做的一些非業務,如:日誌、事務、安全等都會寫在業務程式碼中(也即是說,這些非業務類橫切於業務類),但這些程式碼往往是重複,複製——貼上式的程式碼會給程式的維護帶來不便,AOP就實現了把這些業務需求與系統需求分開來做。這種解決的方式也稱代理機制。
先來了解一下AOP的相關概念:
- 切面(Aspect):官方的抽象定義為“一個關注點的模組化,這個關注點可能會橫切多個物件”,在本例中,“切面”就是類TestAspect所關注的具體行為,例如,AServiceImpl.barA()的呼叫就是切面TestAspect所關注的行為之一。“切面”在ApplicationContext中<aop:aspect>來配置。
- 連線點(Joinpoint) :程式執行過程中的某一行為,例如,UserService.get的呼叫或者UserService.delete丟擲異常等行為。
- 通知(Advice) :“切面”對於某個“連線點”所產生的動作,例如,TestAspect中對com.spring.service包下所有類的方法進行日誌記錄的動作就是一個Advice。其中,一個“切面”可以包含多個“Advice”,例如ServiceAspect。
- 切入點(Pointcut) :匹配連線點的斷言,在AOP中通知和一個切入點表示式關聯。例如,TestAspect中的所有通知所關注的連線點,都由切入點表示式execution(* com.spring.service.*.*(..))來決定。
- 目標物件(Target Object) :被一個或者多個切面所通知的物件。例如,AServcieImpl和BServiceImpl,當然在實際執行時,Spring AOP採用代理實現,實際AOP操作的是TargetObject的代理物件。
- AOP代理(AOP Proxy) :在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。預設情況下,TargetObject實現了介面時,則採用JDK動態代理,例如,AServiceImpl;反之,採用CGLIB代理,例如,BServiceImpl。強制使用CGLIB代理需要將 <aop:config>的 proxy-target-class屬性設為true。
通知(Advice)型別:
- 前置通知(Before advice):在某連線點(JoinPoint)之前執行的通知,但這個通知不能阻止連線點前的執行。ApplicationContext中在<aop:aspect>裡面使用<aop:before>元素進行宣告。例如,TestAspect中的doBefore方法。
- 後置通知(After advice):當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>裡面使用<aop:after>元素進行宣告。例如,ServiceAspect中的returnAfter方法,所以Teser中呼叫UserService.delete丟擲異常時,returnAfter方法仍然執行。
- 返回後通知(After return advice):在某連線點正常完成後執行的通知,不包括丟擲異常的情況。ApplicationContext中在<aop:aspect>裡面使用<after-returning>元素進行宣告。
- 環繞通知(Around advice):包圍一個連線點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的呼叫前後完成自定義的行為,也可以選擇不執行。ApplicationContext中在<aop:aspect>裡面使用<aop:around>元素進行宣告。例如,ServiceAspect中的around方法。
- 丟擲異常後通知(After throwing advice):在方法丟擲異常退出時執行的通知。ApplicationContext中在<aop:aspect>裡面使用<aop:after-throwing>元素進行宣告。例如,ServiceAspect中的returnThrow方法。
注:可以將多個通知應用到一個目標物件上,即可以將多個切面織入到同一目標物件。
使用Spring AOP可以基於兩種方式,一種是比較方便和強大的註解方式,另一種則是中規中矩的xml配置方式。
先說註解,使用註解配置Spring AOP總體分為兩步,第一步是在xml檔案中宣告啟用自動掃描元件功能,同時啟用自動代理功能(來測試AOP功能)
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
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-3.0.xsd">
<!-- 啟用元件掃描功能,在包cn.ysh.studio.spring.aop及其子包下面自動掃描通過註解配置的元件 -->
<context:component-scan base-package="com.study.spring.aop"/>
<!-- 啟用自動代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
第二步是為Aspect切面類添加註解:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 系統服務元件Aspect切面Bean
* @author zjx
*/
//宣告這是一個元件
@Component
//宣告這是一個切面Bean
@Aspect
public class ServiceAspect {
private final static Log log = LogFactory.getLog(ServiceAspect.class);
//配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點
@Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
public void aspect(){ }
/*
* 配置前置通知,使用在方法aspect()上註冊的切入點
* 同時接受JoinPoint切入點物件,可以沒有該引數
*/
@Before("aspect()")
public void before(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("before " + joinPoint);
}
}
//配置後置通知,使用在方法aspect()上註冊的切入點
@After("aspect()")
public void after(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("after " + joinPoint);
}
}
//配置環繞通知,使用在方法aspect()上註冊的切入點
@Around("aspect()")
public void around(JoinPoint joinPoint){
long start = System.currentTimeMillis();
try {
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
if(log.isInfoEnabled()){
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
}
} catch (Throwable e) {
long end = System.currentTimeMillis();
if(log.isInfoEnabled()){
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
}
//配置後置返回通知,使用在方法aspect()上註冊的切入點
@AfterReturning("aspect()")
public void afterReturn(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("afterReturn " + joinPoint);
}
}
//配置丟擲異常後通知,使用在方法aspect()上註冊的切入點
@AfterThrowing(pointcut="aspect()", throwing="ex")
public void afterThrow(JoinPoint joinPoint, Exception ex){
if(log.isInfoEnabled()){
log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
}
}
}
測試程式碼
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.gupaoedu.vip.aop.service.MemberManagerService;
@ContextConfiguration(locations={"classpath*:application-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class MemberManagerServiceTest {
@Autowired MemberManagerService memberManagerService;
@Test
// @Ignore
public void testAdd(){
memberManagerService.add(null);
}
@Test
@Ignore
public void testRemove(){
try {
memberManagerService.remove(0);
} catch (Exception e) {
// e.printStackTrace();
}
}
public void testModify(){
memberManagerService.modify(null);
}
public void testQuery(){
memberManagerService.query("");
}
}
可以看到,正如我們預期的那樣,雖然我們並沒有對UserSerivce類包括其呼叫方式做任何改變,但是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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
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-3.0.xsd">
<!-- 系統服務元件的切面Bean -->
<bean id="serviceAspect" class="cn.zjx.study.spring.aop.aspect.ServiceAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 宣告一個切面,並注入切面Bean,相當於@Aspect -->
<aop:aspect id="simpleAspect" ref="serviceAspect">
<!-- 配置一個切入點,相當於@Pointcut -->
<aop:pointcut expression="execution(* cn.ysh.studio.spring.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:aspect>
</aop:config>
</beans>
個人覺得不如註解靈活和強大,你可以不同意這個觀點,但是不知道如下的程式碼會不會讓你的想法有所改善:
//配置前置通知,攔截返回值為cn.ysh.studio.spring.mvc.bean.User的方法
@Before("execution(com.zjx.study.spring.mvc.bean.User cn.ysh.studio.spring.aop.service..*(..))")
public void beforeReturnUser(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("beforeReturnUser " + joinPoint);
}
}
//配置前置通知,攔截引數為cn.ysh.studio.spring.mvc.bean.User的方法
@Before("execution(* com.zjx.study.spring.aop.service..*(cn.ysh.studio.spring.mvc.bean.User))")
public void beforeArgUser(JoinPoint joinPoint){
if(log.isInfoEnabled()){
log.info("beforeArgUser " + joinPoint);
}
}
//配置前置通知,攔截含有long型別引數的方法,並將引數值注入到當前方法的形參id中
@Before("aspect()&&args(id)")
public void beforeArgId(JoinPoint joinPoint, long id){
if(log.isInfoEnabled()){
log.info("beforeArgId " + joinPoint + "\tID:" + id);
}
}
附上UserService的程式碼:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 使用者服務模型
*/
public class UserService {
private final static Log log = LogFactory.getLog(UserService.class);
public User get(long id){
if(log.isInfoEnabled()){
log.info("getUser method . . .");
}
return new User();
}
public void save(User user){
if(log.isInfoEnabled()){
log.info("saveUser method . . .");
}
}
public boolean delete(long id) throws Exception{
if(log.isInfoEnabled()){
log.info("delete method . . .");
throw new Exception("spring aop ThrowAdvice演示");
}
return false;
}
}
應該說學習Spring AOP有兩個難點,第一點在於理解AOP的理念和相關概念,第二點在於靈活掌握和使用切入點表示式。概念的理解通常不在一朝一夕,慢慢浸泡的時間長了,自然就明白了,下面我們簡單地介紹一下切入點表示式的配置規則吧。
通常情況下,表示式中使用”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包下,返回值為任意型別;方法名任意;引數不作限制的所有方法。
例如定義切入點表示式 execution (* com.sample.service.impl..*.*(..))
execution()是最常用的切點函式,其語法如下所示:
整個表示式可以分為五個部分:
1、execution(): 表示式主體。
2、第一個*號:表示返回型別,*號表示所有的型別。
3、包名:表示需要攔截的包名,後面的兩個句點表示當前包和當前包的所有子包,com.sample.service.impl包、子孫包下所有類的方法。
4、第二個*號:表示類名,*號表示所有的類。
5、*(..):最後這個星號表示方法名,*號表示所有的方法,後面括弧裡面表示方法的引數,兩個句點表示任何引數。
<aop:config>
<aop:aspect id="TestAspect" ref="aspectBean">
<aop:pointcut id="businessService"
expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />
<aop:after pointcut-ref="businessService" method="doAfter"/>
</aop:aspect>
</aop:config>
上面的程式碼args(msg,..)是指將切入點方法上的第一個String型別引數新增到引數名為msg的通知的入參上,這樣就可以直接使用該引數啦。
訪問當前的連線點
在上面的Aspect切面Bean中已經看到了,每個通知方法第一個引數都是JoinPoint。其實,在Spring中,任何通知(Advice)方法都可以將第一個引數定義為 org.aspectj.lang.JoinPoint型別用以接受當前連線點物件。JoinPoint介面提供了一系列有用的方法, 比如 getArgs() (返回方法引數)、getThis() (返回代理物件)、getTarget() (返回目標)、getSignature() (返回正在被通知的方法相關資訊)和 toString() (打印出正在被通知的方法的有用資訊)。
AOP到底能幹嘛?
Authentication 許可權
caching 快取
Debugging 除錯
Logging 日誌
Transaction Manager 事務管理
Mointer 監聽
Inteceptor 攔截器
Context Passing 內容傳遞
監聽的快取結果,第一次呼叫的時候,就把結果存到快取中的,那麼第二次呼叫,就不再呼叫哪個實際的方法了,直接呼叫before就返回去了。
SpringAOP設計原理及原始碼分析
開始之前上線,看看spring中主要的aop元件
Spring提供了兩種方式來生成代理物件: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據AdvisedSupport物件的配置來決定。預設的策略是如果目標類是介面,則使用JDK動態代理技術,否則使用Cglib來生成代理。下面我們來研究一下Spring如何使用JDK來生成代理物件,具體的生成程式碼放在JdkDynamicAopProxy這個類中,直接上相關程式碼:
/**
* <ol>
* <li>獲取代理類要實現的介面,除了Advised物件中配置的,還會加上SpringProxy, Advised(opaque=false)
* <li>檢查上面得到的介面中有沒有定義 equals或者hashcode的介面
* <li>呼叫Proxy.newProxyInstance建立代理物件
* </ol>
*/
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());
}
Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
那這個其實很明瞭,註釋上我也已經寫清楚了,不再贅述。
下面的問題是,代理物件生成了,那切面是如何織入的?
我們知道InvocationHandler是JDK動態代理的核心,生成的代理物件的方法呼叫都會委託到InvocationHandler.invoke()方法。而通過JdkDynamicAopProxy的簽名我們可以看到這個類其實也實現了InvocationHandler,下面我們就通過分析這個類中實現的invoke()方法來具體看下Spring AOP是如何織入切面的。
publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {
MethodInvocation invocation = null;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class targetClass = null;
Object target = null;
try {
//eqauls()方法,具目標物件未實現此方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){
return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);
}
//hashCode()方法,具目標物件未實現此方法
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){
return newInteger(hashCode());
}
//Advised介面或者其父介面中定義的方法,直接反射呼叫,不應用通知
if (!this.advised.opaque &&method.getDeclaringClass().isInterface()
&&method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations onProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);
}
Object retVal = null;
if (this.advised.exposeProxy) {
// Make invocation available ifnecessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//獲得目標物件的類
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
//獲取可以應用到此方法上的Interceptor列表
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
//如果沒有可以應用到此方法的通知(Interceptor),此直接反射呼叫 method.invoke(target, args)
if (chain.isEmpty()) {
retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
} else {
//建立MethodInvocation
invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// Massage return value if necessary.
if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)
&&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned"this" and the return type of the method
// is type-compatible. Notethat we can't help if the target sets
// a reference to itself inanother returned object.
retVal = proxy;
}
return retVal;
} finally {
if (target != null && !targetSource.isStatic()) {
// Must have come fromTargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
主流程可以簡述為:獲取可以應用到此方法上的通知鏈(Interceptor Chain),如果有,則應用通知,並執行joinpoint; 如果沒有,則直接反射執行joinpoint。而這裡的關鍵是通知鏈是如何獲取的以及它又是如何執行的,下面逐一分析下。
首先,從上面的程式碼可以看到,通知鏈是通過Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取的,我們來看下這個方法的實現:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
MethodCacheKeycacheKey = 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);
}
returncached;
}
可以看到實際的獲取工作其實是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()這個方法來完成的,獲取到的結果會被快取。
下面來分析下這個方法的實現:
/**
* 從提供的配置例項config中獲取advisor列表,遍歷處理這些advisor.如果是IntroductionAdvisor,
* 則判斷此Advisor能否應用到目標類targetClass上.如果是PointcutAdvisor,則判斷
* 此Advisor能否應用到目標方法method上.將滿足條件的Advisor通過AdvisorAdaptor轉化成Interceptor列表返回.
*/
publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {
// This is somewhat tricky... we have to process introductions first,
// but we need to preserve order in the ultimate list.
List interceptorList = new ArrayList(config.getAdvisors().length);
//檢視是否包含IntroductionAdvisor
boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);
//這裡實際上註冊一系列AdvisorAdapter,用於將Advisor轉化成MethodInterceptor
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
for (int i = 0; i <advisors.length; i++) {
Advisor advisor = advisors[i];
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;
if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
//TODO: 這個地方這兩個方法的位置可以互換下
//將Advisor轉化成Interceptor
MethodInterceptor[]interceptors = registry.getInterceptors(advisor);
//檢查當前advisor的pointcut是否可以匹配當前方法
MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {
if(mm.isRuntime()) {
// Creating a newobject instance in the getInterceptors() method
// isn't a problemas we normally cache created chains.
for (intj = 0; j < interceptors.length; j++) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));
}
} else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
} else if (advisor instanceof IntroductionAdvisor){
IntroductionAdvisor ia =(IntroductionAdvisor) advisor;
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;
}
這個方法執行完成後,Advised中配置能夠應用到連線點或者目標類的Advisor全部被轉化成了MethodInterceptor.
接下來我們再看下得到的攔截器鏈是怎麼起作用的。
if (chain.isEmpty()) {
retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
} else {
//建立MethodInvocation
invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
從這段程式碼可以看出,如果得到的攔截器鏈為空,則直接反射呼叫目標方法,否則建立MethodInvocation,呼叫其proceed方法,觸發攔截器鏈的執行,來看下具體程式碼
public Object proceed() throws Throwable {
// We start with an index of -1and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {
//如果Interceptor執行完了,則執行joinPoint
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)) {
//執行當前Intercetpor
returndm.interceptor.invoke(this);
}
else {
//動態匹配失敗時,略過當前Intercetpor,呼叫下一個Interceptor
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcutwill have
// been evaluated statically before this object was constructed.
//執行當前Intercetpor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}