Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析
这里主要是使用org.springframework:spring-beans:5.2.0.RELEASE进行分析
文章目录
- ①. aspectj-autoproxy标签
- 1. proxy-target-class属性
- 2. expose-proxy属性
- ②. aspectj-autoproxy标签解析
- ③. DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法
- ④. 通过parseCustomElement解析自定义标
- ⑤. Namespacehandler接口
- ⑥. 获取NamespaceHandler
- ⑦. AopNamespaceHandler的init方法
- ⑧. 通过NamespaceHandler的parse方法解析aspectj-autoproxy标签
- ⑨. AnnotationAwareAspectJAutoProxyCreator注册器分析
- ⑩. ``标签解析
- ⑪. AspectJAutoProxyBeanDefinitionParser 源码分析
1. 查看源码相关的快捷键
快捷键 | 作用 |
---|---|
Ctrl + Shift+i | 出现类似于预览的小窗口 |
Ctrl + Enter | (接上步)完全打开源码 |
Ctrl + 鼠标左键 | 一步到位打开源码 = Ctrl + Shift+i –>Ctrl + Enter |
Alt+7 | 查看类的有什么方法 |
ctrl+f12 | 查看继承方法 |
ctrl+h | 查看接口的实现类 |
alt+7 | 查看类的有什么方法 |
2下shift | 全局搜索整个项目 |
基于注解配置的属性源码分析
①. aspectj-autoproxy标签
使用注解方式应用aop需要在配置文件中配置aop:aspectj-autoproxy/, 而该标签有两个属性,proxy-target-class 和 expose-proxy。
1. proxy-target-class属性
如果被代理的目标对象至少实现了一个接口,则会使用JDK动态代理
,所有实现该目标类实现的接口都将被代理;如果该目标对象没有实现任何接口,则创建CGLIB动态代理
。但是可以通过proxy-target-class属性强制指定使用CGLIB代理。如果指定了proxy-target-class="true"
则将强制开启CGLIB动态代理。
2. expose-proxy属性
解决目标对象内部的自我调用无法实施切面增强的问题。
②. aspectj-autoproxy标签解析
分析一下Spring是如何解析自定义标签aspectj-autoproxy的。
自定义标签解析入口DefaultBeanDefinitionDocumentReader
③. DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法
DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 1、解析默认命名空间if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;// 默认命名空间if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}// 自定义命名空间else {delegate.parseCustomElement(ele);}}}}// 2、解析自定义命名空间else {delegate.parseCustomElement(root);}}
④. 通过parseCustomElement解析自定义标
通过parseCustomElement解析自定义标签
@Nullablepublic BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {// 1、获取namespaceUriString namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;}// 2、根据namespaceUri得到命名空间解析器NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}// 2、使用命名空间解析器解析自定义标签return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}
对自定义标签的解析交给了NamespaceHandler接口,如果我们想在Spring中实现自己的自定义标签,那么就需要实现NamespaceHandler接口,并通过重写其中的方法,来完成对自定义标签的解析。
⑤. Namespacehandler接口
Namespacehandler接口
⑥. 获取NamespaceHandler
获取NamespaceHandler
@Override@Nullablepublic NamespaceHandler resolve(String namespaceUri) {// 1.获取所有的namespaceUri,NamespaceHandler键值对map集合并得到// 当前namespaceUri对应的NamespaceHandler类Map<String, Object> handlerMappings = getHandlerMappings();Object handlerOrClassName = handlerMappings.get(namespaceUri);if (handlerOrClassName == null) {return null;}else if (handlerOrClassName instanceof NamespaceHandler) {return (NamespaceHandler) handlerOrClassName;}else {String className = (String) handlerOrClassName;try {Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");}// 2、通过BeanUtils实例化NamespaceHandlerNamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);//最后调用NamespaceHandler实例的init方法。namespaceHandler.init();handlerMappings.put(namespaceUri, namespaceHandler);return namespaceHandler;
⑦. AopNamespaceHandler的init方法
AopNamespaceHandler的init方法
@Overridepublic void init() {registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());//aspectj-autoproxy标签,registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());}
}
⑧. 通过NamespaceHandler的parse方法解析aspectj-autoproxy标签
具体的转换工作委托给了BeanDefinitionParser实例:
public BeanDefinition parse(Element element, ParserContext parserContext) {// 1、注册AnnotationAwareAspectJAutoProxyCreatorAopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);// 2、处理子标签<aop:include/>, 指定@Aspect类,支持正则表达式,符合该表达式的切面类才会被应用extendBeanDefinition(element, parserContext);return null;
⑨. AnnotationAwareAspectJAutoProxyCreator注册器分析
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {// 1、注册AnnotationAwareAspectJAutoProxyCreatorBeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 2、解析子标签 proxy-target-class 和 expose-proxyuseClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 3、注册组件并发送组件注册事件registerComponentIfNecessary(beanDefinition, parserContext);}
1. 注册AnnotationAwareAspectJAutoProxyCreator
@Nullableprivate static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {// 如果registry已经包含了internalAutoProxyCreator,if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 如果已经注册的internalAutoProxyCreator不是AnnotationAwareAspectJAutoProxyCreator,则需要判断优先级并决定使用哪个if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName(cls.getName());}}
// 如果已经注册的internalAutoProxyCreator是AnnotationAwareAspectJAutoProxyCreator,则无需特殊处理return null;}RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);
// HIGHEST_PRECEDENCE --> 指定最高优先级beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
// ROLE_INFRASTRUCTURE --> 表示Spring的内部beanbeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册BeanDefinitionregistry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}
2 .解析子标签 proxy-target-class 和 expose-proxy
⑩. <aop:include/>
标签解析
<aop:include/>
标签的作用:例如有下面的配置文件,并指定了<aop:include name=“aspectJTest”></aop:include>,则aspectJTest切面类会被应用,且name支持正则表达式,如果配置文件中的切面类不符合name的表达式,则切面类不会被应用。
⑪. AspectJAutoProxyBeanDefinitionParser 源码分析
AspectJAutoProxyBeanDefinitionParser 源码分析
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {// 解析标签的时候将会执行的方法@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {// 注册一个类型为AnnotationAwareAspectJAutoProxyCreator的bean到Spring容器中AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);// 通过读取配置文件对扩展相关属性 extendBeanDefinition(element, parserContext);return null;}private void extendBeanDefinition(Element element, ParserContext parserContext) {// 获取前面注册的AnnotationAwareAspectJAutoProxyCreator对应的BeanDefinitionBeanDefinition beanDef =parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);// 解析当前标签的子标签if (element.hasChildNodes()) {addIncludePatterns(element, parserContext, beanDef);}}// 解析子标签中的name属性,其可以有多个,这个name属性最终会被添加到// AnnotationAwareAspectJAutoProxyCreator的includePatterns属性中,// Spring在判断一个类是否需要进行代理的时候会判断当前bean的名称是否与includePatterns中的// 正则表达式相匹配,如果不匹配,则不进行代理private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {ManagedList<TypedStringValue> includePatterns = new ManagedList<>();NodeList childNodes = element.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {Node node = childNodes.item(i);if (node instanceof Element) {Element includeElement = (Element) node;// 解析子标签中的name属性TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));valueHolder.setSource(parserContext.extractSource(includeElement));includePatterns.add(valueHolder);}}// 将解析到的name属性设置到AnnotationAwareAspectJAutoProxyCreator// 的includePatterns属性中if (!includePatterns.isEmpty()) {includePatterns.setSource(parserContext.extractSource(element));beanDef.getPropertyValues().add("includePatterns", includePatterns);}}
}
Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析相关推荐
- Spring源码学习(四) | @Configuration的cglib动态代理
文章目录 前言 例子 @Configuration :full or lite 设置 full or lite Cglib生成代理类AppConfig Where is it generated Ho ...
- spring源码学习之路---深入AOP(终)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...
- Spring源码学习的初步体会
Spring源码学习的初步体会: 深入学习和巩固java的基础知识,其中的java知识范围全部,可以边研究源码边巩固复习基础知识 体会其中用到的设计思想:其中包含的设计原则和设计模式. 加深对spri ...
- spring源码学习之整合Mybatis原理分析
本文主要解析spring是如何与mybatis进行整合,整合的过程中需要哪些组件的支持.以前面提到过的配置例子<spring源码学习之aop事物标签解析> 整合的过程中需要使用以下这个依赖 ...
- 【Spring源码学习】Spring Bean的销毁
[Spring源码学习]Spring Bean的销毁 一.注册bean销毁的类 1.registerDisposableBeanIfNecessary() 2.DisposableBeanAdapte ...
- SpringBoot源码学习系列之异常处理自动配置
SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...
- Spring源码学习第七天==>解析配置注解类与BPP
关键词: Spring解析配置类注解Bean Spring注册Bean后置增强器(BPP) Spring消息资源和监听器的初始化 一:Spring解析配置类注解Bean==>Configurat ...
- Spring源码学习(一)--Spring底层核心原理解析
目录 Spring中是如何创建一个对象? Bean的创建过程 推断构造方法 AOP大致流程 Spring事务 最近在跟视频学习spring源码,将每节课记录下来,以后好来复习. 首先把Spring中核 ...
- Spring源码学习(三)-- 底层架构核心概念解析
前面,我们大概了解了Spring中的一些概念和底层工作流程,这篇是后续看Spring源码所必备的,防止后续看源码的过程中,遇到不会的概念得单独跳出来学习. BeanDefinition BeanDe ...
- Spring源码学习路线
如果你想加入spring源码的学习,笔者的建议是从 spring-core入手,其次是spring-beans和spring-aop,随后是spring-context,再其次是spring-t ...
最新文章
- echart 折线图设置y轴单位_如何让echarts中y轴的单位位于数值的右上角
- 6.非关系型数据库(Nosql)之mongodb:集群(主从复制)
- linux 下tomcat服务每天定时启动
- NuGet的简单使用
- 计算机excel图表考试题库,2016年职称计算机考试Excel操作题库
- 三维振镜原理_展商专访 | 先临三维与易加三维联合携多款精品及新品亮相2020年TCT亚洲展...
- C++中的接口继承和实现继承
- c语言遗传算法实例ppt,遗传算法的C语言程序案例.doc
- 在线播放,将文件转成流媒体
- excel 姓名汉字转拼音
- 为什么你的问题总是得不到博主回复?一文教你如何高效和博主进行沟通!
- 米兔机器人自主编程_可编程米兔机器人 让成年人回归童心的玩具
- 玩转IE之自动切换代理服务器
- 编写站点robots文件
- php 图片 圆角,php使用gd库在图片中画圆角矩形
- aspose html转换pdf,aspose html转pdf java
- 阅读用户思维的思考。
- java服务器如何群发消息,java TCP编程简单实现一个消息群发功能
- 如何提高加好友通过率
- 色度学:RGB颜色空间与CMYK颜色空间的比较与图示
热门文章
- java flightrecorder_java 11 Java Flight Recorder
- 华为普工一个月的工资是多少?
- IDEA搭建POI导入导出Excel入门版
- WINDOWS系统还原后原来多个分区合并为一个分区后怎么恢复数据?
- 软阈值迭代算法(ISTA)和快速软阈值迭代算法(FISTA)
- 一个在线运行的Taro小程序完整实例
- 数据库SQL Server 2017下载及安装
- 多线程总结--小码哥java
- Java多线程篇--基本概念
- [Vue][transition]Vue中实现类似JQuery中slideUp slideDown的滑动显示隐藏过渡动画效果