Spring5底层原理 学习笔记(二)AOP篇


参考 糖果墙黑马程序员Spring视频教程,全面深度讲解spring5底层原理 学习笔记


AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能
除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高
  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

AOP实现之ajc编译器

使用ajc编译器进行代码增强,首先需要在pom.xml文件中加入ajc编译器插件依赖

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.11</version>
            <configuration>
                <complianceLevel>1.8</complianceLevel>
                <source>8</source>
                <target>8</target>
                <showWeaveInfo>true</showWeaveInfo>
                <verbose>true</verbose>
                <Xlint>ignore</Xlint>
                <encoding>UTF-8</encoding>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <!-- use this goal to weave all your main classes -->
                        <goal>compile</goal>
                        <!-- use this goal to weave all your test classes -->
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

加入aspectjweaver的依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>

需要增强的类MyService

@Slf4j
public class MyService {
 
    public void foo() {
        log.debug("foo()");
    }
}

切面类MyAspect,编写execution表达式,对MyService类的foo()方法进行增强

@Aspect // ⬅️注意此切面并未被 Spring 管理,本项目pom文件中根本没有引入spring的相关类
public class MyAspect {
    private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
    @Before("execution(* top.jacktgq.service.MyService.foo())")
    public void before() {
        log.debug("before()");
    }
}

测试代码

public class Aop_Aspectj_Test {
    @Test
    public void testAopAjc() {
        new MyService().foo();
    }
}

  • 可以看到没有引入任何跟spring框架相关的包,MyService类是通过直接new()的方式获得的,所以也就不存在使用了动态代理的说法了

  • 打开编译后的MyService.class文件,双击以后idea会反编译该字节码文件,可以看到foo()方法体的开头加了一行代码,这就是增强的代码,这是ajc编译器在编译MyService类的时候为我们添加的代码,这是一种编译时的增强。
    在这里插入图片描述

AOP实现之agent类加载

需要依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>

需要增加的类MyService

@Slf4j
public class MyService {
    public void foo() {
        log.debug("foo()");
        bar();
    }
    public void bar() {
        log.debug("bar()");
    }
}

切面类MyAspect,编写execution表达式,对MyService类的foo()方法进行增强

@Aspect // ⬅️注意此切面并未被 Spring 管理,本项目pom文件中根本没有引入spring的相关类
@Slf4j
public class MyAspect {
    @Before("execution(* top.jacktgq.service.MyService.*())")
    public void before() {
        log.debug("before()");
    }
}

测试代码

public class Aop_agent_Test {
    @Test
    public void testAopAgent() throws Exception {
        MyService myService = new MyService();
        myService.foo();
        System.in.read();
    }
}

运行时需要在 VM options 里加入 -javaagent:D:\同步空间\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar把其中 D:\同步空间\repository 改为你自己 maven 仓库起始地址
注:还需要在resources/META-INF目录下建一个aop.xml配置文件,内容如下,aspectj会自动扫描到这个配置文件,不加这个配置文件不会出效果。

<aspectj>
    <aspects>
        <aspect name="top.jacktgq.aop.MyAspect"/>
        <weaver options="-verbose -showWeaveInfo">
            <include within="top.jacktgq.service.MyService"/>
            <include within="top.jacktgq.aop.MyAspect"/>
        </weaver>
    </aspects>
</aspectj>

总结:

  • 可以看到没有引入任何跟spring框架相关的包,MyService类是通过直接new()的方式获得的,所以也就不存在使用了动态代理的说法了

  • 打开编译后的MyService.class文件,双击以后idea会反编译该字节码文件,可以看到foo()方法体中并没有添加多余的代码,所以就不是编译时增强了,而是类加载的时候增强的,这里可以借助阿里巴巴的Arthas工具,下载地址:https://arthas.aliyun.com/doc/en/download.html,解压以后进入到arthas的bin目录下,启动黑窗口,输入java -jar .\arthas-boot.jar,在输出的java进程列表里面找到我们要连接的进程,输入对应进程的序号,我这里是4,连接上以后会打印ARTHAS的logo
    在这里插入图片描述
    再输入jad top.jacktgq.service.MyService反编译内存中的MyService类
    在这里插入图片描述
    可以看到foo()和bar()方法体的第一行都加了一行代码,这就说明通过添加虚拟机参数-javaagent的方式可以在类加载的时候对代码进行增强。

AOP实现之动态代理

jdk动态代理

演示

public class ProxyJdkTest {
    public static void main(String[] args) {
        Dog dog = new Dog();
        // 用于加载在运行期间生成的字节码
        ClassLoader classLoader = ProxyJdkTest.class.getClassLoader();
        Animal proxyDog = (Animal) Proxy.newProxyInstance(classLoader, new Class[]{Animal.class}, (proxy, method, args1) -> {
            System.out.println("Before");
            Object result =  method.invoke(dog, args1);
            System.out.println("After");
            return result;
        });
        proxyDog.run();
    }

    interface Animal {
        void run();
    }

    static class Dog implements Animal {

        @Override
        public void run() {
            System.out.println("Dog is running.");
        }
    }
}

运行结果
在这里插入图片描述
Jdk动态代理要求目标必须实现接口,代理类与被代理类是平级兄弟关系

模拟实现动态代理

准备好一个接口Animal和一个目标代理类Dog

public interface Animal {
    void run();
    int eat();
}

public class Dog implements Animal {

    @Override
    public void run() {
        System.out.println("Dog is running.");
    }

    @Override
    public int eat() {
        System.out.println("Dog is eating");
        return 0;
    }
}

手动创建一个类叫做 P r o x y 0 ,假装这是编译时期生成的并且定义一个 I n v o c a t i o n H a n d l e r 置于 Proxy0,假装这是编译时期生成的 并且定义一个InvocationHandler置于 Proxy0,假装这是编译时期生成的并且定义一个InvocationHandler置于Proxy0内部,用于提供给使用者使用以灵活编写增强逻辑

public class $Proxy0 implements Animal {
    static Method runMethod;
    static Method eatMethod;

    static {
        try {
            runMethod = Animal.class.getMethod("run");
            eatMethod = Animal.class.getMethod("eat");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    public $Proxy0(InvocationHandler invocationHandler) {
        this.invocationHandler = invocationHandler;
    }

    private final InvocationHandler invocationHandler;

    @Override
    public void run() {
        try {
            invocationHandler.invoke(this, runMethod, new Object[0]);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int eat() {
        Object result;
        try {
            result = invocationHandler.invoke(this, eatMethod, new Object[0]);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return (int) result;
    }
}

public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Exception;
}

测试代码

public class ProxyJdkSimulation {
    public static void main(String[] args) {
        Dog dog = new Dog();
        // 模拟Jdk动态代理
        Animal proxyDog = new $Proxy0((proxy, method, args1) -> {
            System.out.println("Before");
            Object result = method.invoke(dog, args1);
            System.out.println("After");
            return result;
        });

        proxyDog.eat();
        proxyDog.run();
    }
}

测试结果
在这里插入图片描述

动态生成代理类需要使用到asm的api,这里就不展开了

Jdk对于反射调用的优化

在jdk代理中调用方法使用到的method.invoke()是通过反射实现的,我们知道反射的效率比较低
循环调用method.invoke17次,能够发现前16次都是通过NativeMethodAccessorImpl实现的,第17次通过一个动态生成的GeneratedMethodAccessor2,优化为非反射调用

cglib动态代理

演示

cglib动态代理是通过生成被代理类的子类,子类重写被代理类的方法实现的
因此要求被代理类不能被final修饰,待增强方法也不能被final修饰

cglib动态代理的增强过程有method和methodProxy两个参数,他们可以做到相同的效果,但是method.invoke()是通过反射的方式调用,methodProxy不是通过反射,所以效率要比method高

methodProxy有invoke和invokeSuper两个方法
methodProxy.invoke(targe, args)方法是Spring使用的
methodProxy.invokeSuper(proxy, args)方法不需要被代理类作为参数

public class ProxyCglibTest {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Dog proxyDog = (Dog) Enhancer.create(Dog.class, (MethodInterceptor) (proxy, method, args1, methodProxy) -> {
            System.out.println("Before");
//            Object result = method.invoke(dog, args1);
//            Object result = methodProxy.invoke(dog, args1);
            Object result = methodProxy.invokeSuper(proxy, args1);
            System.out.println("After");
            return result;
        });
        proxyDog.run();
    }

    static class Dog {
        public void run() {
            System.out.println("Dog is running.");
        }
    }
}

控制台输出
在这里插入图片描述

模拟实现cglib动态代理

cglib动态代理其实跟jdk的很相似
不同在于代理类中除了带有增强功能的方法之外,还需要带有原始功能的方法,以用于创建methodProxy对象

public class Target {
    public void save() {
        System.out.println("save()");
    }
    public void save(int i) {
        System.out.println("save(int i)");
    }
    public void save(long j) {
        System.out.println("save(long j)");
    }
}

public class Proxy extends Target {
    private static Method save0Method;
    private static Method save1Method;
    private static Method save2Method;

    private static MethodProxy save0MethodProxy;
    private static MethodProxy save1MethodProxy;
    private static MethodProxy save2MethodProxy;

    static {
        try {
            save0Method = Target.class.getMethod("save");
            save1Method = Target.class.getMethod("save", int.class);
            save2Method = Target.class.getMethod("save", long.class);

            save0MethodProxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
            save1MethodProxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
            save2MethodProxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private MethodInterceptor methodInterceptor;

    public Proxy(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    /**
     * 原始功能的方法
     */
    public void saveSuper() {
        super.save();
    }

    public void saveSuper(int i) {
        super.save(i);
    }

    public void saveSuper(long j) {
        super.save(j);
    }

    /**
     * 以下是带有增强功能的方法
     */

    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0Method, null, save0MethodProxy);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1Method, new Object[]{i}, save1MethodProxy);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void save(long j) {
        try {
            methodInterceptor.intercept(this, save2Method, new Object[]{j}, save2MethodProxy);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
}

测试代码

public class CglibProxySimulation {
    public static void main(String[] args) {
        Target target = new Target();
        Proxy targetProxy = new Proxy((p, method, objects, methodProxy) -> {
            System.out.println("Before");
//            return method.invoke(target, objects);
//            return methodProxy.invoke(target, objects);
            return methodProxy.invokeSuper(p, objects);
        });
        targetProxy.save();
        targetProxy.save(1);
        targetProxy.save(2L);
    }
}

cglib如果通过MethodProxy使用非反射的方式调用方法

在MethodInterceptor中如果调用了methodProxy.invoke()或methodProxy.invokeSuper()方法,都会生成一个FastClass子类的动态代理类来使用非反射的方式调用

  • TargetFastClass
    假设methodProxy.invoke()时生成的代理类叫做TargetFastClass,那么这个类内部大概是这样的
public class TargetFastClass {
    static Signature signature0 = new Signature("save", "()V");
    static Signature signature1 = new Signature("save", "(I)V");
    static Signature signature2 = new Signature("save", "(J)V");

    public Object invoke(int index, Object target, Object[] args) throws InvocationTargetException {
        if (index == 0) {
            ((Target) target).save();
            return null;
        }
        if (index == 1) {
            ((Target) target).save((int) args[0]);
            return null;
        }
        if (index == 2) {
            ((Target) target).save((long) args[0]);
            return null;
        }
        throw new RuntimeException("No such method");
    }

    public int getIndex(Signature signature) {
        if (signature.equals(signature0)) {
            return 0;
        }
        if (signature.equals(signature1)) {
            return 1;
        }
        if (signature.equals(signature2)) {
            return 2;
        }
        return -1;
    }
}
* getIndex方法根据方法的标签判断得到当前方法在内部定义的序号
* 然后fastClass内部会将getIndex拿到的序号传递到方法invoke中,根据这个序号就可以使用非反射的方式调用方法了。
  • ProxyFastClass
    这个类是针对代理类使用的,内部同TargetFastClass逻辑类似,不同之处在于标签记录的是原始功能的方法(即前面的saveSuper()那几个方法),invoke方法也是调用代理类的原始功能方法,因为如果调用增强功能的方法,就会不断进入MethodInterceptor.intercept开始死循环

Spring选择代理

Spring在使用动态代理的时候会在不同情况下使用jdk亦或是cglib的动态代理

准备一个切面(一个通知+一个切点),使用Spring提供的ProxyFactory生成一个代理类,打印查看这个代理类是什么方式生成的
导入依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>

public class SpringChooseProxyDemo {
    public static void main(String[] args) {

        // 2. 准备一个切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        // 1. 准备一个通知
        MethodInterceptor methodInterceptor = invocation -> {
            System.out.println("Before");
            Object result = invocation.proceed();
            System.out.println("After");
            return result;
        };

        // 3. 将通知和切点整合成一个切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, methodInterceptor);
        // 4. 通过ProxyFactory生成代理类
        ProxyFactory proxyFactory = new ProxyFactory();

        Target target = new Target();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);
        I1 proxy = (I1) proxyFactory.getProxy();
        System.out.println("proxy.class ======== " + proxy.getClass());
        proxy.foo();
        System.out.println("-----------");
        proxy.bar();
    }

    static class Target implements I1 {

        @Override
        public void foo() {
            System.out.println("foo");
        }

        @Override
        public void bar() {
            System.out.println("bar");
        }
    }

    interface I1 {
        void foo();
        void bar();
    }
}

执行结果
在这里插入图片描述可以看出这里是通过cglib实现的动态代理

Spring选择动态代理规则

ProxyFactory有一个属性叫做proxyTargetClass,默认情况下proxyTargetClass = false;

  • 当proxyTargetClass = false, 若目标类实现了接口,使用jdk动态代理
  • 当proxyTargetClass = false, 若目标类没有实现接口,使用cglib动态代理
  • 当proxyTargetClass = true,则总是使用cglib动态代理

但是前面并没有把proxyTargetClass改成true,并且Target类也实现了接口I1,为什么还是使用了cglib动态代理呢,那是因为我们需要主动告诉proxyFactory目标类实现了什么接口
proxyFactory.setInterfaces();

切点匹配

前面提到的AspectJExpressionPointcut能够匹配切点,其实是实现了MethodMatcher接口的matches方法,根据matches的返回值是否为true来判断是否匹配上
现在就可以通过实现一个MethodMatcher接口,定义@Transactional注解的切点规则

public class MethodMatch{
    public static void main(String[] args) throws NoSuchMethodException {
        StaticMethodMatcher methodMatcher = new StaticMethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                MergedAnnotations annotations = MergedAnnotations.from(method);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                return annotations.isPresent(Transactional.class);
            }
        };
        System.out.println(methodMatcher.matches(Target1.class.getMethod("foo"), Target1.class));
        System.out.println(methodMatcher.matches(Target1.class.getMethod("bar"), Target1.class));
        System.out.println(methodMatcher.matches(Target2.class.getMethod("foo"), Target2.class));
        System.out.println(methodMatcher.matches(Target3.class.getMethod("foo"), Target3.class));
    }

    static class Target1 {
        @Transactional
        public void foo() {

        }

        public void bar() {

        }
    }

    @Transactional
    static class Target2 {
        public void foo() {

        }
    }

    @Transactional
    interface T {
    }

    static class Target3 implements T {
        public void foo() {

        }
    }
}

控制台输出
在这里插入图片描述

从@Aspect到Advisor

AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator是一个Bean后处理器,Spring容器能够获取到配置的切面信息,跟它的存在息息相关
AnnotationAwareAspectJAutoProxyCreator的发挥作用的时机
类:创建 -> (发挥作用)依赖注入 -> 初始化(发挥作用)

方法findEligibleAdvisors

AnnotationAwareAspectJAutoProxyCreator中有两个很重要的方法,一个是它的父类AbstractAdvisorAutoProxyCreator的findEligibleAdvisors,这个方法可以查询容器中有哪些切面是与指定的类有关系的。
下面模拟使用AnnotationAwareAspectJAutoProxyCreator,通过反射的方式调用findEligibleAdvisors方法,查看哪些切面与Target1或Target2有关系。

public class Demo {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

        context.refresh();

        AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method findEligibleAdvisors = AbstractAdvisorAutoProxyCreator.class.getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
        findEligibleAdvisors.setAccessible(true);
        List<Advisor> advisors = (List<Advisor>) findEligibleAdvisors.invoke(autoProxyCreator, Target1.class, "target1");
        advisors.forEach(System.out::println);
    }

    static class Target1 {
        public void foo() {
            System.out.println("Target1 foo");
        }
    }

    static class Target2 {
        public void bar() {
            System.out.println("Target2 bar");
        }
    }

    @Aspect
    static class Aspect1 {
        @Before("execution(* foo())")
        public void before() {
            System.out.println("Aspect1 before");
        }

        @After("execution(* foo())")
        public void after() {
            System.out.println("Aspect1 after");
        }
    }

    @Configuration
    static class Config {
        @Bean
        public DefaultPointcutAdvisor pointcutAdvisor(MethodInterceptor methodInterceptor) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");

            return new DefaultPointcutAdvisor(pointcut, methodInterceptor);
        }

        @Bean
        public MethodInterceptor methodInterceptor() {
            return invocation -> {
                System.out.println("Config before");
                return invocation.proceed();
            };
        }
    }

}

当findEligibleAdvisors的目标是Target1的时候,控制台打印

org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [com.ysatnaf.spring_learning.aop.from_aspect_to_advisor.Demo$Config$$Lambda$56/580871917@7d8704ef]
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.ysatnaf.spring_learning.aop.from_aspect_to_advisor.Demo$Aspect1.before()]; perClauseKind=SINGLETON
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.ysatnaf.spring_learning.aop.from_aspect_to_advisor.Demo$Aspect1.after()]; perClauseKind=SINGLETON

是Target2的时候,控制台打印为空

方法wrapIfNecessary

该方法是AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator的方法。
该方法内部会调用方法findEligibleAdvisors,根据返回的advisor数量,来判断是否需要为目标对象生成代理对象。如果没有切面与目标对象相关,那么就返回目标对象,如果有切面,那么生成代理类

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

        context.refresh();

        AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method wrapIfNecessary = AbstractAutoProxyCreator.class.getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
        wrapIfNecessary.setAccessible(true);
        Object target1 = wrapIfNecessary.invoke(autoProxyCreator, new Target1(), "target1", "target1");
        Object target2 = wrapIfNecessary.invoke(autoProxyCreator, new Target2(), "target2", "target2");
        System.out.println("target1:" + target1.getClass());
        System.out.println("target2:" + target2.getClass());
    }

输出台打印,在这里插入图片描述
因为Target1有被切面切到,所以生成了代理类,而Target2没有被切面切到,所以没有生成代理类。

代理对象的创建时机

正常执行下来代理对象是在对象初始化之后才被创建
但是如果有循环依赖,就会在依赖注入之前创建
假设A依赖B,B又依赖A,在A的创建过程中 依赖注入B的时候会先去创建对象B,但是因为B也依赖了A,这时候就会先把A的代理对象创建出来放到二级缓存中,然后B创建完成,A才能拿到B的对象进行依赖注入
也就是说依赖注入的是代理对象
注意:依赖注入和初始化过程不应该被增强,仍应该被施加于原始对象上

@Order

可以通过将@Order注解标注在高级切面(被@Aspect标注的类)上控制切面的顺序
低级切面是通过setOrder()进行控制
order数值越大优先级越低,默认是最大的

从高级切面到低级切面

在查找切面的过程中,如果遇到了高级切面,那么会通过解析把高级切面转换成低级切面,最后放在一个List里
以解析@Before为例
在这里插入图片描述

将切面统一转换成环绕通知

拿到所有切面之后会将他们都统一转换成环绕通知MethodInvocation
在这里插入图片描述
打印,查看转换前后的类型
在这里插入图片描述
可以看到AspectJMethodBeforeAdvice转成了MethodBeforeAdviceInterceptor
AspectJAfterReturningAdvice转化成AfterReturningAdviceInterceptor
AspectJAroundAdvice没有转换,因为它本身就是环绕通知
转换过程采用了适配器模式

调用执行链

调用链List的执行过程是一个执行链
在最前面需要添加一个环绕通知叫做ExposeInvocationInterceptor,它的作用就是把一个MethodInvocation放入当前线程以供后续的切面使用。
通过ReflectiveMethodInvocation执行

最后执行每一个环绕通知,其实是采用了递归的方式

  1. before1
  2. before2
  3. target.method.invoke
  4. after2
  5. after1
  • 模拟调用链
    在这里插入图片描述

总结静态通知调用过程

  1. 分析执行类是否需要生成代理类
  2. 获取所有的切面
  3. 把高级切面转换成低级切面
  4. 把所有的通知转换成环绕通知
  5. 调用执行链,在这之前需要把一个MethodInvocation放入当前线程

动态通知调用

动态通知调用指的是需要传递参数的通知,没有参数的通知因为可以直接执行增强逻辑所以叫做静态通知,相对于没有参数的通知来说,需要记录切点的信息以便于后续解析参数
下图中就是一个动态通知
在这里插入图片描述
动态通知调用过程和静态的基本一样
唯一不同的地方就在于factory.getInterceptorsAndDynamicInterceptionAdvice(转换成环绕通知)时,会在List中新增一个叫做InterceptorAndDynamicMethodMatcher,这个动作其实在方法名中DynamicInterception体现了。
在这里插入图片描述
InterceptorAndDynamicMethodMatcher包含了两个成员变量,一个是环绕通知,一个是MethodMatcher
在这里插入图片描述
通过反射的方式打印InterceptorAndDynamicMethodMatcher内部包含的信息
在这里插入图片描述
可以看到包含了切点信息和通知类型,切点信息就可以用于解析动态参数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值