Aop源码分析-程序员宅基地

技术标签: spring  java  Powered by 金山文档  前端  springboot  

      <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>

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

Aop:指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编码方式;

Aop:的底层就是动态代理

public class Sp{

    public int add(int a,int b){
        int res=1/0;
        return a+b;
    }
}
//让spring知道这是一个切面类
@Aspect
public class Linux {


    //切入点 *表示任何方法 ..表示所有参数
    @Pointcut("execution(public int com.dmg.Sp.*(..))")
    public void pointcut(){

    }

    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        //方法名称
        String methodName=joinPoint.getSignature().getName();
        //参数
        Object[]args=joinPoint.getArgs();
        System.out.println(methodName+"方法,前置通知:,参数:"+ Arrays.asList(args));
    }


    @After("pointcut()")
    public void after(JoinPoint joinPoint){
        //方法名称
        String methodName=joinPoint.getSignature().getName();
        //参数
        Object[]args=joinPoint.getArgs();
        System.out.println(methodName+"方法,后置通知:,参数:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "pointcut()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        //方法名称
        String methodName=joinPoint.getSignature().getName();
        //参数
        Object[]args=joinPoint.getArgs();
        System.out.println(methodName+"方法,返回通知:,参数:"+ Arrays.asList(args)+",结果:"+result);
    }

    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        //方法名称
        String methodName=joinPoint.getSignature().getName();
        //参数
        Object[]args=joinPoint.getArgs();
        System.out.println(methodName+"方法,异常通知:,参数:"+ Arrays.asList(args)+",异常信息:"+e);
    }
}
//开启aop代理
@EnableAspectJAutoProxy
@Configuration
public class TestConfig {



    @Bean
    public Linux linux(){
        return new Linux();
    }

    @Bean
    public Sp sp(){
        return new Sp();
    }
}


public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestConfig.class);
        Sp sp = applicationContext.getBean(Sp.class);
        int res = sp.add(11, 22);
        System.out.println(res);
    }
}

执行之后,打印了前置通知,后置通知,和异常通知,也拿到了方法的名字,参数和异常信息

我们吧int res=1/0;注释掉

可以看到这次拿到了结果

我们进入@EnableAspectJAutoProxy源码

在这里会导入一个AspectJAutoProxyRegistrar类到容器中,我们点进去

在这里会注册bean定义的信息

进入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法

注册切面注解自动代理必要时创建

然后进入AnnotationAwareAspectJAutoProxyCreator类

创建注解增强切面自动代理

在这个initBeanFactory方法 初始化bean工厂

我们进入AnnotationConfigApplicationContext 注解配置应用程序上下文的源码

传入配置类,创建ioc容器

注册配置类

调用刷新方法,刷新环境变量,刷新容器

进入refresh()方法,进入this.registerBeanPostProcessors(beanFactory);

注册bean后置处理器

找到PostProcessorRegistrationDelegate类的registerBeanPostProcessors方法这里

先获取ioc容器已经定义了的需要创建对象的索引BeanPostProcessor

给容器中加别的BeanPostProcessor

优先注册实现了PriorityOrdered接口的xxxBeanPostProcessor

在给容器中注册实现了Ordered接口的xxxBeanPostProcessor

注册没实现优先级接口的xxxBeanPostProcessor

然后进入AbstractAutowireCapableBeanFactory抽象自动注入有能力的bean工厂类的

doCreateBean方法

创建bean对象

给bean的各种属性赋值

初始化bean

进入initializeBean方法

处理Aware接口的方法回调

应用后置处理器,初始化之前的操作

执行自定义的初始化方法,比如说@Bean(这里设置初始化方法,销毁方法)

执行后置处理器的,初始化之后的方法

进入invokeAwareMethods方法

判断是否实现的bean工厂的增强接口,然后进入对方的类设置bean工厂

然后进入AbstractAdvisorAutoProxyCreator 抽象顾问自动代理创建器类

的setBeanFactory方法

然后这个时候走到初始化bean工厂的时候,就进入了

AnnotationAwareAspectJAutoProxyCreator注解增强切面自动代理创建器类的initBeanFactory方法

这个类就是我们之前使用开启aop代理哪个类,对应注册到bean定义的一个类

BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功

然后在回到PostProcessorRegistrationDelegate后置处理程序注册委托类的

registerBeanPostProcessors方法

把后置处理程序注册到bean工厂中

上面都是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程

接下来我们进入AbstractAutoProxyCreator 抽象自动代理创建器类的

postProcessBeforeInstantiation方法 后置处理器在实例化之前处理

他继承了InstantiationAwareBeanPostProcessor类

AbstractApplicationContext类中的这一行

this.finishBeanFactoryInitialization(beanFactory);

完成bean工厂初始化工作,创建剩下的单实例bean

点进去 在这一行

beanFactory.preInstantiateSingletons();

创建单例bean

遍历获取容器中所有的Bean,依次创建对象getBean(beanName)

在getBean方法里面又调用了doGetBean

Object sharedInstance = this.getSingleton(beanName);这一行

先从缓存中获取bean是否存在,如果存在,说明已经创建过了,直接使用,

只要创建好的bean,都会被缓存起来

如果没有创建,才去创建

this.createBean这里创建bean

在resolveBeforeInstantiation方法 解析实例化之前操作

希望后置处理器在此能返回一个代理对象,如果能返回代理对象就使用,如果不能就继续

调用doCreateBean 去创建bean 这里才是真正的实例化bean,就和我们上面的结合上了

createBeanInstance 创建bean实例

populateBean bean的属性赋值

initializeBean 初始化bean

invokeAwareMethods 执行增强方法

applyBeanPostProcessorsBeforeInitialization 执行后置处理器初始化之前的操作

applyBeanPostProcessorsAfterInitialization 执行后置处理器初始化之后的操作

进入resolveBeforeInstantiation方法

在这里先执行bean后置处理器实例化之前拿到bean

如果bean不为空的时候,在执行bean后置处理初始化之后的方法

进入applyBeanPostProcessorsBeforeInstantiation方法

可以看到这里判断是否是InstantiationAwareBeanPostProcessor 这个类实例化增强bean后置处理器,然后执行对应的postProcessBeforeInstantiation方法

然后又回到了这里

在这里关心linux的创建,判断当前bean是否在advisedBeans中(保存了所有需要增强bean)

isInfrastructureClass这里判断是否是基础类型的bean,如果是放入advisedBeans中

在这里会判断是否带着@Aspect这个注解

首先会进入wrapIfNecessary方法

然后再进入getAdvicesAndAdvisorsForBean方法

找到4个增强的通知方法

然后进入findAdvisorsThatCanApply方法

然后进入canApply方法 来看增强器是否匹配

sortAdvisors这里给增强器排序

把找到的增强bean返回到一个数组,然后再放入增强beans中

如果不为空,那么创建代理对象

获取所有增强器(通知方法),放入到代理工厂中

用代理工厂帮我们创建对象

创建Aop的代理对象

spring会自动判断选择cglib动态代理,还是jdk动态代理

最后我们可以看到拿到的是cglib动态代理

给容器中返回当前组件使用cglib增强了的代理对象

以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程

接下来我们在看下目标方法执行,也就是sp.add这个方法之前,先拦截

容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象)

然后进入cglib的拦截方法,在这一行

List<Object>chain=this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

根据代理工厂对象,获取将要执行的目标方法拦截器链

如果没有拦截器链,那么直接执行目标方法

如果有拦截器链,那么执行cglib代理的方法,然后去执行我们设置好的前置,后置,返回,异常方法,并吧结果返回给retVal

我们进入getInterceptorsAndDynamicInterceptionAdvice获取拦截器链的方法

如果缓存为空,那么

进入增强链工厂的getInterceptorsAndDynamicInterceptionAdvice方法

遍历所有的增强器,然后转成拦截器,放入拦截器集合

拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

接下来我们在来看下拦截器链的触发过程

进入ReflectiveMethodInvocation反射方法调用类的proceed方法

currentInterceptorIndex 当前拦截器的索引

如果不相等,那么继续进入proceed方法

每次执行proceed方法,当前拦截器的索引就会自增1

先打印前置通知

如果当前拦截器的索引=拦截器链的集合-1,那么通过反射调用目标方法

也就是执行我们的后置,异常,返回通知方法

拦截器链的触发过程

如果没有拦截器执行目标方法,或者拦截器的索引和拦截器数组大小一样,(指定到了最后一个拦截器),执行目标方法

链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行

Aop总结

1.使用@EnableAspectJAutoProxy 开启Aop功能;

2.@EnableAspectJAutoProxy会给容器注册一个组件,AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;

3. registerBeanPostProcessors()注册后置处理器,创建AnnotationAwareAspectJAutoProxyCreator;

4.finishBeanFactoryInitialization() 初始化剩下的单实例bean;

5. 创建业务逻辑组件和切面组件;

6. AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程;

7.组件创建完之后,判断组件是否需要增强;

8.如果是需要增强,切面的通知方法,包装成增强器(Advisor),给业务逻辑组件创建一个代理对象(cglib);

9.代理对象执行目标方法

10.得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)

11.利用拦截器的链式机制,依次进入每一个拦截器进行执行;

12.正常执行:前置通知-》目标方法-》返回通知-》后置通知

13.出现异常:前置通知-》目标方法-》异常通知-》后置通知

大白话Aop总结;

aop就是代理一个方法,然后在目标方法执行之前,做一些拦截的处理;

1.使用@EnableAspectJAutoProxy 开启Aop功能;

2.在切面类加入@Aspect让spring知道这是一个切面类;

3.在完成bean工厂初始化中,会判断是否使用@Aspect这个注解;

4.获取所有的增强器(通知方法),放入到代理工厂中,使用cglib创建Aop动态代理对象;

5.获取拦截器链,然后执行通知方法;

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_21277357/article/details/128881172

智能推荐

leetcode 172. 阶乘后的零-程序员宅基地

文章浏览阅读63次。题目给定一个整数 n,返回 n! 结果尾数中零的数量。解题思路每个0都是由2 * 5得来的,相当于要求n!分解成质因子后2 * 5的数目,由于n中2的数目肯定是要大于5的数目,所以我们只需要求出n!中5的数目。C++代码class Solution {public: int trailingZeroes(int n) { ...

Day15-【Java SE进阶】IO流(一):File、IO流概述、File文件对象的创建、字节输入输出流FileInputStream FileoutputStream、释放资源。_outputstream释放-程序员宅基地

文章浏览阅读992次,点赞27次,收藏15次。UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1。注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。UTF-8字符集:汉字占3个字节,英文、数字占1个字节。GBK字符集:汉字占2个字节,英文、数字占1个字节。GBK规定:汉字的第一个字节的第一位必须是1。_outputstream释放

jeecgboot重新登录_jeecg 登录自动退出-程序员宅基地

文章浏览阅读1.8k次,点赞3次,收藏3次。解决jeecgboot每次登录进去都会弹出请重新登录问题,在utils文件下找到request.js文件注释这段代码即可_jeecg 登录自动退出

数据中心供配电系统负荷计算实例分析-程序员宅基地

文章浏览阅读3.4k次。我国目前普遍采用需要系数法和二项式系数法确定用电设备的负荷,其中需要系数法是国际上普遍采用的确定计算负荷的方法,最为简便;而二项式系数法在确定设备台数较少且各台设备容量差..._数据中心用电负荷统计变压器

HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板_网页设计成品百度网盘-程序员宅基地

文章浏览阅读7k次,点赞4次,收藏46次。HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业_网页设计成品百度网盘

【Jailhouse 文章】Look Mum, no VM Exits_jailhouse sr-iov-程序员宅基地

文章浏览阅读392次。jailhouse 文章翻译,Look Mum, no VM Exits!_jailhouse sr-iov

随便推点

chatgpt赋能python:Python怎么删除文件中的某一行_python 删除文件特定几行-程序员宅基地

文章浏览阅读751次。本文由chatgpt生成,文章没有在chatgpt生成的基础上进行任何的修改。以上只是chatgpt能力的冰山一角。作为通用的Aigc大模型,只是展现它原本的实力。对于颠覆工作方式的ChatGPT,应该选择拥抱而不是抗拒,未来属于“会用”AI的人。AI职场汇报智能办公文案写作效率提升教程 专注于AI+职场+办公方向。下图是课程的整体大纲下图是AI职场汇报智能办公文案写作效率提升教程中用到的ai工具。_python 删除文件特定几行

Java过滤特殊字符的正则表达式_java正则表达式过滤特殊字符-程序员宅基地

文章浏览阅读2.1k次。【代码】Java过滤特殊字符的正则表达式。_java正则表达式过滤特殊字符

CSS中设置背景的7个属性及简写background注意点_background设置背景图片-程序员宅基地

文章浏览阅读5.7k次,点赞4次,收藏17次。css中背景的设置至关重要,也是一个难点,因为属性众多,对应的属性值也比较多,这里详细的列举了背景相关的7个属性及对应的属性值,并附上演示代码,后期要用的话,可以随时查看,那我们坐稳开车了······1: background-color 设置背景颜色2:background-image来设置背景图片- 语法:background-image:url(相对路径);-可以同时为一个元素指定背景颜色和背景图片,这样背景颜色将会作为背景图片的底色,一般情况下设置背景..._background设置背景图片

Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏8次。Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程

PyCharm2021安装教程-程序员宅基地

文章浏览阅读10w+次,点赞653次,收藏3k次。Windows安装pycharm教程新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入下载安装PyCharm1、进入官网PyCharm的下载地址:http://www.jetbrains.com/pycharm/downl_pycharm2021

《跨境电商——速卖通搜索排名规则解析与SEO技术》一一1.1 初识速卖通的搜索引擎...-程序员宅基地

文章浏览阅读835次。本节书摘来自异步社区出版社《跨境电商——速卖通搜索排名规则解析与SEO技术》一书中的第1章,第1.1节,作者: 冯晓宁,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.1 初识速卖通的搜索引擎1.1.1 初识速卖通搜索作为速卖通卖家都应该知道,速卖通经常被视为“国际版的淘宝”。那么请想一下,普通消费者在淘宝网上购买商品的时候,他的行为应该..._跨境电商 速卖通搜索排名规则解析与seo技术 pdf

推荐文章

热门文章

相关标签