IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 对Spring AOP的进一步深入理解 -> 正文阅读

[Java知识库]对Spring AOP的进一步深入理解

前言

从AOP的开启,到切面生成,再到代理类注入,最后增强方法的调用整个流程做一个整理和理解。将Spring AOP功能整体串联起来。

@EnableAspectJAutoProxy开启AOP功能

前面已经研究过这个注解原理:Spring之@EnableAspectJAutoProxy开启AOP功能原理
简单来说,就是这个注解通过@Import注解向Spring容器注入了一个BeanDefinition对象,这个BeanDefinition对象实例化后是AnnotationAwareAspectJAutoProxyCreator类,即AOP的入口类,这个类是一个BeanPostProcessor类。

AOP入口类工作原理

AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor类。那么在Spring容器bean实例化过程中,会调用postProcessBeforeInitialization方法和postProcessAfterInitialization方法。AOP入口是在postProcessAfterInitialization方法切入的,下面看这个方法源码:

@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {//这里的判断跟循环依赖有关,如果涉及到循环依赖,则在这里之前就生成了循环依赖对象的代理对象。这里不考虑这种情况,直接执行wrapIfNecessary方法
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

下面看wrapIfNecessary方法源码:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//           ......省略......
		// Create proxy if we have advice.
		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;
		}

		//  ......省略......
	}

截取重点代码,可以看到,首先收集正在实例化bean的Advisors(切面)和Adivces(增强),如果收集到了,则调用createProxy方法创建一个代理对象并返回。道理都懂,那么这个返回的代理对象,是如何与收集到的Advisors(切面)和Adivces(增强)交互的呢?这就需要查看createProxy方法源码:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		//  ...........省略..............

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
		return proxyFactory.getProxy(classLoader);
	}

可以看出,这里找出了bean对应的所有advisors对象,然后在生成代理对象定义增强时,执行增强代码。生成的代理对象返回给Spring容器中。

调用代理对象方法

调用代理对象的方法,最终走到的是ReflectiveMethodInvocation类的process()方法,该方法里维护了代理对象每个方法的一个拦截器链,拦截器链就是对原方法的增强。具体流程参考另一篇博客:Spring之Joinpoint类详解

这里需要解释的一点就是,为何代理方法执行的时候会执行ReflectiveMethodInvocation的process方法呢?一定是在生成代理对象时,不管是jdk代理对象还是cglib代理对象,设置增强的回调函数时,设置成了ReflectiveMethodInvocation的process方法。拦截器链也是在设置回调函数时,根据获取到的advisors生成了拦截器链,只不过这部分代码,没有继续追踪源码,而是猜想而来的。Spring一定是按照这个逻辑,进行的AOP代理。

综上所述,AOP的核心思想就是代理模式的应用。以BeanPostProcessor为切入,生成bean的代理对象。获取到代理对象的切面,并在代理的回调函数中执行切面中的增强方法。这样,在bean实例化完成后,调用其方法时,就走回调函数,然后根据拦截器链,执行增强方法。

感悟

上面讲到,通过 @EnableAspectJAutoProxy开启AOP功能的本质,就是这个注解@Import到Spring容器中一个BeanPostProcessor,这个BeanPostProcessor具有AOP功能。其他开启功能的注解思路和这个都一致。本质就是往Spring容器中添加bean,然后这些bean就具备了要开启的功能。
只不过添加bean的方式略有不同。比如可以通过registry BeanDefinition方式加入,也可以通过FactoryBean方式加入,或者直接将BeanPostProcessor加入Spring容器等等。这些不同方式的加入,都是由开启功能的注解中@Import进来的类而决定的。

@Async中AOP的应用

我们自定义切面时,通过@Aspect来标注切面。那么第三方的注解,如@Async、@Transaction、@Cache等注解,是如何进行AOP操作的呢?不同注解实现方式略有不同,但是核心思想都是收集这些注解,然后生成注解所在类的切面。切点就是注解修饰的方法,而增强就是注解要增强的功能逻辑。以@Async为例进行说明:
具体原理参考:一文彻底讲透@Async注解的原理和使用方法

简单来说,就是通过@EnableAsync注解开启异步功能,这个注解将AsyncAnnotationBeanPostProcessor类加入了Spring容器,而这个类里,自己创建了Advisor,并设置了增强Advice。而切点PointCut就是注解修饰的方法。自己生成切面后,在AOP收集切面时,就会收集到这个切面,然后在实例化bean时,如果这个bean有切面,则生成代理对象,代理方法的增强就是@Async定义的异步增强。

画外音

之前一直认为一个对象,如果有接口,AOP生成代理对象时就用jdk动态代理,如果没有接口,则就用cglib代理。而在研究本篇博客内容时发现,在SpringBoot项目中,无论是否有接口,默认生成的代理对象都是cglib代理对象。查阅网上资料发现是在SpringBoot2.x版本,默认都使用了cglib代理来规避jdk动态代理面向接口代理而产生的一些问题。详情自行百度即可,这里只是记录一下这个情况。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:12:55  更:2022-09-21 00:17:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:45:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码