【Spring】AOP实现原理

news2024/12/23 3:23:57

注册AOP代理创建器

在平时开发过程中,如果想开启AOP,一般会使用@EnableAspectJAutoProxy注解,这样在启动时,它会向Spring容器注册一个代理创建器用于创建代理对象,AOP使用的是AnnotationAwareAspectJAutoProxyCreator,它实现了SmartInstantiationAwareBeanPostProcessor,从名字中可以看出这是一个Bean后置处理器BeanPostProcessor,BeanPostProcessor是Spring提供的一个扩展点,里面提供了两个方法,分别为postProcessBeforeInitialization(初始化之前)和postProcessAfterInitialization(初始化之后),可以在Bean初始化前后,进行一些操作(比如为Bean设置属性值)。

关于后置处理器的使用可参考:【Spring】BeanPostProcessor后置处理器

  • Advisor:对切面的封装,使用了@AspectJ注解的类会被Spring封装成Advisor。

AOP的实现主要在代理创建器的postProcessAfterInitialization方法中:

  • postProcessAfterInitialization:在bean初始化之后执行的方法,这时候bean已经实例化完毕,这里会调用wrapIfNecessary方法判断是否有必要为该Bean生成AOP代理对象,如果不需要创建AOP代理对象直接返回即可,反之会获取Advisors,然后创建AOP的代理对象,替换掉原来生成的Bean
// AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator中实现
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    /**
     * 在bean初始化之后执行的方法
     */
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            // 构建缓存Key
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                // 是否有必要创建AOP代理对象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
}

AOP代理条件判断

Spring需要知道有哪些类需要进行AOP代理、哪些需要跳过AOP代理,比如我们使用@Aspect标注的切面类,只是一个普通Bean,不需要进行AOP代理,而我们的目标类,就需要进行代理,所以这一步会进行判断。

处于以下情况之一是不需要进行AOP代理的,会跳过:

  1. 如果是Advice、Pointcut、Advisor、AopInfrastructureBean类本身及其子类则跳过创建,这些是Spring的基础类,不需要进行AOP代理;
  2. 如果有Aspect注解且不是通过Ajc编译的类,这个就是用@Aspect标注的切面类,也不需要AOP进行代理;
  3. 这个判断条件的作用同上,只不过个判断主要用于在XML中通过 aop:aspect标签形式来配置切面的情况,Spring会生成一个对应的AspectJPointcutAdvisor,切面本身对应的那个Java类是不需要进代理的,所以添加了一个判断,跳过切面本身对应的那个Java类,在使用注解和使用aop:aspect标签时实现不一样,所以这里又加了一个条件判断;

获取Advisor

对于上述情况的bean会跳过,剩下的Bean需要先获取所有的Advisors,从中找出适用于当前Bean的Advisor,如果查找到表示当前Bean需要进行AOP代理,依旧返回原来的Bean对象即可。

Spring会把使用了@AspectJ注解定义的切面包装成Advisor,判断是否有与当前bean匹配的Advisor,判断方式如下:

  1. 根据切点Pointcut的getClassFilter方法对类进行匹配,判断当前Bean的class是否匹配;

  2. 根据切点Pointcut获取MethodMatcher方法匹配器,通过MethodMatcher对当前Bean中的每一个方法进行匹配,也就是使用配置的切点表达式对方法进行匹配;

经过这一步处理,如果匹配到了该Bean的Advisor,说明当前Bean需要进行AOP代理,会返回适用于当前Bean的Advisor集合,接下来会为该Bean创建AOP代理对象。

创建AOP代理对象

创建代理对象

前置知识:JDK动态代理,可参考 【Java】JDK动态代理实现原理

Spring提供了两种方式创建代理对象,分别是JDK动态代理和Cglib,使用JDK动态代理需要被代理对象实现接口,否则使用Cglib实现。

以JDK动态代理为例,创建代理对象的过程在JdkDynamicAopProxy中,它实现了InvocationHandler,在通过JDK的动态代理创建对象的时候,需要这个InvocationHandler,通过Proxy的newProxyInstance即可创建AOP代理对象:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        // 生成代理对象,proxiedInterfaces为目标代理类,this为InvocationHandler也就是当前的JdkDynamicAopProxy
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
}
执行目标方法

在创建了AOP代理对象之后,会使用这个代理对象替换掉原来容器中的Bean,开发过程中拿到的Bean就是这个AOP代理对象了,当执行目标方法时,首先会进入到代理对象的invoke方法(JdkDynamicAopProxy中的invoke方法,InvocationHandler中定义了invoke方法,JdkDynamicAopProxy实现了InvocationHandler接口所以会实现这个方法):

对于AOP,在invoke方法中,会先获取目标方法的所有拦截器,Spring会将适用于当前方法的Advisor转为方法拦截器,然后使用责任链模式,对拦截器进行一个个的调用,当然如果当前方法没有对应的拦截器需要执行,直接通过反射执行目标方法即可。

因为拦截器可以有多个,所以执行拦截器链方法是一个递归调用的过程(在ReflectiveMethodInvocation中实现),它使用了一个变量currentInterceptorIndex记录了当前拦截器的下标:

  1. 判断currentInterceptorIndex是否与拦截器链的大小一致,如果一致说明已经走到了最后一个拦截器,拦截器走完就可以执行目标方法了,此时会通过反射执行目方法;
  2. 如果拦截器链未走完,会对currentInterceptorIndex加1,获取下一个拦截器,继续执行;

举个例子
比如定义了一个切面,里面设置了两个通知,分别是前置通知和环绕通知,要将这个切面作用到某个目标方法,在方法执行前后进行一些操作,Spring会将切面及通知封装为拦截器,在执行目标方法时,拦截器链中就会有两个拦截器,首先执行第一个拦截器,它是一个前置通知,执行完前置通知的方法后,会向后推进进入下一个拦截器:

执行到第二个拦截器,它是一个环绕通知,首先执行环绕通知中的前置操作(环绕通知中目标方法执行之前),运行完毕之后,该方法不会结束,会继续进入拦截器链的处理逻辑,等待目标方法执行之后再继续执行后置操作(环绕通知中目标方法执行之后的操作):

此时已经是拦截器链中最后一个,所以此时可以执行目标方法,执行完目标方法,拦截器链的逻辑已经执行完毕,所以对于第二个拦截器来说,会回到环绕通知中的处理逻辑,开始执行目标方法执行之后的后置操作。

总结
(1)在开启AOP的时候,它会向容器中注册一个AOP的代理对象创建器,它是一个后置处理器,在Spring容器中每个Bean实例化之后,初始化前后会进入到后置处理器对应的方法中,AOP创建代理对象并将原来的Bean替换就是在后置处理器的postProcessAfterInitialization方法中进行的。

(2)在创建AOP代理之前会先判断是否需要为当前Bean创建代理对象,因为并不是所有的Bean都需要进行创建,只有切面中设置要拦截的那些方法所在的Bean才需要创建AOP代理对象,所以一些Spring基础类、使用@Aspect标注的切面本身等Bean都会跳过。

(3)经过上述步骤后,会或获取所有的Advisor,Spring会将创建的切面包装成Advisor,所以可以理解为获取定义的所有切面,从中找出是否有匹配当前Bean的切面,如果有表示需要为Bean创建AOP代理,之后就会根据Bean的信息,比如是否实现了接口,来决定使用JDK动态代理还是Cglib创建代理对象,创建代理对象之后会替换掉原来的Bean,将这个代理对象返回。

(4)代理对象创建完毕之后,执行目标方法时,会进入到代理对象的业务逻辑中,在这里会获取匹配当前目标方法的所有Advisor(比如前置通知、后置通知等)将其转换成一个拦截器链,然后执行拦截器链,执行每个拦截器链的时候会执行对应通知中的方法,当拦截器链执行完毕之后,会通过反射执行真正的目标方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1194428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MES系统数据集成系统源码

MES系统数据集成系统源码 1 MES的生产计划管理综述 生产计划的管理在MES系统中是非常重要的。我们知道&#xff0c;一般的情况下&#xff0c;企业的生产计划是由上层管理系统ERP来完成&#xff0c;那么MES中的生产计划和ERP的生产计划的不同点在于&#xff1a; ERP的生产计划…

选择振弦采集仪进行岩土工程监测时,根据不同工况选择合适的种类

选择振弦采集仪进行岩土工程监测时&#xff0c;根据不同工况选择合适的种类 岩土工程监测是保证工程质量和安全的重要手段。振弦采集仪是一种常用的岩土工程监测仪器&#xff0c;可用于对岩土工程场地振动环境的监测。它具有测量精度高、实时性强、易于安装、使用方便等特点。…

唯众技能大赛公开公平公正践行者

​近日&#xff0c;2023一带一路暨金砖国家技能发展与技术创新大赛网络综合布线赛项成功举办。唯众创始人辜渝傧对本次比赛情况就相关问题回答了记者提问。 采访人&#xff1a;第一个问题&#xff0c;咱们这一次“2023一带一路暨金砖国家技能发展与技术创新大赛之网络综合布线赛…

React【异步逻辑createAsyncThunk(一)、createAsyncThunk(二)、性能优化、createSelector】(十二)

文章目录 异步逻辑 createAsyncThunk&#xff08;一&#xff09; createAsyncThunk&#xff08;二&#xff09; 性能优化 createSelector 异步逻辑 //Product.js const onAdd () > {const name nameRef.current.value// 触发添加商品的事件dispatch(addProduct({name…

基于.NET的强大文件格式开源转换工具

推荐一个非常强大、轻便的强大文件格式转换工具。 01 项目简介 一个基于.NET平台的开源文件格式转换工具&#xff0c;可以支持Windows 7/8/10等操作系统。安装后在右键菜单中出现 “File Converter” 项目&#xff0c;可以方便地通过右键菜单对选中文件进行格式转换&#xff…

HALCON的python下的使用方法(直接开发,不需要调用.hdev文件)

一、环境配置方法 基本要求&#xff1a; Python版本>3.8 ; Halcon版本 >20.11 1&#xff09;首先创建一个python版本大于3.8的基础环境 2&#xff09;然后查看自己的halcon的版本&#xff0c;在该环境下安装halcon 如图所示&#xff0c;版本是20110&#xff0c;执行以…

Java,多线程,线程的两种创建方式

首先是多线程的一些相关概念&#xff1a; 相关概念&#xff1a; 程序&#xff08;program&#xff09;&#xff1a;为完成特定任务&#xff0c;用某种语言编写的一组指令的集合。即指一段静态&#xff08;指不在执行中&#xff09;的代码。 进程&#xff08;process&#xf…

十分钟了解回归测试

1. 什么是回归测试&#xff08;Regression Testing&#xff09; 回归测试是一个系统的质量控制过程&#xff0c;用于验证最近对软件的更改或更新是否无意中引入了新错误或对以前的功能方面产生了负面影响&#xff08;比如你在家中安装了新的空调系统&#xff0c;发现虽然新的空…

34 mysql limit 的实现

前言 这里来看一下 我们常见的 mysql 分页的 limit 的相的处理 这个问题的主要是来自于 之前有一个需要处理 大数据量的数据表的信息, 将数据转移到 es 中 然后就是用了最简单的 “select * from tz_test limit $pageOffset, $pageSize ” 来分页处理 但是由于 数据表的数…

[autojs]逍遥模拟器和vscode对接

第一步&#xff1a;启动autojs服务 第二步&#xff1a;去cmd查看ip地址&#xff0c;输入ipconfig 第三步&#xff1a;打开逍遥模拟器中的sutojs-左上角- 连接电脑&#xff0c;然后输入WLAN或者其他ip也行&#xff0c;根据自己电脑实际情况确认 此时vscode显示连接成功。我们写…

上传ipa到appstore最简单的方法

假如使用原生xcode开发ios的app&#xff0c;可以使用xcode提交打包好的ipa文件到app store&#xff0c;但是假如使用hbuilderx或者apicloud等H5工具开发的app&#xff0c;假如没有mac电脑&#xff0c;是无法将ipa提交到app store的。 因为苹果开发者并不能在线上传ipa到app st…

数据结构-图的应用

最小生成树&#xff08;最小代价树&#xff09; 对于一个带权连通无向图G(V,E)&#xff0c;生成树不同&#xff0c;每棵树的权&#xff08;即树中所有边上的权值之和)也可能不同。设R为G的所有生成树的集合&#xff0c;若T为R中边的权值之和最小的生成树&#xff0c;则T称为G的…

SPI简介及FPGA通用MOSI模块实现

简介 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外围设备接口&#xff09;通讯协议&#xff0c;是Motorola公司提出的一种同步串行接口技术。是一种高速、全双工、同步通信总线。在芯片中只占用四根管脚用来控制及数据传输。 优缺点&#xff1a; SPI通讯协…

VM17虚拟机设置网络,本地使用工具连接虚拟机

VM17虚拟机设置网络&#xff0c;本地使用工具连接虚拟机 下载及安装虚拟机不再说明&#xff0c;网络一堆教程。此处只对VM17设置网路及本地使用工具连接虚拟机操作&#xff0c;进行说明。 我下载的是VM17&#xff0c;网上有说VM16是较稳定的版本。想尝尝鲜&#xff0c;结果耗…

linux系统源码安装php5.6手把手教程

linux系统源码安装php5.6实用教程 1、下载php5.6安装包2、开始安装3、安装成功 1、下载php5.6安装包 wget http://mirrors.sohu.com/php/php-5.6.2.tar.gz在安装之前&#xff0c;我们需要安装php5.6编译时所依赖的软件包。如下&#xff1a; yum -y install gcc gcc-c lib2、开…

docker下的nginx代理转发到tomcat

多次尝试失败原因&#xff0c;修改nginx配置文件以后&#xff0c;需要./nginx.sh -s reload 下&#xff0c;之前一直不转发&#xff0c;好像完全没有跳转的意思&#xff0c;后来查了多篇文档&#xff0c;最简单的方法如下 docker 安装 nginx 和tomcat就不多说了&#xff0c;可…

一文6个步骤带你实现接口测试入门!

一、接口测试概述 1 什么是接口测试&#xff1a; 接口测试是测试系统组件间交互的一种测试。接口测试主要用于检测外部系统与系统之间&#xff0c;内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑…

CLion配置libtorch找不到xxx.dll

项目场景&#xff1a; 使用CLion配置libtorch时遇到该问题 问题描述 使用CLion配置libtorch时&#xff0c;CMakeLists.txt文件写完后&#xff0c;cmake也能成功&#xff0c;但是一旦运行代码就会报错找不到xxx.dll&#xff0c;比如找不到torch_cuda.dll或找不到c10.dll 原因分…

jsp基本表格和简单算法表格

基本表格&#xff1b; <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd…

java项目之果蔬经营平台系统(ssm框架)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的果蔬经营平台系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 管理员&#xff1a;首页、个人…