之前研究了AOP代理對象的創建過程以及注入到Spring的過程,接下來研究AOP的調用過程。
0. 代碼同上一節的測試代碼
1. pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.qz</groupId> <artifactId>springlearn</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <name>springlearn</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 引入 spring aop 依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> </build> </project>
2. Aop相關配置類: 這里使用兩種方式實現AOP
(1) @Aspect 相關
package cn.qz.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Arrays; @Aspect // 表示該類是一個通知類 @Component // 交給spring管理 public class MyAdvice { // 定義一個空方法,借用其注解抽取切點表達式 @Pointcut("execution(* cn.qz..*.*(..))") public void pc() { } // 前置通知 @Before("MyAdvice.pc()") public void before(JoinPoint joinPoint) throws Exception { System.out.println("---------------前置通知開始~~~~~~~~~~~"); // 獲取到類名 String targetName = joinPoint.getTarget().getClass().getName(); System.out.println("代理的類是:" + targetName); // 獲取到方法名 String methodName = joinPoint.getSignature().getName(); System.out.println("增強的方法是:" + methodName); // 獲取到參數 Object[] parameter = joinPoint.getArgs(); System.out.println("傳入的參數是:" + Arrays.toString(parameter)); // 獲取字節碼對象 Class<?> targetClass = Class.forName(targetName); // 獲取所有的方法 Method[] methods = targetClass.getMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == parameter.length) { System.out.println("找到這個方法"); //處理一些業務邏輯 break; } } } System.out.println("---------------前置通知結束~~~~~~~~~~~"); } // 后置通知(異常發生后不會調用) @AfterReturning("MyAdvice.pc()") public void afterRunning() { System.out.println("這是后置通知(異常發生后不會調用)"); } // 環繞通知(推薦下面這種方式獲取方法) @Around("MyAdvice.pc()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("----------------環繞通知之前 的部分----------------"); // 獲取到類名 String targetName = pjp.getTarget().getClass().getName(); System.out.println("代理的類是:" + targetName); // 獲取到參數 Object[] parameter = pjp.getArgs(); System.out.println("傳入的參數是:" + Arrays.toString(parameter)); // 獲取到方法簽名,進而獲得方法 MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); System.out.println("增強的方法名字是:" + method.getName()); //處理一些業務邏輯 // 獲取參數類型 Class<?>[] parameterTypes = method.getParameterTypes(); System.out.println("參數類型是:" + parameterTypes.toString()); //讓方法執行(proceed是方法的返回結果,可以針對返回結果處理一下事情) System.out.println("--------------方法開始執行-----------------"); Object proceed = pjp.proceed(); //環繞通知之后的業務邏輯部分 System.out.println("----------------環繞通知之后的部分----------------"); return proceed; } // 異常通知 @AfterThrowing("MyAdvice.pc()") public void afterException() { System.out.println("這是異常通知(發生異常后調用)~~~~~~~~~~~"); } // 最終通知(發生異常也會在最終調用) @After("MyAdvice.pc()") public void after() { System.out.println("這是后置通知(發生異常也會在最終調用)"); } }
(2) 基於類繼承實現AOP
增強類:
package cn.qz.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(invocation.getMethod() + "==方法執行前=="); Object proceed = invocation.proceed(); System.out.println(invocation.getArguments() + "--方法執行后--"); return proceed; } }
切點:
package cn.qz.aop; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import java.lang.reflect.Method; public class MyPoint implements Pointcut { @Override public ClassFilter getClassFilter() { return new ClassFilter() { @Override public boolean matches(Class<?> clazz) { if (clazz != null && clazz.getName().contains("cn.qz")) { return true; } return false; } }; } @Override public MethodMatcher getMethodMatcher() { return new MethodMatcher() { /** * 判斷方法是否匹配 */ @Override public boolean matches(Method method, Class<?> targetClass, Object... args) { return "test".equals(method.getName()); } /** * 判斷方法是否匹配 */ @Override public boolean matches(Method method, Class<?> targetClass) { return "test".equals(method.getName()); } @Override public boolean isRuntime() { return false; } }; } }
切面:
package cn.qz.aop; import org.aopalliance.aop.Advice; import org.springframework.aop.Pointcut; import org.springframework.aop.PointcutAdvisor; import org.springframework.stereotype.Component; @Component public class MyAdvicer implements PointcutAdvisor { @Override public Advice getAdvice() { return new MyInterceptor(); } @Override public boolean isPerInstance() { return true; } @Override public Pointcut getPointcut() { return new MyPoint(); } }
3. 測試類:
DeptDao
package cn.qz.user; import org.springframework.stereotype.Component; @Component public class DeptDao { public void test() { System.out.println("test"); } }
UserDao接口:
package cn.qz.user; public interface UserDao { void addUser(); }
UserDaoImpl實現類:
package cn.qz.user; import org.springframework.stereotype.Service; @Service public class UserDaoImpl implements UserDao{ @Override public void addUser() { System.out.println("addUser"); } }
1. 研究Cglib代理的調用過程
這里以DeptDao類為例研究其調用過程,其沒有實現接口默認會用Cglib代理。
package cn.qz; import cn.qz.user.DeptDao; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; @ComponentScan @EnableAspectJAutoProxy public class App { public static void main(String[] args) { //在指定目錄下生成動態代理類,我們可以反編譯看一下里面到底是一些什么東西 System.setProperty("cglib.debugLocation", "G:/proxy"); AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class); DeptDao bean = applicationContext.getBean(DeptDao.class); bean.test(); } }
打印日志如下:
public void cn.qz.user.DeptDao.test()==方法執行前== ----------------環繞通知之前 的部分---------------- 代理的類是:cn.qz.user.DeptDao 傳入的參數是:[] 增強的方法名字是:test 參數類型是:[Ljava.lang.Class;@33990a0c --------------方法開始執行----------------- ---------------前置通知開始~~~~~~~~~~~ 代理的類是:cn.qz.user.DeptDao 增強的方法是:test 傳入的參數是:[] 找到這個方法 ---------------前置通知結束~~~~~~~~~~~ test 這是后置通知(異常發生后不會調用) 這是后置通知(發生異常也會在最終調用) ----------------環繞通知之后的部分---------------- [Ljava.lang.Object;@429bffaa--方法執行后--
接下來研究其AOP調用過程。
1. 打斷點查看applicationContext.getBean(DeptDao.class); 的返回類的信息
2. 反編譯查看代理類信息,主要查看test方法(可以通過arthas查看,也可以通到導出的代理類查看)
public final void test() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy); } else { super.test(); } }
可以看到核心的方法是獲取到內部屬性CGLIB$CALLBACK_0, 然后調用其intercept 方法。
3. 斷點打到org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
源碼如下:
@Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { 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); List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // 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 = methodProxy.invoke(target, argsToUse); } else { // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
4. 獲取到的chain 鏈如下:
查看其獲取方式:
1》org.springframework.aop.framework.AdvisedSupport#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; }
2 》 org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice
@Override public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class<?> targetClass) { // This is somewhat tricky... We have to process introductions first, // but we need to preserve order in the ultimate list. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); Advisor[] advisors = config.getAdvisors(); List<Object> interceptorList = new ArrayList<>(advisors.length); Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); Boolean hasIntroductions = null; for (Advisor advisor : advisors) { if (advisor instanceof PointcutAdvisor) { // Add it conditionally. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); boolean match; if (mm instanceof IntroductionAwareMethodMatcher) { if (hasIntroductions == null) { hasIntroductions = hasMatchingIntroductions(advisors, actualClass); } match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions); } else { match = mm.matches(method, actualClass); } if (match) { MethodInterceptor[] interceptors = registry.getInterceptors(advisor); 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)); } } return interceptorList; }
斷點查看信息:
2.1》registry如下:
2.2》 advisors如下:
2.3》MethodInterceptor[] interceptors = registry.getInterceptors(advisor); 會根據advisor處理到對應的MethodInteceptor
org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List<MethodInterceptor> interceptors = new ArrayList<>(3); Advice advice = advisor.getAdvice(); if (advice instanceof MethodInterceptor) { interceptors.add((MethodInterceptor) advice); } 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[0]); }
adapters如下:
判斷是否滿足是以是否是某個類型來判斷,比如:org.springframework.aop.framework.adapter.MethodBeforeAdviceAdapter#supportsAdvice
public boolean supportsAdvice(Advice advice) { return (advice instanceof MethodBeforeAdvice); }
可以看到是獲取到Advisor里面的Advise(MethodInterceptor)
2.4》經過處理后返回的interceptorList 如下:
5. 接下來就是用獲取到的chain來進行調用 retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed
public Object proceed() throws Throwable { try { return super.proceed(); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) { throw ex; } else { throw new UndeclaredThrowableException(ex); } } }
調用到父類也就是調用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
@Override @Nullable public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
代碼解釋:
(1) 下面代碼先判斷是否是最后最后一個inteceptor,currentInterceptorIndex 一開始是-1。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); }
(2) 下面代碼獲取到鏈條中的inteceptor進行調用
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
interceptorsAndDynamicMethodMatchers 就是之前獲取到的chain,如下:
(3) 接下來走((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);,第一次會到org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke 方法
@Override public Object invoke(MethodInvocation mi) throws Throwable { MethodInvocation oldInvocation = invocation.get(); invocation.set(mi); try { return mi.proceed(); } finally { invocation.set(oldInvocation); } }
可以看到這里面沒做深惡業務處理然后調用到mi.proceed(), mi 也就是上面的ReflectiveMethodInvocation 對象(這樣可以實現責任鏈模式)
(4) 接下來繼續到org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 方法,當前的advisor下標+1並獲取到advisor,也就是MyInteceptor
接下來調用到cn.qz.aop.MyInterceptor#invoke,如下:
public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(invocation.getMethod() + "==方法執行前=="); Object proceed = invocation.proceed(); System.out.println(invocation.getArguments() + "--方法執行后--"); return proceed; }
(5) 打印完信息之后繼續調用invocation.proceed() 方法,也就是遞歸調用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed, 繼續調用下一個advisor的方法,也就是
接下來調用其invoke方法:org.springframework.aop.aspectj.AspectJAroundAdvice#invoke
public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); }
接下來調用到:org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { Object[] actualArgs = args; if (this.aspectJAdviceMethod.getParameterCount() == 0) { actualArgs = null; } try { ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); // TODO AopUtils.invokeJoinpointUsingReflection return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); } catch (IllegalArgumentException ex) { throw new AopInvocationException("Mismatch on arguments to advice method [" + this.aspectJAdviceMethod + "]; pointcut expression [" + this.pointcut.getPointcutExpression() + "]", ex); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }
aspectJAdviceMethod 方法也就是我們的切面方法。如下:
接下來通過方法的invoke調用到cn.qz.aop.MyAdvice#around
public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("----------------環繞通知之前 的部分----------------"); // 獲取到類名 String targetName = pjp.getTarget().getClass().getName(); System.out.println("代理的類是:" + targetName); // 獲取到參數 Object[] parameter = pjp.getArgs(); System.out.println("傳入的參數是:" + Arrays.toString(parameter)); // 獲取到方法簽名,進而獲得方法 MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); System.out.println("增強的方法名字是:" + method.getName()); //處理一些業務邏輯 // 獲取參數類型 Class<?>[] parameterTypes = method.getParameterTypes(); System.out.println("參數類型是:" + parameterTypes.toString()); //讓方法執行(proceed是方法的返回結果,可以針對返回結果處理一下事情) System.out.println("--------------方法開始執行-----------------"); Object proceed = pjp.proceed(); //環繞通知之后的業務邏輯部分 System.out.println("----------------環繞通知之后的部分----------------"); return proceed; }
pjp如下:
pjp.proceed() 調用到:org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint#proceed(),源碼如下:
@Override public Object proceed() throws Throwable { return this.methodInvocation.invocableClone().proceed(); }
this.methodInvocation.invocableClone() 也就是上面的ReflectiveMethodInvocation對象:
(6) 接下來繼續返回到org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 處理下一個advisor
接下來調用到:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor#invoke
public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); }
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());調用到org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { Object[] actualArgs = args; if (this.aspectJAdviceMethod.getParameterCount() == 0) { actualArgs = null; } try { ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); // TODO AopUtils.invokeJoinpointUsingReflection return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); } catch (IllegalArgumentException ex) { throw new AopInvocationException("Mismatch on arguments to advice method [" + this.aspectJAdviceMethod + "]; pointcut expression [" + this.pointcut.getPointcutExpression() + "]", ex); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }
這里會調到cn.qz.aop.MyAdvice#before。
調用完之后繼續下一個調用鏈的執行。
(7) 加下來下標為4的advisor開始執行
調用 org.springframework.aop.aspectj.AspectJAfterAdvice#invoke
public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } finally { invokeAdviceMethod(getJoinPointMatch(), null, null); } }
這個方法先調用 調用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed處理下一個,也就是先調用進行下面的(8)操作。
然后返回之前finally調用 invokeAdviceMethod 調用我們定義的方法,和上面一個套路,調用到org.springframework.aop.aspectj.AbstractAspectJAdvice#invokeAdviceMethodWithGivenArgs,然后調到自定義的方法
(8) org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 處理下標為5的調用鏈
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor#invoke
@Override public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
這里和上面套路一樣,先調用下一個調用鏈,然后調用自定義的方法
(9) org.springframework.aop.framework.ReflectiveMethodInvocation#proceed 處理下標為6的調用鏈
調用到: org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke
public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } }
可以看到先調用下一個advisor發生異常后調用自己的異常通知
(10) 接下來繼續調用org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
這時候沒有可用的調用器鏈,則會調用invokeJoinpoint 方法,會調到: org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint
@Override protected Object invokeJoinpoint() throws Throwable { if (this.methodProxy != null) { return this.methodProxy.invoke(this.target, this.arguments); } else { return super.invokeJoinpoint(); } }
接着調用org.springframework.cglib.proxy.MethodProxy#invoke
public Object invoke(Object obj, Object[] args) throws Throwable { try { init(); FastClassInfo fci = fastClassInfo; return fci.f1.invoke(fci.i1, obj, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } catch (IllegalArgumentException ex) { if (fastClassInfo.i1 < 0) throw new IllegalArgumentException("Protected method: " + sig1); throw ex; } }
fci.f1是下面對象:
實際里面就是反射調用對應的方法了。
(11) 反射調用完方法之后會一直回調剛才未調用的方法以及環繞通知后半部分的邏輯等過程。
補充:再增加一個切面查看其advisor通知鏈
package cn.qz.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Arrays; @Aspect // 表示該類是一個通知類 @Component // 交給spring管理 public class MyAdvice2 { // 定義一個空方法,借用其注解抽取切點表達式 @Pointcut("execution(* cn.qz..*.*(..))") public void pc() { } // 前置通知 @Before("cn.qz.aop.MyAdvice2.pc()") public void before(JoinPoint joinPoint) throws Exception { System.out.println("---------------前置通知開始~~~~~~~~~~~"); // 獲取到類名 String targetName = joinPoint.getTarget().getClass().getName(); System.out.println("代理的類是:" + targetName); // 獲取到方法名 String methodName = joinPoint.getSignature().getName(); System.out.println("增強的方法是:" + methodName); // 獲取到參數 Object[] parameter = joinPoint.getArgs(); System.out.println("傳入的參數是:" + Arrays.toString(parameter)); // 獲取字節碼對象 Class<?> targetClass = Class.forName(targetName); // 獲取所有的方法 Method[] methods = targetClass.getMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == parameter.length) { System.out.println("找到這個方法"); //處理一些業務邏輯 break; } } } System.out.println("---------------前置通知結束~~~~~~~~~~~"); } // 后置通知(異常發生后不會調用) @AfterReturning("cn.qz.aop.MyAdvice2.pc()") public void afterRunning() { System.out.println("這是后置通知(異常發生后不會調用)"); } // 環繞通知(推薦下面這種方式獲取方法) @Around("cn.qz.aop.MyAdvice2.pc()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("----------------環繞通知之前 的部分----------------"); // 獲取到類名 String targetName = pjp.getTarget().getClass().getName(); System.out.println("代理的類是:" + targetName); // 獲取到參數 Object[] parameter = pjp.getArgs(); System.out.println("傳入的參數是:" + Arrays.toString(parameter)); // 獲取到方法簽名,進而獲得方法 MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); System.out.println("增強的方法名字是:" + method.getName()); //處理一些業務邏輯 // 獲取參數類型 Class<?>[] parameterTypes = method.getParameterTypes(); System.out.println("參數類型是:" + parameterTypes.toString()); //讓方法執行(proceed是方法的返回結果,可以針對返回結果處理一下事情) System.out.println("--------------方法開始執行-----------------"); Object proceed = pjp.proceed(); //環繞通知之后的業務邏輯部分 System.out.println("----------------環繞通知之后的部分----------------"); return proceed; } // 異常通知 @AfterThrowing("cn.qz.aop.MyAdvice2.pc()") public void afterException() { System.out.println("這是異常通知(發生異常后調用)~~~~~~~~~~~"); } // 最終通知(發生異常也會在最終調用) @After("cn.qz.aop.MyAdvice2.pc()") public void after() { System.out.println("這是后置通知(發生異常也會在最終調用)"); } }
查看調用鏈:chain
打印的日志如下:
public void cn.qz.user.DeptDao.test()==方法執行前== ----------------環繞通知之前 的部分---------------- 代理的類是:cn.qz.user.DeptDao 傳入的參數是:[] 增強的方法名字是:test 參數類型是:[Ljava.lang.Class;@1755e85b --------------方法開始執行----------------- ---------------前置通知開始~~~~~~~~~~~ 代理的類是:cn.qz.user.DeptDao 增強的方法是:test 傳入的參數是:[] 找到這個方法 ---------------前置通知結束~~~~~~~~~~~ ----------------環繞通知之前 的部分---------------- 代理的類是:cn.qz.user.DeptDao 傳入的參數是:[] 增強的方法名字是:test 參數類型是:[Ljava.lang.Class;@736d6a5c --------------方法開始執行----------------- ---------------前置通知開始~~~~~~~~~~~ 代理的類是:cn.qz.user.DeptDao 增強的方法是:test 傳入的參數是:[] 找到這個方法 ---------------前置通知結束~~~~~~~~~~~ test 這是后置通知(異常發生后不會調用) 這是后置通知(發生異常也會在最終調用) ----------------環繞通知之后的部分---------------- 這是后置通知(異常發生后不會調用) 這是后置通知(發生異常也會在最終調用) ----------------環繞通知之后的部分---------------- [Ljava.lang.Object;@7ce97ee5--方法執行后--
2. JDK代理調用規則
這個查看其生成時的InvocationHandler類如下:org.springframework.aop.framework.JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } 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; } // 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. 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. 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); } } }
可以看到其調用過程最終也會調到org.springframework.aop.framework.ReflectiveMethodInvocation#proceed:(調用過程和上面一樣使用責任鏈模式進行調用)
public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
補充:調用過程圖表展示,以一開始的調用過程為例
程序的入口是:org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept。首先調用getInterceptorsAndDynamicInterceptionAdvice獲取到 inteceptors 鏈條(chain), 然后進行鏈條的調用,大致過程如下
可以看到調用目標方法是在chain鏈條調完之后才調用的,之所以能實現后置、環繞等的效果,是因為方法的遞歸調用;調用完目標方法之后會處理通知中沒有處理的代碼邏輯。也就是實現了整個鏈式效果。chian相當於個調度器,在調度整個鏈條的處理。
補充: 一般也不會都用到這五種通知,一般一個Around 通知就夠用了,比如Spring的事務
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
public Object invoke(MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
接着調用org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. TransactionAttributeSource tas = getTransactionAttributeSource(); final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); final PlatformTransactionManager tm = determineTransactionManager(txAttr); final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } else { Object result; final ThrowableHolder throwableHolder = new ThrowableHolder(); // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); try { return invocation.proceedWithInvocation(); } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw new ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; return null; } } finally { cleanupTransactionInfo(txInfo); } }); } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } // Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } }
可以看到核心的邏輯是自己操作的,前面先准備了事務相關信息,然后try(操作)、catch(事務回滾)、finally(清除事務相關資源) 進行操作
補充:AOP自調用無效
AOP在方法內部進行調用是無效的。解決辦法有兩種:
1. 用BeanFactory.getBean 重新獲取bean, 或者在當前類注入自身。調用的時候不通過this 調用,通過spring容器中的對象調用。
2. 通過AopContext.currentProxy(); 獲取代理對象,其實原理和getBean 一樣,只是代理不會調Spring,而是AOP在處理過程中緩存到ThreadLocal的對象。
(1) 主應用允許暴露代理
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
(2) 查看AOP處理過程如下:
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept AOP調用入口
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); Object var16; try { if (this.advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } target = targetSource.getTarget(); Class<?> targetClass = target != null ? target.getClass() : null; List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed(); } retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal); var16 = retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } return var16; }
oldProxy = AopContext.setCurrentProxy(proxy); 這里緩存了代理對象。
(3) org.springframework.aop.framework.AopContext 如下:
public final class AopContext { private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal("Current AOP proxy"); private AopContext() { } public static Object currentProxy() throws IllegalStateException { Object proxy = currentProxy.get(); if (proxy == null) { throw new IllegalStateException("Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context."); } else { return proxy; } } @Nullable static Object setCurrentProxy(@Nullable Object proxy) { Object old = currentProxy.get(); if (proxy != null) { currentProxy.set(proxy); } else { currentProxy.remove(); } return old; } }