首页 > 编程语言 >Spring 事务源码(二):beanDefinition的准备-配置文件加载

Spring 事务源码(二):beanDefinition的准备-配置文件加载

时间:2022-12-31 20:23:28浏览次数:37  
标签:String 配置文件 parserContext Spring 源码 标签 解析 methodEle beanDefinition

     普通bean标签的beanDefinition的解析不再赘述,仅对事务相关的核心beanDefinition的获取做分析。

一、BeanDefinition预览

  IOC容器刷新完成后,容器中的BeanDefinition详情如下:  

  Spring中的事务是基于AOP实现的,在实现AOP中有几个核心的对象:AspectJAwareAdvisorAutoProxyCreator、Advisor、PointCut、MethodInterceptor。在开始正式分析源码前,根据上述的beanDefinition集合容器与AOP的核心对象,可发现如下映射关系

  AspectJAwareAdvisorAutoProxyCreator在AOP源码分析中已做介绍,不再赘述,下面来看看Spring事务中Advisor、Advice、Pointcut的beanDefinition加载工作。

二、BeanDefinition的加载

  Spring解析自定义标签的通用流程:

2.1、PropertySourcesPlaceholderConfigurer 主要用于占位符解析

  类图关系中,PropertySourcesPlaceholderConfigurer属于BeanFactoryPostProcessor,在IOC容器刷新时,在invokeBeanFactoryPostProcessors(beanFactory)方法中处理该BeanDefinition的实例化、初始化过程。

  1、根据元素节点的命令空间,在指定模块的spring.handlers中,找到命名空间与对应beanDefinition的解析处理类ContextNamespaceHandler;   2、通过ContextNamespaceHandler获取元素beanDefinition解析器PropertyPlaceholderBeanDefinitionParser;   3、在解析器中通过BeanDefinitionBuilder创建beanDefinition并对beanDefinition做赋值操作。

2.2、AspectJExpressionPointcut 切点

1、AspectJExpressionPointcut切点解析的核心流程图

2、AspectJExpressionPointcut切点解析的核心代码

  ConfigBeanDefinitionParser#parse 核心伪代码
 1 // 切点解析核心伪代码
 2 public BeanDefinition parse(Element element, ParserContext parserContext) {
 3 
 4    // 注册自动代理模式创建器,AspectjAwareAdvisorAutoProxyCreator
 5    configureAutoProxyCreator(parserContext, element);
 6    
 7    // 解析aop:config子节点下的aop:pointcut/aop:advisor/aop:aspect等标签元素
 8    List<Element> childElts = DomUtils.getChildElements(element);
 9    
10    // 遍历子元素
11    for (Element elt: childElts) {
12       // 获取标签名称
13       String localName = parserContext.getDelegate().getLocalName(elt);
14       // pointcut的解析
15       if (POINTCUT.equals(localName)) {
16          parsePointcut(elt, parserContext);
17       }
18    }
19    return null;
20 }

  ConfigBeanDefinitionParser#parsePointcut 核心伪代码:

1 // 解析切入点
2 private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
3    // 获取切入点的表达式
4    String expression = pointcutElement.getAttribute(EXPRESSION);
5    // 创建pointcut的beanDefinition
6    AbstractBeanDefinition  pointcutDefinition = createPointcutDefinition(expression);
7    // 返回bean定义信息
8    return pointcutDefinition;
9 }

  ConfigBeanDefinitionParser#createPointcutDefinition 核心代码

 1 // 创建pointcut的beanDefinition并返回
 2 protected AbstractBeanDefinition createPointcutDefinition(String expression) {
 3    // 为AspectJExpressionPointcut创建bean定义信息
 4    RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
 5    // 设置bean定义信息为原型模式
 6    beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
 7    // 代理标识设置为true
 8    beanDefinition.setSynthetic(true);
 9    // 将切点表达式设置进bean定义信息中
10    beanDefinition.getPropertyValues().add(EXPRESSION, expression);
11    return beanDefinition;
12 }

2.3、DefaultBeanFactoryPointcutAdvisor 的beanDefinition加载

1、DefaultBeanFactoryPointcutAdvisor 的beanDefinition加载核心流程

2、DefaultBeanFactoryPointcutAdvisor 的beanDefinition加载核心伪代码

  ConfigBeanDefinitionParser#parseAdvisor 解析Advisor标签获取beanDefinition

 1 // 解析Advisor标签
 2 private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
 3    // 解析<aop:advisor>节点,最终创建的beanClass为`DefaultBeanFactoryPointcutAdvisor`
 4    AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
 5    
 6    // 生成Advisor的beanName并注册至beanDefinitionMap
 7    String advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
 8 
 9    // 解析point-cut属性并赋值到DefaultBeanFactoryPointcutAdvisor#pointcut内部属性
10    Object pointcut = parsePointcutProperty(advisorElement, parserContext);
11    // 将pointcut设置Advisor的beanDefinition的propertyValues属性中
12    advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
13 }

  ConfigBeanDefinitionParser#createAdvisorBeanDefinition 创建Advisor的beanDefinition

 1 // 创建Advisor的beanDefinition
 2 private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
 3    // 创建DefaultBeanFactoryPointcutAdvisor的beanDefinition
 4    RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
 5    
 6    // 获取advice-ref属性
 7    String adviceRef = advisorElement.getAttribute(ADVICE_REF);
 8    // 将advice-red属性值设置进beanDefinition的propertyValues属性中
 9    advisorDefinition.getPropertyValues().add(ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
10    
11    // 返回beanDefinition
12    return advisorDefinition;
13 }

2.4、TransactionInterceptor Advice

1、TransactionInterceptor 解析Advice标签获取beanDefinition 核心流程图

2、TransactionInterceptor 解析Advice标签获取beanDefinition 核心伪代码

  TxAdviceBeanDefinitionParser#parseInternal 解析advice标签获取beanDefinition

 1 protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
 2    // 创建beanDefinition
 3    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
 4    // 获取TransactionInterceptor的Class对象
 5    Class<?> beanClass = getBeanClass(element);
 6    // 为beanDefinition设置beanClass
 7    builder.getRawBeanDefinition().setBeanClass(beanClass);
 8    // 
 9    // 解析method,获取事务传播特性、隔离级别等事务信息,将事务信息封装至NameMatchTransactionAttributeSource对象,
10    // 并将NameMatchTransactionAttributeSource设置在advice的beanDefinition中
11    doParse(element, parserContext, builder);
12    return builder.getBeanDefinition();
13 }

  TxAdviceBeanDefinitionParser#doParse 解析attributes标签获取beanDefinition填充值Advice的beanDefinition中

1 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
2   // 获取attributes标签元素信息
3   Element attributeSourceElement = txAttributes.get(0);
4   // 解析attributes标签元素信息
5   RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
6   // 将解析后attributes标签元素信息填充值Advice的beanDefinition中
7   builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
8 }
  TxAdviceBeanDefinitionParser#parseAttributeSourcee 解析method标签设置事务信息并用NameMatchTransactionAttributeSource封装事务信息
 1 // 解析attribute标签元素信息
 2 private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
 3    // 获取attributes下的所有methods元素
 4    List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
 5    // 创建事务属性信息容器
 6    ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
 7          new ManagedMap<>(methods.size());
 8    
 9    // 遍历所有method元素,将method的事务属性信息添加进transactionAttributeMap容器
10    for (Element methodEle : methods) {
11       // 获取方法的名称
12       String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
13       // 用TypedStringValue对象封装方法名称
14       TypedStringValue nameHolder = new TypedStringValue(name);
15 
16       // 事务信息获取并设置
17       RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
18       // 传播特性
19       String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
20       // 隔离级别
21       String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
22       // 超时时间
23       String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
24       // 是否为只读事务
25       String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
26       if (StringUtils.hasText(propagation)) {
27          attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
28       }
29       if (StringUtils.hasText(isolation)) {
30          attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
31       }
32       if (StringUtils.hasText(timeout)) {
33          try {
34             attribute.setTimeout(Integer.parseInt(timeout));
35          }
36          catch (NumberFormatException ex) {
37             parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
38          }
39       }
40       if (StringUtils.hasText(readOnly)) {
41          attribute.setReadOnly(Boolean.parseBoolean(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
42       }
43       
44       // 设置rollback规则
45       List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(1);
46       if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
47          String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
48          addRollbackRuleAttributesTo(rollbackRules, rollbackForValue);
49       }
50       // 设置norollback规则 
51       if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
52          String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
53          addNoRollbackRuleAttributesTo(rollbackRules, noRollbackForValue);
54       }
55       attribute.setRollbackRules(rollbackRules);
56       
57       // 将方法的事务属性信息添加进transactionAttributeMap事务属性容器
58       transactionAttributeMap.put(nameHolder, attribute);
59    }
60    
61    // 创建NameMatchTransactionAttributeSource的beanDefinition信息
62    RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
63    // 将方法事务属性容器设置进NameMatchTransactionAttributeSource的beanDefinition中的propertyValues属性
64    attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
65    // 返回带有方法事务属性信息的beanDefinition
66    return attributeSourceDefinition;
67 }

 

标签:String,配置文件,parserContext,Spring,源码,标签,解析,methodEle,beanDefinition
From: https://www.cnblogs.com/RunningSnails/p/17017176.html

相关文章

  • SpringBoot动态更新yml文件
    前言在系统运行过程中,可能由于一些配置项的简单变动需要重新打包启停项目,这对于在运行中的项目会造成数据丢失,客户操作无响应等情况发生,针对这类情况对开发框架进行升级提......
  • SpringBoot动态更新yml文件
    前言在系统运行过程中,可能由于一些配置项的简单变动需要重新打包启停项目,这对于在运行中的项目会造成数据丢失,客户操作无响应等情况发生,针对这类情况对开发框架进行升级提......
  • 超多制作模板的姓氏头像生成器微信小程序源码
    ☑️编号:ym612☑️品牌:无☑️语言:小程序☑️大小:0.2MB☑️类型:姓氏头像生成器☑️支持:小程序......
  • SpringCloud之Sleuth全链路日志跟踪
    目录1Sleuth链路跟踪1.1分布式系统面临的问题1.2Sleuth是什么1.3Zipkin是什么1.4链路监控相关术语1.5实战练习1.5.1pom.xml1.5.2添加yml配置1.5.3添加控制器1.5.4......
  • WSGI服务器源码分析
    一、wsgiref模块在web开发中,后端服务分为服务器和应用程序:服务器本质就是通过socket进行请求的接收应用程序就是对请求进行相应业务处理为了开发方便,将上述两部分分......
  • 分布式排队叫号系统源码出售
    我司开发的分布式排队叫号系统,支持政务大厅、银行、工商、税务等应用场景。1、采用C/S和B/S混合架构,后台采用B/S架构,易于维护2、支持WINDOWS、android系统3、兼容多种LED屏......
  • 使用Gradle整合angular和Spring boot
    前言本文讲述如何使用Gradle搭建一个以springboot技术栈作为后端,使用Angular作为前端技术栈,同过gradle将其整合成一个项目的实践经验.这里只是作为一种实践,并不建......
  • java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you
    在使用RestTemplate时,写了一个测试测试方法,遇到了这个报错。java.lang.IllegalStateException:Unabletofinda@SpringBootConfiguration,youneedtouse@ContextCon......
  • 2022最炫酷的圣诞树合集(附动态效果展示和网盘源码)
    文章目录​​3D旋转水晶球(雪屋)​​​​3D旋转水晶球(圣诞树)​​​​豪华圣诞树​​​​Garland圣诞树​​​​花灯圣诞树​​​​Live圣诞树​​​​五彩圣诞树​​​​Gre......
  • HTML5期末大作业:上海介绍网站设计——代码质量好-上海介绍(5页) HTML+CSS+JavaScript(
    一、作品展示>二、文件目录>三、代码实现>​​h​​<!DOCTYPE ​​html​​><​​head​​>  <metacharset="utf-8"/>  <title>上海旅游介绍</title>  <link......