目录
1. JDK的动态代理:
2. cglib动态代理:
3、Spring AOP 动态代理类生成
4. 找切面的具体过程
4. 1 找所有切面
这里需要细说一下:
4.2 找到合格的切面
4.3 对切面进行排序
5. 生成代理对象
6、 代理类的调用:此处以jdk代理类为例
核心步骤:
链式调用的核心方法:
调用顺序:
Spring AOP技术是一个难点,缓存、事务都是AOP的一部分。今天,我们就开始AOP的第一讲.
我之前写过一篇关于动态代理的博客《大话设计模式》——读后感 (4)为别人做嫁衣?——动态代理模式(2)_chen_yao_kerr的博客-CSDN博客里面 大概介绍了一下动态代理设计模式。说实话,实际开发中,动态代理用的并不多,除非你是写底层框架的。今天,我们继续说一下动态代理:
package com.zhuguang.jack.proxy;
public interface People {
public void zhaoduixiang();
public void yanghaizi() ;
}
package com.zhuguang.jack.proxy;
/*
* 被代理实例
* */
public class Xiaoming implements People {
//1、在小明双手没有解放之前,小明的父母要帮助小明找到对象
@Override
public void zhaoduixiang() {
System.out.println("我在北京工作,没有时间找对象!!");
}
@Override
public void yanghaizi() {
System.out.println("努力挣钱, 养孩子");
}
}
package com.zhuguang.jack.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
* 这个是一个增强类,是对目标对象的一个方法增强
* */
public class Parent implements InvocationHandler {
private People people;
public Parent(People people) {
this.people = people;
}
/*
* 帮助消息找到对象,
* 找到对象以后,帮助小明操持婚礼,带消息
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//帮他找到对象
before();
//这个invoke就会掉到被代理类中的method
method.invoke(people,args);
after();
return null;
}
/*
* 这个方法是小明在找到对象之前,父母帮助他做得事情
* */
private void before() {
System.out.println("我是小明的父母,我需要帮助小明找对象!!");
}
/*
* 找到对象之前,父母帮助他操持婚礼,带小孩
* */
private void after() {
System.out.println("我是小明的父母,我们需要帮助小明操持婚礼,帮他带小孩");
}
}
package com.zhuguang.jack.proxy;
import sun.misc.ProxyGenerator;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//proxy对象就是xiaoming这个对象的一个代理实例,xiaoming这个对象就是一个被代理
People proxy = (People)Proxy.newProxyInstance(People.class.getClassLoader(),new Class>[]{People.class},
new Parent(new Xiaoming()));
proxy.zhaoduixiang();
proxy.yanghaizi();
createProxyClassFile();
}
public static void createProxyClassFile() {
byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{People.class});
try {
FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class");
fileOutputStream.write($Proxy0s);
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
通过以上几段代码,我们就实现了JDK的动态代理的技术了。我们的核心方法就是Xiaoming这个类实现了zhaoduixiang这件事。但是,通过动态代理,我们对小明找对象这件事情实现了功能的增强。找对象之前和找对象之后。
那么,它是如何实现这样的增强的呢?
我们可以在根目录下找到代理类$proxy的class文件了:
下面贴出来代码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.zhuguang.jack.proxy.People;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements People {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void yanghaizi() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void zhaoduixiang() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("com.zhuguang.jack.proxy.People").getMethod("yanghaizi");
m3 = Class.forName("com.zhuguang.jack.proxy.People").getMethod("zhaoduixiang");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
通过代理类 $Proxy0我们知道,这个代理类实现了接口People。也就是说,我们时间调用的是内存中的代理类的 $Proxy0.zhaoduixiang() 方法。 而这个方法内部, 通过反射又调用了 h.invoke(this, m3, (Object[])null), 那这个h是什么呢? 其实,它就是parent的实例。m3 是什么呢?m3对应的就是zhaoduixiang()这个方法。
调用逻辑就是:$Proxy0.zhaoduixiang() --- > h.invoke(this, m3, (Object[])null) h是parent实例 ----> xiaoming.zhaoduixiang(). 因为,我们在parent的invoke方法中实现了对zhaoduixiang()的增强。也就是在找对象之前,之后,我们还可以添加好多方法去干别的事情。这样也就实现了增强功能。
我们首先需要引入依赖包:
cglib
cglib
3.2.5
cglib
cglib-nodep
3.2.5
被代理类:
package com.zhuguang.jack.cglib;
/*
* 这里不实现接口
* */
public class TargetClass {
public void add() {
System.out.println("TargetClass.add");
}
/*
* 这里有一个开启事务
* */
public void del() {
System.out.println("TargetClass.del");
}
/*
* 增强缓存
* */
public void query() {
System.out.println("TargetClass.query");
}
}
针对被代理类3个方法的3个增强:
package com.zhuguang.jack.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//这个是对add方法的一个拦截
public class AddInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("AddInterceptor.intercept.before");
// method.invoke(obj,args);
proxy.invokeSuper(obj,args);
System.out.println("AddInterceptor.intercept.after");
return null;
}
}
package com.zhuguang.jack.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DelInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("DelInterceptor.intercept.before");
proxy.invokeSuper(obj,args);
System.out.println("DelInterceptor.intercept.after");
return null;
}
}
package com.zhuguang.jack.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class QueryInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("QueryInterceptor.intercept.before");
proxy.invokeSuper(obj,args);
System.out.println("QueryInterceptor.intercept.after");
return null;
}
}
工厂:
package com.zhuguang.jack.cglib;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
public class CglibInstanceFactory {
public static Object getInstance() {
//拿到字节码增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallbackFilter(new CglibFilter());
Callback[] callbacks = {
new AddInterceptor(),
new DelInterceptor(),
new QueryInterceptor()};
enhancer.setCallbacks(callbacks);
return enhancer.create();
}
}
测试类:
package com.zhuguang.jack.cglib;
public class Test {
public static void main(String[] args) {
TargetClass instance = (TargetClass)CglibInstanceFactory.getInstance();
instance.query();
}
}
这就是cglib的动态代理。
上一讲,我们已经说到了AOP的开启,其实就是AnnotationAwareAspectJAutoProxyCreator注入并实例化BeanDefinition。
我们在Spring之实例化Bean _ @Resource和@Autowired实现原理(3)_chen_yao_kerr的博客-CSDN博客
中重点谈到了populateBean() 和 initializeBean()这两个方法。而我说initializeBean() 方法是对Bean进行初始化操作的。其实这个方法也是AOP的入口方法:
进入这个方法:
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction
里面备注已经说明applyBeanPostProcessorsAfterInitialization就是AOP的入口,具体就是调用postProcessor接口进行支持的。而我们今天需要关注的则是AnnotationAwareAspectJAutoProxyCreator.
首先,定义好切面类:
package com.xiangxue.jack.aop.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AspectAnnotation {
@Pointcut("execution(public * com.xiangxue.jack.service.*.queryUser(..))")
public void pc1(){}
@Before("pc1()")
public void before() throws Throwable {
System.out.println("==============before =========");
}
@Around("pc1()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("============== before 111=========");
Object result = joinPoint.proceed();
System.out.println("==============after 111 =========");
return result;
}
@After("pc1()")
public void after() throws Throwable {
System.out.println("==============After=========");
}
@AfterReturning("pc1()")
public void afterReturn() throws Throwable {
System.out.println("==============afterReturn =========");
}
@AfterThrowing("pc1()")
public void afterThrowing() throws Throwable {
System.out.println("==============afterThrowing =========");
}
/* @Before("pc2()")
public void before() {
System.out.println("===============只拦截add方法=========");
}
@Before("pc3()")
public void before1(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
Object[] args = joinPoint.getArgs();
System.out.println("===============service1包的拦截=========");
}
@Before("pc3()&&args(bankId,id,list)")
public void before2(String bankId,Integer id,List list) {
System.out.println("===============service1包的拦截=========");
}
@Before(value = "@annotation(targetMethod)",argNames = "joinPoint,targetMethod")
public void xx(JoinPoint joinPoint, TargetMethod targetMethod) {
System.out.println("===============注解拦截 前置通知=========");
System.out.println("==================targetMethod.name = " + targetMethod.name());
}
@AfterReturning(value = "@annotation(returnValue)",returning = "retVal")
public void afterReturning(JoinPoint joinPoint, ReturnValue returnValue, Object retVal) {
System.out.println("==============AspectAnnotation 后置通知 拿返回值=========" + retVal);
}
@AfterThrowing(value = "@annotation(throwsAnno)",throwing = "e")
public void throwMethod(JoinPoint joinPoint, ThrowsAnno throwsAnno, Throwable e) {
System.out.println("==============AspectAnnotation 异常通知 拿异常=========" + e);
}*/
}
需要被拦截的类:
package com.xiangxue.jack.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@Lazy
@Primary
@Service
public class UserServiceImpl1 implements UserService {
@Autowired
AccountService accountService;
@Override
public String queryUser(String userId) {
System.out.println("UserServiceImpl1 ->" + userId);
return "UserServiceImpl1 ->" + userId;
}
@Override
public void addxx(String id) {
System.out.println("UserServiceImpl1 -> addxx :" + id);
}
}
ok,准备工作搞定,还是接着我们的源码bebug;
首先,我们的Spring会实例化Bean,然后判断这个实例化后的bean对象是否需要进行代理类的生成:
其实,就是找到所有的切面类,也就是有@Aspect注解的类。然后,根据当前的beanName找到能够拦截当前bean的切面。如果能找到,则为当前bean生成代理类proxy;如果找不到,则不生成;
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class> beanClass, String beanName, @Nullable TargetSource targetSource) {
//找到合格的切面,重点看
List advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
重点关注一下这个 buildAspectJAdvisors()方法:
第二个问题,它是如何创建切面的呢?
其实,就是找到有@Aspect注解的class对象,然后找到该对象中所有的method方法,只要不是含有@PointCut注解的方法,就进行搜集;如下图:
获取到所有的method方法以后,对把method方法和pointCut方法进行包装,封装成Advisor
1. 我们找到了有注解@Aspect的class文件了,说明这是一个切面;
2. 然后,我们会找到里面所有有注解的方法,除了@PonitCut注解。
3. 此时,我们会搜集@Before,@Around, @After @AfterReturnning @ AfterThrowing等注解。 并且把这些注解、注解里面的信息,比如表达式、参数、注解类型等封装成AspectJAnnotation。 然后创建PointCut对象,把注解对象中的表达式设置到pointCut对象中,最后创建Advice对象。
4. 此处,不同的注解会生成不同的Advice对象。比如:AspectJAroundAdvice、AspectJMethodBeforeAdvice、AspectJAfterThrowingAdvice、AspectJAfterReturningAdvice、AspectJAfterAdcvice。
5. 因为,Advice本身就是最小单位的切面,因此,最终会把Advice和pointCut封装成切面Advisor对象。
上一步,我们已经拿到了所有的切面对象了。接下来,我们就是要根据匹配规则,去找到对象的切面对象了。其实,就是根据pointCut对象中的表达式进行匹配。如果表达式中包含了当前的bean,那这就是一个合格的切面,当前的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)) {
//proxyTargetClass 是否对类进行代理,而不是对接口进行代理,设置为true时,使用CGLib代理。
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//把advice类型的增强包装成advisor切面
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
用来控制代理工厂被配置后,是否还允许修改代理的配置,默认为false
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//获取代理实例
return proxyFactory.getProxy(getProxyClassLoader());
}
1、 创建代理工厂对象proxyFactory
2. 切面对象重新包装,会把自定义的MethodInterceptor 类型的类包装成advisor切面并放入到代理工厂中去
3. 根据 proxyTargetClass 配置生成JDK动态代理还是cglib动态代理
4.创建代理对象,并把代理对象传递到jdk和cglib中
cglib的调用和jdk代理类的调用逻辑一样。动态代理的链式调用,其实就是一个变种的职责链模式,可以简单连接一下相关设计模式:【行为型模式】《大话设计模式》——读后感 (16)加薪非要老板批?——职责链模式_chen_yao_kerr的博客-CSDN博客
测试类:
package com.xiangxue.jack.test;
import com.xiangxue.jack.AOP.ComponentScanBean;
import com.xiangxue.jack.service.UserService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AopTest {
@Autowired
UserService userService;
private ApplicationContext applicationContext;
/* @Before
public void before() {
applicationContext = new AnnotationConfigApplicationContext(ComponentScanBean.class);
}*/
@Test
public void test1() {
applicationContext = new AnnotationConfigApplicationContext(ComponentScanBean.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.queryUser("jack");
userService.addxx("1");
}
/* @Test
public void circularRef() {
UserService userService = applicationContext.getBean(UserService.class);
AccountService accountService = applicationContext.getBean(AccountService.class);
userService.queryUser("1");
accountService.queryAccount("2");
}
@Test
public void test2() {
BankService bankService = applicationContext.getBean(BankService.class);
System.out.println(bankService.queryBank("jack",1,new ArrayList()));
}
@Test
public void introductionTest() {
BankService bean = applicationContext.getBean(BankService.class);
System.out.println(bean.queryBank("jack",1,new ArrayList()));
DataCheck dataCheck = (DataCheck)bean;
dataCheck.check();
}*/
/*
* 可以做一波甜点,带着找问题
* */
/* @Test
public void costomInterceptorTest() {
AnnotationAwareAspectJAutoProxyCreator bean = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
bean.setInterceptorNames("girlMethodInterceptor");
UserService userService = applicationContext.getBean(UserService.class);
userService.queryUser("a");
}
*/
/* @Test
public void proxyFactoryTest() {
ProxyFactory pf = new ProxyFactory();
pf.setTarget(new UserServiceImpl());
pf.addAdvice(new BeforeAdviceBean());
UserServiceImpl proxy = (UserServiceImpl)pf.getProxy();
}
@Test
public void annoIntercepoter() {
BankService bean = applicationContext.getBean(BankService.class);
bean.queryArea("jack");
bean.returnValue("jack");
bean.ThrowMethod("jack");
}*/
}
在我们调用到 userService.queryUser("jack"); 的时候,我们会进入到JdkDynamicAopProxy类的invoke方法中,因为这个类实现了 InvocationHandler 接口:
1. 首先,我们会从代理工厂中拿过滤器链。 Object是一个MethodInterceptor类型的对象,其实就是对advice对象包装了的新对象。其实,就是拿到所有带有注解的的拦截方法,但是他们也是最小的切面。这是我们在生成代理对象的时候传进去的
2. 拿不到调用链的话,说明没有拦截的方法。我们直接调用核心方法,也就是我们的业务方法。如果能够拿到,上面我们说过对切面进行排序操作的,此时我们会根据排好序的Advisor进行调用。说白了,就是有顺序的调用。不管调用顺序如何,最终代码肯定是按照 Around (before 核心业务方法 after) --- > afterReturning --> afterThrowing 这种顺序进行逻辑处理。
3. invocation.proceed() 这个方法就是进入链式调用的意思。也就是说,不管是afterThrowing、afterReturning、after、Around、before 他们都会调用proceed()方法进入链式调用,否则的话,有些方法就会不起作用。
举个例:@Around注解包含了3个步骤。 即业务代码之前 业务代码 业务代码之后。 如果我们不在自己写的Around方法内部调用proceed()方法,那么我们的@Around方法就会缺失部分调用,有可能调用了业务代码之前,然后就停止拦截了,导致业务代码都无法走完。
再举个例子。我们既有@Before拦截,也有@Around拦截,还有@After拦截,当然这样的拦截是不合理的,因为@Around拦截有包含了@Before和@After。此次,就是为了举个例子。如果@Around方法体内部不调用proceed()方法,那么@After方法就不会被调用,也就导致链式调用无法完成。
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//如果执行链中的advice全部执行完,则直接调用joinPoint方法,就是被代理方法
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.
//调用MethodInterceptor中的invoke方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
AspectJAfterReturningAdvice AspectJAfterThrowingAdvice |
AspectJAfterAdvice | AspectJAroundAdvice | MethodBeforeAdviceInterceptor | ||||
拦截方法 | proceed | 拦截方法 | proceed | 拦截方法 | proceed | 拦截方法 | proceed |
1. 直接进入下一次链式调用 | |||||||
2. 直接进入下一次链式调用 | |||||||
3、进入拦截方法执行 | 4、执行完以后,需要自己手动调用proceeed方法进行下一次链式调用 | ||||||
5,@Before需要在业务方法之前调用,所有会直接进入拦截方法 | 6、执行完以后会调用proceed进入下一次链式调用 | ||||||
7、在proceed方法中,我们会调用invokeJoinpoint()。此时,我们会进入到我们的核心业务方法调用。最后退回到链式调用的大流程中去 | |||||||
8. 此时,退回到proceed()方法,执行剩余方法 | |||||||
9. 此时,我们会进入到@After的拦截方法中,执行代码 | |||||||
10、最后退回到@AfterReturning的方法体中,执行方法 | |||||||
11. 最终,完成所有拦截方法的调用,也就是Advice方法的链式调用 |
下面看一下,每个Advice是如何实现链式调用的。
首先调用AspectJAfterThrowingAdvice,直接进入下一次链式调用(不会进入拦截方法)
AfterReturningAdviceInterceptor 直接进入下一次链式调用(不会进入拦截方法)
AspectJAfterAdvice 直接进入下一次链式调用(不会进入拦截方法)
AspectJAroundAdvice 内部没有链式调用,所有直接进入拦截方法中。在拦截方法中,我们需要自己手动调用proceed方法进行链式调用
在拦截方法中,我们可以调用一些没有注解的增强方法,然后再次进入调用链
MethodBeforeAdviceInterceptor @Before是需要在业务方法之前调用的,所有会进入拦截方法,执行完以后,会自动调用proceed进入调用链,执行我们的核心业务方法。
最后,我们看一下打印结果信息,验证一下我们的代码调用流程分析系是否准确