• Spring AOP 超详细源码解析


    知识章节

    基础知识

    什么是 AOP

    • AOP 的全称是 “Aspect Oriented Programming”,即面向切面编程
    • 在 AOP 的思想里面,周边功能(比如性能统计,日志,事务管理等)被定义为切面,核心功能和切面功能分别独立进行开发,然后把核心功能和切面功能“编织”在一起,这就叫 AOP
    • AOP 能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性

    AOP 基础概念

    • 连接点(Join point):能够被拦截的地方,Spring AOP 是基于动态代理的,所以是方法拦截的,每个成员方法都可以称之为连接点
    • 切点(Poincut):每个方法都可以称之为连接点,我们具体定位到某一个方法就成为切点
    • 增强/通知(Advice):表示添加到切点的一段逻辑代码,并定位连接点的方位信息,简单来说就定义了是干什么的,具体是在哪干
    • 织入(Weaving):将增强/通知添加到目标类的具体连接点上的过程
    • 引入/引介(Introduction):允许我们向现有的类添加新方法或属性,是一种特殊的增强
    • 切面(Aspect):切面由切点和增强/通知组成,它既包括了横切逻辑的定义、也包括了连接点的定义。

    • AOP 五种通知分类
      • 前置通知(Before Advice):在目标方法被调用前调用通知功能
      • 后置通知(After Advice):在目标方法被调用之后调用通知功能
      • 返回通知(After-returning):在目标方法成功执行之后调用通知功能
      • 异常通知(After-throwing):在目标方法抛出异常之后调用通知功能
      • 环绕通知(Around):把整个目标方法包裹起来,在被调用前和调用之后分别调用通知功能

    AOP 简单示例

    创建一个配置类,开启 AspectJ 的自动代理功能

    @Configuration
    @EnableAspectJAutoProxy
    @ComponentScan("com.aspectj")
    public class DemoAspectJConfig {
    	// @EnableAspectJAutoProxy 开启注解 aop 功能
    	// 向 spring 容器中注册 AnnotationAwareAspectJAutoProxyCreator
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    新建接口和接口实现类

    // 接口
    public interface DemoDao {
    	void query();
    }
    
    // 实现类
    @Component
    public class DemoDaoImpl implements DemoDao {
    	@Override
    	public void query() {
    		System.out.println("query ......");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    创建一个切面

    @Aspect
    @Component
    public class DemoAspect {
    	// 配置切点
    	@Pointcut("execution(* com.aspectj.dao.*.*(..))")
    	public void point(){
    	}
    	// 切点通知
    	@Before("point()")
    	public void beforeAdd(){
    		System.out.println("before-------------");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    创建启动类

    public class TestAspect {
    	public static void main(String[] args) {
    		// 刷新容器
    		AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext(DemoAspectJConfig.class)
    		// 获取 bean 对象
    		DemoDao dao = configApplicationContext.getBean(DemoDao.class);
    		// 代理对象执行
    		dao.query();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    启动 main() 方法,可以看到输出的内容,AOP 配置方法执行


    Spring AOP 执行流程


    源码解读

    代码入口






    前置处理

    判断是否是切面








    构建 bean 对象 advisors 流程
    ① 遍历所有的类
    ② 判断是否切面,只有切面才会进入后面逻辑
    ③ 获取每个 Aspect 的切面列表
    ④ 保存 Aspect 的切面列表到缓存 advisorsCache


    获取切面列表







    到这一步,前置处理已经完成 !

    后置处理

    获取切面






    创建代理对象



    对象增强处理流程
    ① 获取当前 bean 对象 advisors 列表,判断是否为空
    ② 不为空就创建代理对象

















    切面执行



    实例化 ReflectiveMethodInvocation 对象,调用 proceed() 方法


    第一次递归

    数组的第一个对象是 ExposeInvocationInterceptor,执行 invoke(),注意入参是 ReflectiveMethodInvocation


    第二次递归

    数组的第二个对象是 MethodBeforeAdviceInterceptor,执行 invoke()




    第三次递归

    如果 advisor 有后置处理,就执行第三次递归


    跳出递归


    总结

    • AOP 实现原理
      • 将所有的切面都保存在缓存中
      • 取出缓存中的切面列表,和 bean 对象的所有方法匹配,拿到属于 bean 对象的切面列表
      • 创建 AOP 代理对象
      • 通过 “责任链 + 递归”,去执行切面逻辑
  • 相关阅读:
    21 C++设计模式之访问者(Visitor)模式
    物联网入门系列(一):快速搭建一站式数据存储与实时分析平台
    北邮 数字系统设计 14 Floating Point Number
    京东资深架构师教你搭建高可用高并发系统,亿级流量核心架构文档
    KUKA机器人通过直接输入法设定负载数据和附加负载数据的具体操作
    小知识:使用errorstack定位特定问题
    python烟花代码
    聊聊httpclient的disableConnectionState
    C Primer Plus(6) 中文版 第13章 文件输入/输出 13.2 标准I/O
    【今日文章】:如何用css 实现星空效果
  • 原文地址:https://blog.csdn.net/qq_41956014/article/details/127703382