• springAop原理分析-动态代理对象创建过程分析


    概念

    AspectJ

    1. Aspect 切面(由多个切点组成,多个点组成面)

    启用@AspectJ支持后,Spring 会自动检测出在应用程序上下文中定义的任何 Bean,如下使用@Aspect 定义的一个切面示例。

    package org.xyz;
    import org.aspectj.lang.annotation.Aspect;
    
    @Aspect
    public class NotVeryUsefulAspect {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. Pointcut 切点(被切入的方法)

    切入点决定了连接兴趣点,从而使我们能够控制建议的运行时间。Spring AOP 仅支持 Spring bean 的方法执行连接点,因此您可以将切入点视为与Spring bean 上的方法执行相匹配。切入点声明由两部分组成:一个由名称和任何参数组成的签名,以及一个切入点表达式,该表达式准确确定我们感兴趣的方法执行。在 AOP 的@AspectJ注释样式中,切入点签名由常规方法定义提供,切入点表达式通过使用注释进行指示(用作切入点签名的方法必须具有返回类型)。@Pointcut``void

    一个示例可能有助于明确切入点签名和切入点表达式之间的区别。下面的示例定义了一个名为 的切入点,该切入点与任何名为 的方法来执行相匹配:anyOldTransfer``transfer

    支持的切入点指示符

    弹簧 AOP 支持以下 AspectJ 切入点指示符 (PCD), 用于切入点表达式:

    • execution:用于匹配方法执行连接点。这是使用弹簧 AOP 时要使用的主要切入点指示符。
    • within:限制匹配以连接特定类型中的点(使用Spring AOP时执行在匹配类型中声明的方法)。
    • this:限制与连接点的匹配(使用弹簧 AOP 时的方法的执行),其中 Bean 引用(弹簧 AOP 代理)是给定类型的实例。
    • target:限制与连接点的匹配(使用Spring AOP时的方法执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。
    • args:限制匹配以连接点(使用Spring AOP时执行方法),其中参数是给定类型的实例。
    • @target:限制与连接点(使用Spring AOP时执行方法)的匹配,其中执行对象的类具有给定类型的注释。
    • @args:限制与连接点的匹配(使用Spring AOP时的方法执行),其中传递的实际参数的运行时类型具有给定类型的注释。
    • @within:限制在具有给定注释的类型中连接点的匹配(使用Spring AOP时,执行具有给定注释的类型中声明的方法)。
    • @annotation:将匹配限制为连接点的主题(在 Spring AOP 中运行的方法)具有给定注释的连接点。
    1. Advice 通知(切入的时机,被切入的业务逻辑)

    2. Before 方法执行之前

    3. After方法执行之后

      1. AfterThrowing 方法执行之后异常处理
      2. AfterReturning 方法执行之后,返回结果
    4. Around 环绕通知,方法执行前和执行之后

    5. JoinPoint 连接点,用于获取方法的参数(配合Advice 里的具体通知使用)

      任何建议方法都可以将 类型的参数声明为其第一个参数。请注意,在建议周围需要声明类型的第一个参数,它是 的子类org.aspectj.lang.JoinPointProceedingJoinPointJoinPoint。

      • getArgs():返回方法参数。
      • getThis(): 返回代理对象。
      • getTarget(): 返回目标对象。
      • getSignature(): 返回所建议方法的说明。
      • toString(): 打印所建议方法的有用说明。

    关于具体的切面,切点,建议,以及链接点请参考下面官方文档和实战部分代码结合理解

    核心技术 (spring.io)

    AOP动态代理

    spring AOP 默认对 AOP 代理使用标准 JDK 动态代理。这使得任何接口(或一组接口)都可以被代理。

    这对于代理类,不是必须实现被代理类接口。缺省情况下,如果业务对象没有可以实现的接口,则使用 CGLIB。由于编程到接口而不是类是很好的做法,因此业务类通常实现一个或多个业务接口。在那些(希望是罕见的)情况下,可以强制使用CGLIB,在这些情况下,您需要建议未在接口上声明的方法,或者需要将代理对象作为具体类型传递给方法。

    Aop 动态代理工厂类图

    image-20220917114946634

    Aop 动态代理对象创建过程

    IOC部分

    这部分只是展示一个正常bean的创建过程(如果启用了切点代理,这部分是一样的)

    image-20220917153207204

    initalizeBean 方法和applyBeanPostProcessorsAfterInitialization方法是创建aop动态代理的重要方法,下面aop部分主要以后置处理器的方法进行详细剖析。

    AOP部分

    applyBeanPostProcessorsAfterInitialization(获取所有后置处理器)

    image-20220917154812383

    annotationAwareAspectJAutoProxyCreator(切面代理后置处理器)

    image-20220917155228123

    实战

    AOP动态动态代理对象

    目录结构

    image-20220917160012327

    配置类

    切面类

    package com.kang.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * @Description 切面测试
     * @ClassName Aoptest
     * @Author 康世行
     * @Date 10:06 2022/7/29
     * @Version 1.0
     **/
    @Aspect
    @Component
    public class Aoptest {
    
        @Pointcut("execution(* *(..))")
        private void beforeTest(){}
    
        @Before("beforeTest()")
        public void testBefore(JoinPoint account){
            Object[] args = account.getArgs();
            for (Object arg : args) {
                System.out.println("方法执行之前参数->"+arg);
            }
        }
    
        @AfterReturning("beforeTest()")
        public void testAfter(JoinPoint joinPoint){
            Object[] args = joinPoint.getArgs();
            for (Object arg : args) {
                System.out.println("方法执行之后参数->"+arg);
            }
        }
    
    //    @Around("beforeTest()")
    //    public Object testAroundTest(ProceedingJoinPoint joinPoint){
    //          Object result=null;
    //        try {
    //            Object[] args = joinPoint.getArgs();
    //            for (Object arg : args) {
    //                System.out.println("方法执行之前参数:->"+arg);
    //            }
    //             result= joinPoint.proceed();
    //
    //        } catch (Throwable e) {
    //            throw new RuntimeException(e);
    //        }
    //        System.out.println("方法执行完毕");
    //        return result;
    //    }
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    注解容器上下文配置类(用于扫包和开启aop代理注解)

    package com.kang.aop.config;
    
    import com.kang.aop.Aoptest;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScans;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    import org.springframework.stereotype.Component;
    
    /**
     * @Description 开启切面配置
     * @ClassName AppConfig
     * @Author 康世行
     * @Date 10:05 2022/7/29
     * @Version 1.0
     **/
    @Configuration
    @EnableAspectJAutoProxy
    @ComponentScan({"com.kang.aop"})
    public class AppConfig {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    JDK

    接口

    package com.kang.aop.aoptest;
    
    /**
     * @Description 发送消息接口
     * @ClassName SendMessage
     * @Author 康世行
     * @Date 9:39 2022/7/30
     * @Version 1.0
     **/
    public interface SendMessage {
    
        /**
        * @author 康世行
        * @description: 发送消息
        * @date  2022/7/30 9:40
        * @param msg 消息体
        * @return void
        * @Version1.0
        **/
        void sendMessage(String msg);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    实现类

    package com.kang.aop.aoptest;
    
    import org.springframework.stereotype.Service;
    
    /**
     * @Description 发送消息实现类
     * @ClassName SendMessageImpl
     * @Author 康世行
     * @Date 9:40 2022/7/30
     * @Version 1.0
     **/
    @Service("sendMessageImpl")
    public class SendMessageImpl implements SendMessage {
        @Override
        public void sendMessage(String msg) {
            System.out.println("发送的消息是->"+msg);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    package com.kang.aop.aoptest;
    
    import org.springframework.stereotype.Service;
    
    /**
     * @Description TODO
     * @ClassName temp
     * @Author 康世行
     * @Date 9:46 2022/7/30
     * @Version 1.0
     **/
    @Service("temp")
    public class temp implements SendMessage{
        @Override
        public void sendMessage(String msg) {
            System.out.println(msg);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    结果

    image-20220917160657785

    CGLIB

    实现类

    package com.kang.aop.aoptest;
    
    import org.springframework.stereotype.Service;
    
    /**
     * @Description TODO
     * @ClassName userSavle
     * @Author 康世行
     * @Date 10:12 2022/7/29
     * @Version 1.0
     **/
    @Service
    public class UserImpl {
        public void save(String msg){
            System.out.println("保存用户信息:"+msg);
        }
        public String print(String msg){
            String result= "userImpl"+msg;
            return result;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    结果

    image-20220917160550865

    完整分析-流程图

    https://www.processon.com/view/link/63257d13f346fb3377e81de7

  • 相关阅读:
    解道6-编程技术3
    1、neo4j图数据库基本使用
    21.1 使用PEfile分析PE文件
    【Android】修改aar包里的jar包里的class文件内容
    CentOS7.5 更新内核 内核升级
    千寻简Java词典音标版
    Maven的作用
    今天,该让 python 上个热门
    谈安全测试的重要性
    如何区分快解析内网穿透和Nginx端口映射?
  • 原文地址:https://blog.csdn.net/kangshihang1998/article/details/126907525