Spring源码分析之AOP-@EnableAspectJAutoProxy

前言

  这篇文章之前我们说了Springboot的启动流程,Bean对象怎么实现从无到有的一个过程还有一些接口的拓展的实现等等那么从这一篇文章开始的话我们就会开始说一说我们的常用的AOP它的底层实现原理所以大家一起加油加油!!!

AOP:

1.简介:

  AOP的话大家其实就是特别熟悉就算没有使用过的话其实也听过,其实它就是面向切面编程,我的理解就是说将不同模块的公共的逻辑全部都提取出来然后进行一个统一的处理如:日志的打印,权限的管理,其实这么做的目的就是说其实就是减少代码的冗余我在下面写了一个Demo

切面:

@Aspect
@Component
public class AOPDemo {//这个是第一种方法@Pointcut("execution( * com.example.springsimpledemo.Demo.test(..))")public  void testMethod(){};@Before("testMethod()")public  void testDemo(){System.out.println("成功调用AOP方法");}//这个是第二种方法@Before("execution( * com.example.springsimpledemo.Demo.test(..))")public  void testDemo(){System.out.println("成功调用AOP方法");}
}

测试类:

@Component
public class Demo {public  void test(){System.out.println("这个是一个测试的方法");}
}

启动类:

@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringSimpleDemoApplication {public static void main(String[] args) {// 启动 Spring Boot 应用ConfigurableApplicationContext context =SpringApplication.run(SpringSimpleDemoApplication.class, args);Demo bean = (Demo) context.getBean("demo");bean.test();}
}

结果:

那么从上面我们就是可以知道: 整个AOP的组成主要就是说有四部分组成的:

1.切面(添加Aspect注解)这个就是对不同模块中公共的逻辑进行通过处理的地方也就是说进行了模块化的处理管理连接点 切点以及通知(这个就是在哪里干以及干什么 什么时候干 的集合)

2.切点(@pointcut):这个的话其实就是说是一个表达式就是对哪些地方进行AOP的操作可以是一个包或者具体的一个类,这个也是后面用于和连接点进行一个匹配(这个就是在哪里干)

3.通知(advice):这个表示的就是说进行AOP操作的时间有前置后置以及环绕(什么时候干)

4.连接点(joinPoint):这个就是就是正在进行AOP操作的地方,也就是说根据切点匹配之后然后根据通过进行相对应的额外增强的操作,一般就是在方法调用的时候会生效(干的地方)

2.AOP失效的场景:

 我们在上面测试类的基础之上的话进行了一次修改然后我们继续进行测试

新的测试类:

//这个就是添加了一个check方法并且在test方法里面进行方法的调用
@Component
public class Demo {public void check(){System.out.println("在这个测试方法调用前进行校验");}public  void test(){check();System.out.println("这个是一个测试的方法");}
}

测试代码:

@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringSimpleDemoApplication {public static void main(String[] args) {// 启动 Spring Boot 应用ConfigurableApplicationContext context =SpringApplication.run(SpringSimpleDemoApplication.class, args);Demo bean = (Demo) context.getBean("demo");bean.test();}
}

这个其实的话就是会产生一个AOP失效的场景,如果我们按照正常的思路其实我们就是说这个最后的结果里面肯定会有两个输出的结果为成功调用AOP方法但是最终的只会出现一个因为调用test方法的对象是代理对象但是调用test方法里面调用的对象是普通的对象不信的话我们就一起来看一看如下图所示:

解决方案:

  其实现在的话解决方案就是说进行功能之间的拆分


@Component
public class Demo {public void check(){System.out.println("在这个测试方法调用前进行校验");}public  void test(){System.out.println("这个是一个测试的方法");}
}
@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringSimpleDemoApplication {public static void main(String[] args) {// 启动 Spring Boot 应用ConfigurableApplicationContext context =SpringApplication.run(SpringSimpleDemoApplication.class, args);Demo bean = (Demo) context.getBean("demo");bean.check();bean.test();}
}

通过我上面进行拆分之后就能够达到我们想要其得到的最终的效果:

2.原理:

EnableAspectJAutoProxy注解:

 其实这个注解的话就是我们整个AOP的整个核心,下面的话我们就看看这个源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {//这个就是说是否通过CGLB动态代理创建一个代理对象进行动态代理boolean proxyTargetClass() default false;//是否暴露代理对象boolean exposeProxy() default false;}

@Import(AspectJAutoProxyRegistrar.class) 注解引入了AspectJAutoProxyRegistrar 类那么我们再来看看这个源码

@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//如果必要的话那么就是会创建一个自动代理的创建器(这个就是创建代理对象的核心)//1.如果说容器里面不存在或者优先级比较低的话那么肯定是会进行创建的//2.创建出来就是会进行一个优先级的比较的使用的肯定就是说优先级比较高的那一个AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);// 获取 @EnableAspectJAutoProxy 注解AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAspectJAutoProxy != null) {//这个就是解析要解析proxyTargetClass属性if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}//这个就是解析exposeProxy的属性if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}

从上面的源码我们就是可以看出来这个类的主要的目的就是说进行一个自动代理创建器的创建因为这个的作用的话其实见名知意:就是自动为需要进行代理的Bean对象创建代理对象(这个就是简单理解为就是Spring封装的一个工具类而已)下面的话我们就来看一看这个自动代理创建器

registerAspectJAnnotationAutoProxyCreatorIfNecessary:

这个方法经过几次的跳转之后就是到达了AopConfigUtils.registerOrEscalateApcAsRequired()这个方法里面下面的话我们就是来看一看这个里面的方法

@Nullableprivate static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");//如果已经存在的话if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);//这个就是优先级之间的比较if (currentPriority < requiredPriority) {//这个就是修改BeanDefinition对应的class的值apcDefinition.setBeanClassName(cls.getName());}}//如果已经存在的自动代理创建器和将要创建的自动代理创建器一致的话那么就不需要进行如何处理return null;}//不存在的话那么就进行创建就行了RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);//public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}//这个优先级的话就是这个自动代理创建器的BeanClassName在集合中的位置private static int findPriorityForClass(Class<?> clazz) {return APC_PRIORITY_LIST.indexOf(clazz);}private static int findPriorityForClass(@Nullable String className) {for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {Class<?> clazz = APC_PRIORITY_LIST.get(i);if (clazz.getName().equals(className)) {return i;}}throw new IllegalArgumentException("Class name [" + className + "] is not a known auto-proxy creator class");}

从上面我们可以看出来我们这个自动代理创建器的话都是注册为BeanName为"org.springframework.aop.config.internalAutoProxyCreator"的Bean对象但是在AOP的场景下使用的是AnnotationAwareAspectJAutoProxyCreator这个类型的,那么我们只要分析其的源码其实就是可以明白了

AnnotationAwareAspectJAutoProxyCreator:

  上面就是将AspectJAnnotationAutoProxyCreator注册到Spring容器里面然后这个里面的逻辑其实是不多的最重要的逻辑就是在AbstarctAutoProxyCreator

AbstarctAutoProxyCreator:

  这个wrapIfNecessary放法就是创建代理对象的方法但是真正实现代理对象创建是CreateProxy(这个的话我的下篇文章会进行仔细说明),我觉得更多得就是进行判断是否要创建一个代理对象,从Bean的生命周期的话以及循环依赖的话代理对象的创建时间是不一样的,如果在正常的Bean的生命周期的条件下的话那么初始化之后才会创建代理对象但是如果循环依赖的化那么就是在实例化之前就要进行判断是否要创建代理对象

wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {//已经代理过了的话if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}//不需要继续增强if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}//如果是(advice advisor pointcut )或者应该跳过代理的话if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.//这个方法就是说如果存在advice或者advisor(也就是说进行增强的处理)Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);//这个就是创建一个代理对象Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}//如果没有增强的处理的话那么就直接返回普通的对象this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}

下面的话我们就是对需要跳过代理的条件进行判断的依据进行一个判断主要就isInfrastructureClass以及shouldSkip这两个方法进行判断

isInfrastructureClass:
protected boolean isInfrastructureClass(Class<?> beanClass) {//这个就是通过判断这个Bean对象对应的class的类型//如果为 Advice  Pointcut Advisor AopInfrastructureBean//那么就会进行跳过 boolean retVal = Advice.class.isAssignableFrom(beanClass) ||Pointcut.class.isAssignableFrom(beanClass) ||Advisor.class.isAssignableFrom(beanClass) ||AopInfrastructureBean.class.isAssignableFrom(beanClass);if (retVal && logger.isTraceEnabled()) {logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");}return retVal;}
shouldSkip:

 这个方法通过几次跳转之后就到达了AutoProxyUtils.isOriginalInstance这个方法当中然后我们就是看一下这个源码

//这个方法就是用来识别哪些原始的对象(就是原汁原味的对象)//就是没有进行额外的处理的对象就是通过ORIGINAL_INSTANCE_SUFFIX后缀进行区别的static boolean isOriginalInstance(String beanName, Class<?> beanClass) {if (!StringUtils.hasLength(beanName) || beanName.length() !=beanClass.getName().length() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX.length()) {return false;}return (beanName.startsWith(beanClass.getName()) &&beanName.endsWith(AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX));}

这个类既然是创建代理对象的地方那么这里的话我们就是来复习一下在Bean生命周期中是不是就是实例化前(这个是不是就是说提前进行AOP的操作就是为了解决循环依赖的问题)以及初始化之后也会创建实例对象下面的话那么就是简单看一下这个里面的方法:

	@Override@Nullable//初始化之后创建代理对象public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyBeanReferences.remove(cacheKey) != bean) {//这个就是创建的就是一个代理对象return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}//这个就是实例话之前创建代理对象其实判断条件的话就是和wrapIfNecessary一样//这个注解的话那么就是不进行标记了public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {Object cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}// Create proxy here if we have a custom TargetSource.// Suppresses unnecessary default instantiation of the target bean:// The TargetSource will handle target instances in a custom fashion.TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;}

总结:

  如果要进行AOP操作的话那么就是通过@EnableAspectJAutoProxy注解来进行开启(这个也就是一个自动配置的过程就是通过AopAutoConfiguration),就是通过AspectJAutoProxyRegistrar这个类就是创建了注册了自动代理创建器AnnotationAwareAspectJAutoProxyCreator,然后之后的操作的话都是在AnnotationAwareAspectJAutoProxyCreator中进行然后实现创建代理类的话就是通过AbstractAutoProxyCreator中的CreateProxy方法当中然后就是说通过ProxyFactory(下一篇文章会进行仔细的说明)

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

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

相关文章

Linux(Centos 7.6)基本信息查看

1.服务器硬件信息查看 1.1.服务器厂商、产品名称查看 dmidecode -s system-manufacturer&#xff1a;查看服务器厂商信息 dmidecode -s system-product-name&#xff1a;查看服务器产品名称信息 1.Windows使用VMware安装的Linux(Centos 7.6)后&#xff0c;服务器厂商、产品名…

小程序app封装公用顶部筛选区uv-drop-down

参考ui:DropDown 下拉筛选 | 我的资料管理-uv-ui 是全面兼容vue32、nvue、app、h5、小程序等多端的uni-app生态框架 样式示例&#xff1a; 封装公用文件代码 dropDownTemplete <template><!-- 顶部下拉筛选区封装公用组件 --><view><uv-drop-down ref&…

LeetCode:101. 对称二叉树

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;101. 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输…

Docker-如何启动docker

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 我们在上一章&#xff0c;讲了虚拟化&#xff0c;虚拟化是把硬件虚拟化&#xff0c;然后创建出来的虚拟机完全隔离&#xff…

COMSOL with Matlab

文章目录 基本介绍COMSOL with MatlabCOMSOL主Matlab辅Matlab为主Comsol为辅 操作步骤常用指令mphopenmphgeommghmeshmphmeshstatsmphnavigatormphplot常用指令mphsavemphlaunchModelUtil.clear 实例教学自动另存新档**把语法套用到边界条件**把语法套用到另存新档 函数及其微分…

JVM系列(十二) -常用调优命令汇总

最近对 JVM 技术知识进行了重新整理&#xff0c;再次献上 JVM系列文章合集索引&#xff0c;感兴趣的小伙伴可以直接点击如下地址快速阅读。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的加载过程JVM系列(三) -内存布局详解JVM系列(四) -对象的创建过程JVM系列(五) -对象的内存分…

bmp390l传感器的IIC命令通信(学习汇总)

参考链接&#xff1a; BMP390高精度压力传感器数据读取与处理&#xff08;基于STM32&#xff09;-CSDN博客 https://blog.csdn.net/qq_43862401/article/details/106502397 利用usb转iic模块测试bmp390l传感器采集当前环境的温度和气压数据&#xff0c;下图中reserved表示…

Windows11 家庭版安装配置 Docker

1. 安装WSL WSL 是什么&#xff1a; WSL 是一个在 Windows 上运行 Linux 环境的轻量级工具&#xff0c;它可以让用户在 Windows 系统中运行 Linux 工具和应用程序。Docker 为什么需要 WSL&#xff1a; Docker 依赖 Linux 内核功能&#xff0c;WSL 2 提供了一个高性能、轻量级的…

2025系统架构师(一考就过):案例题之一:嵌入式架构、大数据架构、ISA

一、嵌入式系统架构 软件脆弱性是软件中存在的弱点(或缺陷)&#xff0c;利用它可以危害系统安全策略&#xff0c;导致信息丢失、系统价值和可用性降低。嵌入式系统软件架构通常采用分层架构&#xff0c;它可以将问题分解为一系列相对独立的子问题&#xff0c;局部化在每一层中…

新手SEO指南如何快速入门与提升网站排名

内容概要 搜索引擎优化&#xff08;SEO&#xff09;是提高网站可见度和排名的重要手段&#xff0c;尤其对新手来说&#xff0c;掌握其基本概念和实用技巧至关重要。本文将针对新手提供一系列的指导&#xff0c;帮助你快速入门并逐步提升网站排名。 首先&#xff0c;了解SEO的…

Oracle下载安装(保姆级教学)

方法1 1. 官网下载安装包 对于 Oracle 软件的下载&#xff0c;建议通过官网免费下载&#xff0c;安全且有保证。 下载地址&#xff1a; https://www.oracle.com/database/technologies/oracle19c-windows-downloads.html 通过下载页面可以选择安装压缩包&#xff08; WIND…

第22天:信息收集-Web应用各语言框架安全组件联动系统数据特征人工分析识别项目

#知识点 1、信息收集-Web应用-开发框架-识别安全 2、信息收集-Web应用-安全组件-特征分析 一、ICO图标&#xff1a; 1、某个应用系统的标示&#xff0c;如若依系统有自己特点的图标&#xff1b;一旦该系统出问题&#xff0c;使用该系统的网站都会受到影响&#xff1b; 2、某个公…

重温设计模式--建造者模式

文章目录 建造者模式&#xff08;Builder Pattern&#xff09;概述建造者模式UML图作用&#xff1a;建造者模式的结构产品&#xff08;Product&#xff09;&#xff1a;抽象建造者&#xff08;Builder&#xff09;&#xff1a;具体建造者&#xff08;Concrete Builder&#xff…

arcgisPro相接多个面要素转出为完整独立线要素

1、使用【面转线】工具&#xff0c;并取消勾选“识别和存储面邻域信息”&#xff0c;如下&#xff1a; 2、得到的线要素&#xff0c;如下&#xff1a;

51单片机仿真摇号抽奖机源程序 12864液晶显示

资料下载地址&#xff1a;51单片机仿真摇号抽奖机源程序12864液晶显示仿真程序 一、功能介绍 单片机连接12864&#xff08;st7920&#xff09;液晶显示器和1个按键接INT0&#xff0c;模拟一个抽奖机。 实现效果&#xff1a; 1、液晶初始显示“祝你好运&#xff01;”&#xff…

如何在 Ubuntu 22.04 上安装 Graylog 开源日志管理平台

简介 Graylog 的开源特性、丰富的功能、灵活性和可扩展性使其成为一个流行的日志管理平台。在本教程中&#xff0c;我将向你展示如何在 Ubuntu 22.04 上安装 Graylog&#xff0c;包括配置 Graylog 服务器软件包和访问 Graylog Web UI。 Graylog 是什么&#xff1f; Graylog …

技术发展历程:从 CORBA 到微服务

CORBA CORBA&#xff08;Common Object Request Broker Architecture&#xff09;诞生于上世纪 90 年代初期&#xff0c;由 OMG 组织提出&#xff0c;它作为一种开创性的分布式对象技术规范&#xff0c;在当时的计算机领域引起了轰动。其核心构成部分——接口定义语言&#xf…

抢单人机交互「新红利」!哪些细分赛道“多金”?

受终端用户的智能座舱体验需求驱动&#xff0c;视觉、听觉、触觉等人机交互方式加速焕新。 一方面&#xff0c;人机多模交互引领&#xff0c;车载声学进入新周期。根据高工智能汽车研究院统计数据&#xff0c;单车的车载扬声器搭载量正在快速起量。 很显然&#xff0c;作为智…

前端学习DAY26(华为平板页面)

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>平板图片</title><style> .box{text-al…

算法 双指针技巧

文章目录 双指针[leetcode167 两数之和](https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/)分析题解 [leetcode88 合并两个有序数组](https://leetcode.cn/problems/merge-sorted-array/description/)分析题解 [leetcode142 环形链表](https://lee…