spring源码学习之默认标签的解析(二)

时间:2022-09-06 23:52:11

  这个是接着上一篇来写,主要是这章内容比较多,还是分开来写吧!

一、AbstractBeanDefinition属性介绍

XML中的所有的属性都可以在GenericBeanDefinition中找到对应,GenericBeanDefinition只是子类实现,大部分通用的配置都在
其父类AbstractBeanDefinition中定义,来看一下AbstractBeanDefinition中有哪些属性定义,因为我看的是spring5.0版本,和作者的版本应该不一样,这里是少了两个属性的就是scope和singleton,我下面是spring5.0的源码:

 /**
* bean的作用范围,对应bean的属性scope
*/
@Nullable
private String scope = SCOPE_DEFAULT; /**
* 是否是抽象,对应bean属性abstract
*/
private boolean abstractFlag = false; /**
* 是否延迟加载,对应bean属性lazy-init
*/
private boolean lazyInit = false; /**
* 自动注入模式,对应bean属性autowire
*/
private int autowireMode = AUTOWIRE_NO; /**
* 依赖检查,spring3.0之后弃用这个属性
*/
private int dependencyCheck = DEPENDENCY_CHECK_NONE; /**
* 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean的属性depend-on
*/
@Nullable
private String[] dependsOn; /**
* autowireCandidate属性设置成flase时,这样容器在查找自动装配对象时,将不考虑该bean,<br>
* 即它不会被考虑作为其他bean的自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他bean的
*/
private boolean autowireCandidate = true; /**
* 自动装配时当出现多个bean候选者的时候,将作为首先,对应bean属性primary
*/
private boolean primary = false; /**
* 用于记录Qualifier,对应子元素qualifier
*/
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(); /**
* 这个好像是新加的属性 TODO
*/
@Nullable
private Supplier<?> instanceSupplier; /**
* 允许访问非公开的构造器和方法,程序设置
*/
private boolean nonPublicAccessAllowed = true; /**
* 是否以一种宽松的方式解析构造函数,默认为true <br>
*/
private boolean lenientConstructorResolution = true; /**
* 对应bean属性factory-bean
*/
@Nullable
private String factoryBeanName; /**
* 对应bean属性,factory-method
*/
@Nullable
private String factoryMethodName; /**
* 记录构造函数记录属性,对应bean属性constructor-arg
*/
@Nullable
private ConstructorArgumentValues constructorArgumentValues; /**
* 普通属性集合
*/
@Nullable
private MutablePropertyValues propertyValues; /**
* 方法重写的持有者,记录lookup-method、replace-method元素
*/
@Nullable
private MethodOverrides methodOverrides; /**
* 初始化方法,对应bean属性init-method
*/
@Nullable
private String initMethodName; /**
* 销毁方法,对应bean属性destroy-method
*/
@Nullable
private String destroyMethodName; /**
* 是否执行init-method方法,程序设置
*/
private boolean enforceInitMethod = true; /**
* 是否执行destroy-method方法,程序设置
*/
private boolean enforceDestroyMethod = true; /**
* 是否是用户定义的而不是应用程序定义的。创建AOP的时候为true,程序设置
*/
private boolean synthetic = false; /**
* 定义这个bean的应用
* APPLICATION:用户
* INFRASTRUCTURE:完全内部使用,与用户无关
* SUPPORT:某些复杂配置的一部分
* 程序设置
*/
private int role = BeanDefinition.ROLE_APPLICATION; /**
* bean的描述信息
*/
@Nullable
private String description; /**
* bean定义的资源
*/
@Nullable
private Resource resource;

二、注册解析BeanDefinition

org.springframework.beans.factory.xml包下的DefaultBeanDefinitionDocumentReader类中processBeanDefinition方法中的
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());这行代码进行注册操作
BeanDefinitionReaderUtils所在的包:org.springframework.beans.factory.support,看一下registerBeanDefinition方法的源码

 /**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // Register bean definition under primary name.
// 使用beanName作为唯一标识
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any.
// 注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

通过上面的代码,可以看出,解析的BeanDefinition都会被注册到BeanDefinitionRegistry类型的registry实例中,而对于
BeanDefinition的注册分成了两部分,通过beanName的注册以及通过别名的注册

1、通过beanName的注册BeanDefinition
对于BeanDefinition的注册,或许我们都认为是将BeanDefinition直接放入map中,使用beanName作为key,确实spring是这么做的,
只不过,除此之外,还做了点别的事情
注意:这里spring中的BeanDefinitionRegistry是以接口形式存在的,它有三个实现类,分别是GenericApplicationContext、DefaultListableBeanFactory和
SimpleBeanDefinitionRegistry,其中SimpleBeanDefinitionRegistry类中的方法实现是没做任何事情,只是将其放入map中,其他两个实现类的处理逻辑是一样的
,只不过在GenericApplicationContext中创建了一个DefaultListableBeanFactory实例,来调用DefaultListableBeanFactory中的注册方法,所有,来看下
DefaultListableBeanFactory中registerBeanDefinition方法

org.springframework.beans.factory.support包下的DefaultListableBeanFactory类中:

 @Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) {
try {
/**
* 注册前的最后一次验证,这里的校验不同于之前的XML文件校验<br>
* 主要是对于AbstractBeanDefinition属性中的methodOverrides校验<br>
* 验证methodOverrides是否与工厂方法共存或者methodOverrides对应的方法根本不存在
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
} BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + existingDefinition + "] bound.");
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isWarnEnabled()) {
logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isInfoEnabled()) {
logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 已经创建至少一次的bean的名称
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 因为beanDefinitionMap是全局变量,这里肯定存在并发访问的问题
synchronized (this.beanDefinitionMap) {
// 处理beanName的存储问题
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
// bean没有被创建过
else {
// Still in startup registration phase
// 注册beanDefinition并记录beanName
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
} if (existingDefinition != null || containsSingleton(beanName)) {
// 重置所有beanName对应的缓存
resetBeanDefinition(beanName);
}
}

注意:这里跟作者书中的代码有些出入,不知道难道是我下载的版本和作者不一样,不知道,反正总体的处理逻辑是一样的,只是这段代码是
处理的情况更加完善一些!

2、通过别名注册BeanDefinition

同样这个是在AliasRegistry接口中,BeanDefinitionRegistry是AliasRegistry的实现类,找到AliasRegistry类中registerAlias方法,进而找到实现类中方法实现
最终在org.springframework.core包下的SimpleAliasRegistry类中,源码如下:

 @Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// aliasMap是全局变量,肯定存在并发问题
synchronized (this.aliasMap) {
// 如果alias与beanName相同的话,不记录alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 如果alias别名不允许被覆盖则抛出异常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isInfoEnabled()) {
logger.info("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// 当A->B,存在时,若再次出现A->C->B则抛出异常
checkForAliasCircle(name, alias);
// 注册别名
this.aliasMap.put(alias, name);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}

三、通知监听器解析及注册完成

通过代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
完成此工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式
并将处理逻辑写在监听器中,目前spring中没有对此进行处理

四、alias标签的解析

alias标签,就是给bean起个别名,反正我是没用过这个,大概写一下吧!
用法如下:
1)<bean id="testBean" name="testBean2,testBean" class="com.test"></bean>
2)<bean id="testBean" class="com.test"></bean>
<alias name="testBean" alias="testBean2,testBean" />

源码解读:
org.springframework.beans.factory.xml包下DefaultBeanDefinitionDocumentReader类中的processAliasRegistration方法

 /**
* Process the given alias element, registering the alias with the registry.
*/
protected void processAliasRegistration(Element ele) {
// 获取beanName
String name = ele.getAttribute(NAME_ATTRIBUTE);
// 获取alias
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
// 注册alias
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
// 注册别名后通知监听器做相应处理
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}

五、import标签的解析

import标签实现了模块的划分,可以使得配置文件模块化,主要归结于import标签的功劳,
主要就是引入其他的
<import resource="customerContext.xml" />
<import resource="systemContext.xml" />

源码解读:
org.springframework.beans.factory.xml包下DefaultBeanDefinitionDocumentReader类中的importBeanDefinitionResource方法

 /**
* Parse an "import" element and load the bean definitions
* from the given resource into the bean factory.
*/
protected void importBeanDefinitionResource(Element ele) {
// 获取resource属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果不存在resource属性则不做任何处理
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
} // Resolve system properties: e.g. "${user.dir}"
// 解析系统属性 格式如:"${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<>(4); // Discover whether the location is an absolute or relative URI
// 判断location是绝对路径还是相对路径
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
// cannot convert to an URI, considering the location relative
// unless it is the well-known Spring prefix "classpath*:"
} // Absolute or relative?
// 如果是绝对路径URI 则直接根据地址加载对应的配置文件
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {
// No URL -> considering resource location as relative to the current file.
// 如果是相对路径,则根据相对路径计算出绝对路径
try {
int importCount;
/**
* Resource存在多个实现类,如直接选中之后,快捷键Ctrl+T查看其实现类<br>
* 而每个Resource的createRelative实现都不一样,所以这里先尝试子类的方法进行尝试解析
*/
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
// 如果解析不成功,则使用默认的解析器ResourcePatternResolver进行解析,这个有点太复杂了,好多实现类,理不清楚!
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
ele, ex);
}
}
// 解析后进行监听器激活处理
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

spring源码学习之默认标签的解析(二)的更多相关文章

  1. spring源码学习之默认标签的解析(一)

    继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清 ...

  2. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

  3. Spring 源码学习&lpar;1&rpar; —— 自定义标签

    Spring 工作流程是先加载解析xml配置文件:配置文件中存在默认的标签,也可以自定义标签.解析默认标签调用: private void parseDefaultElement(Element el ...

  4. spring源码学习之容器的扩展(二)

    六 BeanFactory的后处理BeanFactory作为spring容器的基础,用于存放所有已经加载的bean,为了保证程序上的高扩展性,spring针对BeanFactory做了大量的扩展,比如 ...

  5. Spring源码学习之IOC实现原理(二)-ApplicationContext

    一.Spring核心组件结构 总的来说Spring共有三个核心组件,分别为Core,Context,Bean.三大核心组件的协同工作主要表现在 :Bean是包装我们应用程序自定义对象Object的,O ...

  6. Spring源码学习

    Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...

  7. Spring源码学习-容器BeanFactory&lpar;三&rpar; BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  8. Spring源码学习-容器BeanFactory&lpar;四&rpar; BeanDefinition的创建-自定义标签的解析&period;md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  9. Spring源码学习-容器BeanFactory&lpar;二&rpar; BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

随机推荐

  1. asp&period;net中选择数字时,另外的数字同时发生变化(适用dev控件)

      关键: <ClientSideEvents ValueChanged="AgioChanged" />   <div class="col-sm-4 ...

  2. EffectiveJava——复合优先于继承

    继承时实现代码重用的重要手段,但它并非永远是完成这项工作的最佳工具,不恰当的使用会导致程序变得很脆弱,当然,在同一个程序员的控制下,使用继承会变的非常安全.想到了很有名的一句话,你永远不知道你的用户是 ...

  3. combobox中动态加入几个checkbox,实现下拉框多选

    combobox中动态加入几个checkbox,实现下拉框多选,将一个checkbox选中时其内容就会在combobox中显示出来,将另一个checkbox选中时其内容会跟在第一个checkbox的内 ...

  4. js判断输入是否为空,获得输入的类型

    使用typeof算法 typeof的运算数未定义,返回的就是 "undefined". 下面定义x为运算数: 有一下5种情况: 运算数为数字 typeof(x) 返回的就是 &qu ...

  5. 块对象block小结

    blcok的形式 ^(参数列){主体} block作为返回值

  6. JSP的学习(8)——JSP标签

    JSP标签也称为JSP Action(JSP动作)元素,用于在JSP页面中封装Java代码,这样使得在JSP页面中避免直接编写Java代码,让JSP真正成为MVC模式中的作为视图作用. 几个JSP常用 ...

  7. upload 简单的封装

    upload 最简单的封装类 <?php    class Upload{        public function Up($files){            if($files['na ...

  8. 用python给邮箱发邮件,问题,以及解决方法。

    模版 import smtplib #导入相关模块 from email.mime.text import MIMEText from email.utils import formataddr de ...

  9. Hash 分布均衡算法

    1.移位实现 public static int GetIndex(string str, int count) { , (current, c) => (current << ) ...

  10. eclipse或tomcat web项目启动失败其中一种解决办法

    失败信息如下: java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to s ...