Spring源码学习篇之AOP-1

Spring AOP源码解读 aop 是什么 AOP (Aspect-Oriented Programming,面向切面编程),可以说是 OOP 的补充和完善。OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP 则显得无能为力。OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布 阅读详情

开启AOP相关注解,xml中配置如下:

<aop:aspectj-autoproxy />

1、AOP 相关组件注册

在这里插入图片描述
spring容器启动时,将会解析配置文件中的指令节点逐一解析,配置了aspectj-autoproxy指令后,在refresh()中的obtainFreshBeanFactory()阶段解析XML配置时,当解析到该指令时,触发AspectJAutoProxyBeanDefinitionParser解析器对其进行解析。

主要由AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary()注册AOP相关注解组件并通知,便于监听器进一步处理。

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
		ParserContext parserContext, Element sourceElement) {
	// 注册或升级AutoProxyCreator定义beanName为
	BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			parserContext.getRegistry(), parserContext.extractSource(sourceElement));

	//对于proxy-target-class以及expose-proxy属性的处理
	useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);

	//注册组件并通知,便于监听器进一步处理
	//其中beanDefinition的className为AnnotationAwareAspectJAutoProxyCreator
	registerComponentIfNecessary(beanDefinition, parserContext);
}

2、AOP 创建代理

在上文组件注册章节中已经完成了AnnotationAwareAspectJAutoProxyCreator.class类型的bean注册。在其类层次结构中看出实现了BeanPostProcessor接口,即等于是bean的增强器。
在这里插入图片描述
所以我们只需关注bean初始化前后的两个增强逻辑(postProcessBeforeInitialization,postProcessAfterInitialization)即可。

2.1、postProcessBeforeInstantiation方法:

在该方法中,首先判断是否被处理,若被处理,则判断设置是否代理,紧接着判断是否基础设施类或者是否需要跳过该bean,而跳过逻辑中:根据切点增强方法中的切面名称是否和该bean name一致,一致则跳过。

若未被处理,则根据自定义bean creator创建bean,最后根据切面中方法判断是否要创建该bean代理。

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
	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;
}

注意:跳过判断方法中解析了切面中的方法,并添加缓存。下方贴出shouldSkip()代码:

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
	// TODO: Consider optimization by caching the list of the aspect names
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	for (Advisor advisor : candidateAdvisors) {
		//若和切面名称一致,则跳过,无需创建代理类
		if (advisor instanceof AspectJPointcutAdvisor &&
				((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
			return true;
		}
	}
	return super.shouldSkip(beanClass, beanName);
}
protected List<Advisor> findCandidateAdvisors() {
	// Add all the Spring advisors found according to superclass rules.
	// 当时用注解方式配置AOP时,并不是丢弃了对XML的支持
	// 在这里调用父类方法加载配置中的AOP声明
	List<Advisor> advisors = super.findCandidateAdvisors();
	// Build Advisors for all AspectJ aspects in the bean factory.
	if (this.aspectJAdvisorsBuilder != null) {
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	}
	return advisors;
}
public List<Advisor> buildAspectJAdvisors() {
	List<String> aspectNames = this.aspectBeanNames;
	//double check lock
	if (aspectNames == null) {
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new ArrayList<>();
				aspectNames = new ArrayList<>();
				//获取所有的bean names
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Object.class, true, false);
				for (String beanName : beanNames) {
					Class<?> beanType = this.beanFactory.getType(beanName);
					// 如果存在aspect注解
					if (this.advisorFactory.isAspect(beanType)) {
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
							MetadataAwareAspectInstanceFactory factory =
									new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
							// 解析标记AspectJ注解中的 增强方法
							List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
							advisors.addAll(classAdvisors);
						}
					}
				}
				this.aspectBeanNames = aspectNames;
				return advisors;
			}
		}
	}
	return advisors;
}
/**
 * 解析 标记AspectJ注解bean中的  增强方法
 */
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
	// 获取标记AspectJ的类
	Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
	// 获取标记AspectJ的切面名称
	String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
	List<Advisor> advisors = new ArrayList<>();
	// getAdvisorMethods(aspectClass) 获取所有切面中非@PiontCut的方法
	for (Method method : getAdvisorMethods(aspectClass)) {
		/**
		 * 1、获取普通增强器的逻辑
		 * 		1.1、对切点的注解的获取
		 * 		1.2、根据不同的注解信息生成不同的增强(before、around、after...)
		 */
		Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
		if (advisor != null) {
			advisors.add(advisor);
		}
	}
	return advisors;
}

总之:在bean初始化前的增强逻辑中,就会把所有的切面及切面内的增强方法全部解析完成,并加入缓存LIST中,为后续的bean后置增强中创建代理提供支持。

2.2 postProcessAfterInitialization方法

bean的后置增强,则是在初始化bean属性后,如果需要代理,则返回代理后的bean。

🎈在这之前先介绍实例化的初始化(initializeBean)阶段,在此阶段将执行bean前置和后置逻辑增强。代码如下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// 前置增强
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}
		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			// 后置增强——在此处生成代理类如果需要的话
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}

后置增强方法

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			// 如果适合被代理,则需要封装指定bean
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	// 如果已经被处理过
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	// 无需增强的bean
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	// 基础设施类无需被代理,或者配置了指定的bean,不需要自动代理
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	// 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;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

针对不同的模式选择是JDK代理还是CGLIB代理。

该类是否继承接口,存在接口继承则使用JDK代理(生成接口的代理实现类),否则使用CDLIB进行代理(生成完成继承该类所有非final方法的兄弟类)。

注意:若是当前bean不存在实现接口,则直接采用CGLIB生成代理。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

后续学习深入后再进行补充!!!

Spring源码——AOP 前言 内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。 本文主要涉及书中第七章的部分,依照书中内容以及个人理解对Spring源码进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework 正式进入正文前,强烈推荐先阅读文末参考阅读第一篇:关于 S... 阅读详情

相关推荐

Spring之aop源码解析

aop就是面向切面编程,是为了解耦而生。aop是不仅工作常用而且面试也经常问,所以看aop源码是非常有必要的。在看源码之前,我们先要带着结论去看源码,而不是一上来看的一脸懵逼,一定要先总后分。aop的流程就是在spring的生命周期过程中,会缓存所有的切面,然后在bean初始化的时候,它会找当前bean的所有方法是否跟缓存中的切面有匹配的,如果匹配,说明需要代理并创建代理对象,当调用被增强方法时,执行代理逻辑。本文基于springboot2.3.5.RELEASE,如果有错,欢迎指出。

weixin_53067547的博客 1175

Spring中AOP相关的API及源码解析,原来AOP是这样子的

Spring中AOP相关的API及源码解析 本系列文章: 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring中的对象跟Bean,你知道Spring怎么创建对象的吗? 这篇文章,我们来谈一谈Spring中的属性注入 推荐阅读: Spring官网阅读 | 总结篇 Spring杂谈 本系列文章将会带你一行行的将Spring的源码吃透,推荐阅读的文章是阅读源码的基础! 因为本文会涉及到动态代理的相关内容,如果对动态代理不

做一个认真的程序员 1万+

Spring的aop源码分析

Spring的aop源码解析

qq_43418737的博客 1668

Spring AOP 源码解读

name: spring-aop-source title: Spring AOP 源码解析 date: 2020-03-25 15:03:38 tags: spring-aop-source categories: 文章目录前言本文使用的调试代码IOC 容器管理 AOP 实例ProxyFactory 详解基于注解的 Spring AOP 源码分析闲聊 InstantiationAwareBeanPostProcessor小结 之前写过 IOC 的源码分析,那篇文章真的有点长,看完需要点耐心。很多读者希.

孤刺 4074

第二十四章 Spring之源码阅读——AOP篇

对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了所以决定花点时间研究研究Spring的源码。《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》前边A君把AOP集成到了IOC容器中,Spring中最基础,也是最重要的两部分内容都整完了。不知道对大家是否有所帮助?不过,这AOP部分,A君换了个写法,之前在写IOC篇章的时候,A君。

一个卑微的java开发 1384

目标检测技术的发展:从R-CNN、YOLO到DETR、DINO

OV-DINO的出现标志着在开放域目标检测领域的重大进步,其在处理未见过的物体类别时的能力得到了显著提升。这项技术对于需要适应未知或变化环境的应用具有重要意义,如自动驾驶、机器人视觉、安防监控等。

DeepAIedu的博客 2629

Spring源码之AOP实现

Spring源码之AOP

qq_38706652的博客 2784

S7-200 Smart 控制运动轴(脉冲电机/伺服)教程

S7-200 Smart 控制运动轴(脉冲电机/伺服) 结合本人刚接触触摸屏时候的踩坑经验,编写S7-200 Smart 控制运动轴(脉冲电机/伺服)的详细流程,给刚接触PLC的朋友提供一些经验指导。 本文详细介绍了S7-200 Smart 控制脉冲步进/伺服电机流程,包含接线方法及梯形图。希望对初学者有所帮助

xjbsw0的博客 5万+

Spring AOP源码详细解析

一 . 以下是bean的初始化过程: 注意: 先明白beanPostProcessor的作用,才能够明白aop的加载时机 在bean实例化完成之前和完成之后分别会自动BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法。 二: 介绍一些Spring Aop中一些核心类,大致分为三类: advisorCreator,继承 spring ioc的扩展接口 beanPos...

pengjianglilive的专栏 3678

Hypermesh到Abaqus CEL网格转换全攻略:解决欧拉体导入难题

本文详细解析了Hypermesh到Abaqus CEL网格转换的全流程,重点解决欧拉体导入难题。通过单元类型转换、INP文件编辑和多组件合并技术,实现流固耦合仿真的高效前处理。文章包含实用脚本和工程案例,帮助工程师突破ABAQUS CEL分析中的网格转换瓶颈。

weixin_30832143的博客 189

新手必看:如何用Python模拟电动助力转向系统(EPS)的传感器信号处理

本文详细介绍了如何使用Python模拟电动助力转向系统(EPS)的传感器信号处理流程,包括转向力矩、转向角度和车速信号的生成与滤波处理。通过实战代码示例,帮助开发者理解ECU如何计算助力扭矩,并提供了可视化分析和性能优化建议,适合汽车电子系统开发新手学习。

weixin_28327051的博客 354

Spring 源码学习资料 从手写到源码深度剖析

通过这套 Spring 源码学习资料,大家可以从基础到高级,系统地学习 Spring 框架的核心技术和实现原理。无论是手写代码还是深入源码,这些资料都将帮助大家更好地理解和使用 Spring 框架,提升开发效率和代码质量。如果你对 Spring 源码感兴趣,或者想深入了解 Spring 框架的实现原理,这套资料绝对值得一学!希望这些资料能够帮助你在 Spring 学习的道路上更进一步。

运维老生常谈 3443

鸿蒙OS开机动画背后的秘密:从代码到视觉的完整解析(附源码分析)

本文深入解析了鸿蒙OS开机动画的系统级实现原理。从bootanimation服务的启动配置、与Render Service的协同,到基于VSync信号的帧率同步与绘制流水线,结合OpenHarmony源码进行了逐行解读。文章揭示了其事件驱动的优雅退出机制,并探讨了视频支持、多屏适配等高级特性与定制化可能,为开发者理解鸿蒙图形子系统提供了硬核技术视角。

sony5的博客 972

从源码解读Spring的AOP

概念 IOC和AOP作为Spring的两大核心,如果你接触过Spring框架,那就一定需要了解这两个核心部分的工作原理。IOC控制反转(或者说依赖注入)的部分已经在我的从源码解读Spring的IOC文章中有介绍,这篇文章主要来讲解AOP面向切面编程的原理 AOP,即Aspect Oriented Programming,面向切面编程,关于什么是面向切面编程,为了避免喧宾夺主,我在这里不进行过多的探...

Ekmr的博客 4504

Cesium+Three.js融合实战:从零构建GIS与BIM一体化场景

本文详细介绍了如何将Cesium与Three.js进行技术融合,从零构建GIS与BIM一体化场景。通过环境配置、核心场景搭建、坐标转换、性能优化及交互处理等实战步骤,阐述了Cesium负责宏观地理空间渲染、Three.js处理微观精细BIM模型的协同方案,为智慧城市、数字孪生等应用提供关键技术实现路径。

weixin_29230865的博客 460

大模型自我认知微调的实战教程

本文主要介绍了大模型自我认知微调的实战教程,希望对学习大语言模型的同学们有所帮助。微调成功并保留原有能力的截图如下所示: 文章目录 1. 前言 2. 配置环境 2.1 安装虚拟环境 2.2 安装依赖库 3. 实战代码 3.1 配置数据集 3.2 lora微调

herosunly的博客 4万+

Spring AOP源码分析

目录 1、AOP是什么 2、AOP 的作用及其优势 3、AOP 的底层实现 3.1 JDK动态代理: 3.2 CGLIB动态代理 4、AOP相关概念 5、AOP运行流程图 6、AOP源码解析 6.1 源码解析须知 6.2 注册自动代理创建器 6.3 触发后置处理器 6.4 创建Proxy过程分析 6.5 代理回调增强 6.6 增强器执行 6.7 增强器执行顺序 7、源码总结 1、AOP是什么 AOP 为 Aspect Oriented Programming ..

长沙老码农 2446

【WorkBuddy专栏32】从「分文件夹」到「组队打仗」——WorkBuddy v5.0项目模式深度拆解

【WorkBuddy专栏32】从「分文件夹」到「组队打仗」——WorkBuddy v5.0项目模式深度拆解

AI人人懂 297

【Spring AOP 源码解析】彻底搞懂 AOP 是如何执行的

Spring 在真正的通过反射去执行实际调用的方法之前,会有一个名为 **`intercept()`** 的方法,执行一些拦截的逻辑;我们构造的这些切面方法实际上就是在这部分中执行的;Spring 在创建 Bean 的时候会根据 AOP 注解生成一个由一组 interceptor 组成的链式结构,然后在调用实际方法之前,通过循环的方式按照顺序去执行这些 interceptor 中的代码,这样就在合适的时机去执行了合适的代码。

kkq的博客 1365

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【spring源码】AOP底层源码分析

【spring源码】AOP底层源码分析1.测试用例2.AOP流程概述3.AOP源码分析 spring的AOP是什么不在赘述,以下内容默认大家会使用AOP,我们今天来扒源码 1.测试用例 引入必要依赖: <dependencies> <dependency> <groupId>org.springframework&l...

aaa_bbb_ccc_123_456的博客 1万+

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Spring AOP源码学习:基本概念

前言 之前用十几篇文章介绍了SpringIoC的源码,作为与IoC齐名的 AOP自然也不能错过。同样的,接下去将会通过几篇文章来解析Spring AOP的源码。 如何将Spring源码导入 IDEA,请参考:Spring IoC:源码学习总览 注:本文的内容以 AspectJ来进行介绍。 关于 AOP 百度百科:AOP即Aspect Oriented...

程序员囧辉 4710

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Spring AOP 源码解析

之前写过 IOC 的源码分析,那篇文章真的有点长,看完需要点耐心。很多读者希望能写一写 Spring AOP 的源码分析文章,这样读者看完 IOC + AOP 也就对 Spring 会有比较深的理解了。今天终于成文了,可能很多读者早就不再等待了,不过主要为了后来者吧。 本文不会像 IOC 源码分析那篇文章一样,很具体地分析每一行 Spring AOP 的源码,目标读者是已经知道 Spring IOC 源码是怎么回事的读者,因为 Spring AOP 终归是依赖于 IOC 容器来管理的。 阅读建议:1、先

赶路人儿 1830

10. Spring AOP源码解析

10 Spring AOP源码解析 目录 10 Spring AOP源码解析 Pt1 Spring AOP基础知识 Pt1.1 AOP核心概念 Pt1.2 AOP切点配置规则 (1) execution (2) within (3) args Pt2 Spring AOP使用示例 Pt2.1 AOP日志(XML) pom.xml spring.xml配置AOP信息 加载spring.xml 定义切面 定义Controller 运行Controller Pt2.2 JDBC(

Java是世界上最好的语言 1484

Spring Aop初始化源码分析

1 工程简介 1.1 pom <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.tar

qq_34125999的博客 4613

Spring AOP源码学习:AOP 注解的解析

前言 上文介绍了 AOP的基本概念,本文开始进入 AOP的源码解析,本文仍以 AspectJ来进行介绍,首先是 AOP注解的解析。 正文 当使用<aop:aspectj-autoproxy />注解开启 AOP功能时。 Spring会从“META-INF/spring.handlers” 配置文件中拿到该注解对应的 NamespaceHandlerSuppo...

程序员囧辉 6702
上一篇: IDEA之Translation翻译插件-TKK: 更新 TKK 失败
下一篇: Springboot源码解析第一篇:自动装配原理
Musclewl
博客等级 码龄9年 215粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值