spring源码学习笔记1——解析xml生成BeanDefinition的过程解析 ...

打印 上一主题 下一主题

主题 1017|帖子 1017|积分 3051

spring源码学习笔记1——解析xml生成BeanDefinition的过程解析

一丶Spring解析Xml生成BeanDefinition的流程

1.指定xml路径

解析xml首先需要知道xml的位置,如下我们构造了ApplicationContext
  1. ApplicationContext context =
  2. <bean id="期望注入的bean A" >
  3.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  4. </bean><bean id="期望注入的bean A" >
  5.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  6. </bean>new ClassPathXmlApplicationContext("bean.xml");
复制代码

首先根据setConfigLocations方法设置配置文件位置,从这里我们知道Spring支持多个配置文件一起加载
2.构建BeanFactory

生成的BeanDefinition需要放到BeanFactory中,所有在解析之前先生成BeanFactory


这里注释表示会通知子类刷新BeanFactory,这个子类是指AbstractApplicationContext的子类,这里子类是指AbstractRefreshableApplicationContext(ClassPathXmlApplicationContext的父类,但是refreshBeanFactory在父类中进行了实现)

3.解析xml生成BeanDefinition


这里引入一个新的概念,BeanDefinitionReader,这是Spring对BeanDefinition读取提供的规范接口,Spring支持基于注解和基于配置生成BeanDefinitiond的不同方式,所有抽取出BeanDefinitionReader,BeanDefinitionReader负责根据资源生成BeanDefinition并且注解到BeanDefinitionRegistry

解析Document生成BeanDefinition并且注册的操作委托给BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader进行
1.判断当前xml的profile是否需要加载

如果是默认的Namespace(指xmlns=http://www.springframework.org/schema/beans)读取profile (多个环境生效的xml 可以使用 【,;】进行分割,还可以使用【!】表示不在哪个环境下生效,比如!dev表示dev环境下这个xml中的bean将不被注入)

这里会先拿配置文件中的spring.profiles.active,如果不支持那么不处理这个xml,如果spring.profiles.active对于的值是空,那么拿spring.profiles.default中指定的配置,如果也不支持当前环境 那么不处理当前xml
2.解析xml生成BeanDefinition


接下来我们看下是怎么解析每一个生成BeanDefinition的


  • 解析Import标签

对于这种内容,会针对resource的内容解析成Resouce再此调用解析成xml生成Bean的方法——loadBeanDefinitions,存在一点点逻辑就是可以当前绝对路径,url,相对路径去解析resouce中内容

  • 解析alias标签

    为Bean配置别名
    根本上都是向BeanFactory注册,数据维护在aliasMap属性中,这里存在逻辑,如果name和alias相同会进行删除(因为name是bean标签指定的bean名称,别名也是这个的话那么没有必要注册(直接可以通过bean名称拿到bean)),还会检查是否存在别名的循环引用,对于name=a alias=b 和name=b,alias=a 这种情况会抛出异常
  • 解析Bean标签
    见剩余章节
  • 解析Beans标签
    循环处理里面每一个Bean标签内容
二丶解析生成BeanDefinition


1,BeanDefinitionParserDelegate解析Bean标签生成BeanDefinitionHolder
  1. BeanDefinitionHolder是BeanDefinition的包装器,里面持有BeanDefinition和Bean的别名,Bean名称,并且记录元数据的来源
复制代码
1.1获取BeanName 和Bean别名


id是bean的唯一表示,name可以使用,;分割来表示别名,如果没有指定id,那么beanName默认使用name属性中的第一个=>a,;b,;cbeanName是a,还会检查BeanName是否重复

  • 解析其他属性生成BeanDefinition

    • 首先会拿class 和 parent指定的内容
      !
      1. Parent标签允许子类对一些属性不进行值的指定,而是直接使用父类中指定的值
      2. 相当于parent 可以起到模板的作用
      复制代码

1.2解析scope,abstract,lazy-init,autowire等标签


  • abstract的作用:
    ApplicationContext会预实例化所有singleton的bean。因此很重要的一点是:如果你只想把一个(父)bean定义当作模板使用,而它又指定了class属性,那么你就得将'abstract'属性设置为'true'
  • autowire的作用
    这里并不是立即进行依赖注入,只是对autowire的值进行解析,后续创建对象的时候才会依据值的不同有不同的行为

  • scope的作用

    • Singleton :一个Spring 容器中只有一个Bean 的实例,此为Spring 的默认配置,全容器共享一个实例。
    • Prototype :每次调用新建一个Bean 的实例。
    • Request: Web 项目中,给每一个http request 新建一个Bean 实例。
    • Session: Web 项目中,给每一个http session 新建一个Bean 实例。
    • Global Session :这个只在portal 应用中有用,给每一个global http session 新建一个Bean实例。

  • lazy-init
    lazy-init属性用于配置当前的springbean是否延迟加载。所谓延迟加载就是创建spring容器的时候是不创建对象的,当第一次获取该对象时才会实例化该对象
    如下可以指定整个xml 是否延迟加载
    !
  • depends-on
depends-on表现情况就是:如果A 的depends-on配置的是B,则spring会在创建A之前先创建B,会在销毁B之前先下回A。

  • autowire-candidate
autowire-candidate:设置当前bean在被其他对象作为自动注入对象的时候,是否作为候选bean,默认值是true

  • primary
    spring为我们注入bean的时候,如果存在类型相同的多个bean,如接口Service存在实现类A和B,但是A指定了Primary=true 那么会优先注入A
  • init-method
    初始化方法,会在实例化bean之后被回调
  • destroy-method
    销毁方法,Bean被销毁的时候被回调使用
  • factory-method
    factory-method表示使用当前描述的方法指定创建bean的方法,factory-bean用于指定自己定义的类,factory-method用于指定创建bean的方法,另外创建对象的方法可以是静态的也可以是实例的。
1.4 meta 标签

meta 所声明的 key 并不会在 Bean 中体现,只是一个额外的声明,当我们需要使用里面的信息时,通过 BeanDefinition 的 getAttribute() 获取。
1.5  lookup-method 标签
  1. lookup-method 可以将定义bean的方法替换成另外一个bean中的方法
复制代码
Spring框架通过使用CGLIB库中的字节码生成来动态生成重写该方法的子类,从而实现这种方法注入
  1. <bean id="期望注入的bean A" >
  2.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  3. </bean>
复制代码
1.6 replaced-method 标签

和lookup-method 差不多但是要求替换目标bean实现 MethodReplacer  接口
  1. <bean id="期望注入的bean A" >
  2.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  3. </bean>把myValueCalculator 中的 computeValue 方法使用 replacementComputeValue 替换<bean id="期望注入的bean A" >
  4.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  5. </bean><bean id="期望注入的bean A" >
  6.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  7. </bean><bean id="期望注入的bean A" >
  8.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  9. </bean>String<bean id="期望注入的bean A" >
  10.     <lookup-method name="A的某一个方法" bean="替换成Bbean中的方法"/>
  11. </bean>
复制代码
1.7 constructor-arg 标签

通过构造函数注入。可以通过指定入参的名称,入参的index,入参的值(ref or value)来注入,还可以指定入参的类型(type属性)
1.8  property 标签

通过对应的setter方法注入,通过设置name属性 指定 字段的setter方法,使用ref 或者value 来标签字段需要注入成什么内容
1.9  qualifier 标签

qualifier 标签,和@Qualifier  差不多,qualifier标签是用来定义需要注入bean的别名的,代表这个bean必须根据名称(ByName)才会被选为候选bean(一般是ByType),即根据bean的名称进行注入.
2.注册BeanDefinition


解析完xml 之后,得到BeanDefinitionHolder,后续使用BeanDefinitionReaderUtils将BeanDefinition注册到BeanRegistry,在 DefaultListableBeanFactory 底层是使用ConcurrentHashMap 来维护bean名称和BeanDefinition的关系,别名也是使用ConcurrentHashMap 来维护Bean名称

2.1 注册BeanDefinition
  1. DefaultListableBeanFactory 为例子
复制代码
2.1.1 检验是否合法

如果是AbstractBeanDefinition 对象,那么调用validate方法
不能同时指定方法重写 且 指定bean有工厂方法产生,如果指定了重写但是类中没有这个方法 也抛出异常
2.1.2 存在相同bean名称时

如果不允许覆盖BeanDefinition,那么抛出异常,否则覆盖
2.1.3 注册

维护bean 名称和 BeanDefinition的map,把Bean名称加入到beanDefinitionNames  集合中
2.2 注册 别名

使用ConcurrentHashMap 维护别名和bean的名称,会检查是否存在别名循环的情况,比如A的别名是B,B的别名是A这种情况抛出异常

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

去皮卡多

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表