笔记整理自《Spring源码深度解析》(第2版),同时也参考了一些网上资源,具体参考链接在文末
在默认标签中,对bean标签的解析最为复杂也最为重要,我们从此标签分析其解析流程,即可理解其他标签的解析
DefaultBeanDefinitionDocumentReader.java |
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 是否是默认的命名空间
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);
}
}
}
}
else {
// 处理自定义标签
delegate.parseCustomElement(root);
}
}
DefaultBeanDefinitionDocumentReader.java |
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析 import 标签
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析 alias 标签
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);//(4)
}
// 解析 bean 标签
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);//(2)
}
// 解析 beans 标签
// 嵌套的 beans
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
对于不同类型的默认标签有不同的处理方法,接下来我们逐个分析
DefaultBeanDefinitionDocumentReader.java |
// 删除了异常处理
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 创建 bean definition
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); //(2)
if (bdHolder != null) {
// bean definition 装饰
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// Register the final decorated instance.
// 注册beanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); //(3)
// Send registration event.
// component注册事件触发
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));//(4)
}
}
decorateBeanDefinitionIfRequired的作用是当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,就需要进行装饰处理。
Spring 在这里将 bean
标签的处理完全交给了 BeanDefinitionParserDelegate
对象进行。
流程分析:
BeanDefinitionParserDelegate
对象进行 bean
标签的处理得到 BeanDefinitionHolder
对象BeanDefinitionHolder
对象进行 Bean 定义注册fireComponentRegistered为通知监听器解析以及注册完成,这里的实现只为扩展,如果需要对注册BeanDefintion事件进行监听注册时可以通过注册监听器的方式将处理器逻辑写入监听器中。
BeanDefinitionParserDelegate.java |
此方法较为复杂,我们分为几部分分析:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 第一部分
// 获取 id
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取 name
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 别名列表
List<String> aliases = new ArrayList<>();
// 是否有 name 属性
if (StringUtils.hasLength(nameAttr)) {
// 获取名称列表, 根据 `,; `进行分割
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 添加所有
aliases.addAll(Arrays.asList(nameArr));
}
...
}
第一部分的处理很简单获取 bean
标签中的 id
和 name
属性,对 name
属性做分隔符切分后将切分结果作为别名。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
...
String beanName = id;
//如果id为空,且别名列表不为空
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 别名的第一个设置为beanName
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// bean definition 为空
if (containingBean == null) {
// 判断 beanName 是否被使用, bean 别名是否被使用
checkNameUniqueness(beanName, aliases, ele);
}
...
}
id
存在的情况下都是用 id
作为 Bean Name
id
不存在的情况下使用 name
被分隔符切分后的第一个元素作为 Bean Name
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
...
//调用parseBeanDefinitionElement对标签其他属性进行解析
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
//如果不存在beanName,那么根据Spring内部提供的命名规则为当前bean生产对应的beanName
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
}
catch (Exception ex) {
...
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
注意其中
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
它会进一步解析其他所有属性,并统一封装至GenericBeanfinition类型的实例中。GenericBeanfinition是AbstractBeanDefinition的子类
BeanDefinition 是用于属性承载的接口,在 Spring 中存在三种实现: RootBeanDefinition 、childBeanDefinition 以及 GenericBeanDefinition
提取元素中的id以及name属性。
进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。
3、如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName
4、将获取到的信息封装到BeanDefinitionHolder的实例中。
拿到BeanDefinitionHolder实例后,接下来又回到了(1)方法中,我们可以看到接下来需要进行注册了
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); //(3)
BeanDefinitionReaderUtils.java |
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 获取 beanName
String beanName = definitionHolder.getBeanName();
// 注册bean definition
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);
}
}
}
整个过程分为两部分;
首先来分析第一步
DefaultListableBeanFactory.java |
//成员变量,用来存储beanDefinition
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
new ConcurrentHashMap<>(8);
//用来记录已经注册了的beanDefinitionName,主要用于多线程下的判断
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
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文件校验
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
...
}
}
//beanName存在BeanDefinitio的情况
//从map中根据beanName获取beanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//bean name是否允许重复注册
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
//role值比较
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
...
}
//map中存储的beanDefintion是否和参数相同
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
...
}
//如果允许覆盖Bean Definition,通过beanName进行注册
this.beanDefinitionMap.put(beanName, beanDefinition);
}
//容器中不存在Bean Name对应的Bean Definitio的处理
else {
//检查bean是否已经开始创建
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
//通过beanName进行注册
this.beanDefinitionMap.put(beanName, beanDefinition);
//bean definitio的名称列表
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
//加入当前的beanName
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
//对象替换
this.beanDefinitionNames = updatedDefinitions;
//移除当前beanName
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
//通过beanName进行注册
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
//Bean Definition的刷新处理
//两个条件:1. 通过beanName搜索BeanDefinition成功 2. 单例对象容器中包含当前BeanName的实例对象
if (existingDefinition != null || containsSingleton(beanName)) {
//重置所有beanName对应对应的缓存
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
很多人认为注册beanDefinition就是将beanDefinition直接放入map中,使用beanName作为key。不过除此之外,Spring还做了其他事情。具体流程如下:
分析第二步
SimpleAliasRegistry.java |
// 删除日志和验证相关代码
public void registerAlias(String name, String alias) {
synchronized(this.aliasMap) {
// 别名和真名是否相同
if(alias.equals(name)) {
// 移除
this.aliasMap.remove(alias);
} else {
// 通过别名获取真名
String registeredName = this.aliasMap.get(alias);
// 真名不为空
if(registeredName != null) {
// 真名等于参数的真名
if(registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 是否覆盖别名
if(!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 别名是否循环使用
checkForAliasCircle(name, alias);
// 设置 别名对应真名
this.aliasMap.put(alias, name);
}
}
}
整个方法以这样一个判断作为分支: 判断别名是否和真名相等,相等时候的处理很简单,从别名容器中剔除这个别名的键值信息。
接下来就是对于别名和真名不相等的情况处理了。
第一步:从别名容器中用别名去获取容器中可能存在的真名,
第二步:当容器中可能存在的真名存在的情况下会和参数真名进行比较是否相同 , 相同的话就直接返回了, 不做其他处理。如果参数不相同,且不允许覆盖的话,则报错
第三步:当容器中可能存在的真名不存在的情况下 Spring 会进行别名是否循环使用,当检测通过时加入到别名容器中。
在对 bea 进行定义时,除了使用 id 属性来指定名称之外,为了提供多个名称,可以使用 alias 标签来指定 而所有的这些名称都指向同一个 bean ,在某些情况下提供别名非常有用,比 如为了让应用的每 个组件能更容易地对公共组件进行引用
我们回到parseDefaultElement方法中的processAliasRegistration中继续分析如何进行alias标签解析的
DefaultBeanDefinitionDocumentReader.java |
protected void processAliasRegistration(Element ele) {
//获取beanName
String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取alisa
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().fireAliasRegistered(name, alias, extractSource(ele));
}
}
applicationContext.xml文件中使用import的方式导入有模块配置文件,以后若有新模块的加入,那就可以简单修改这个文件了。这样大大简化了配置后期维护的复杂度,并使配置模块化,易于管理
我们回到parseDefaultElement方法中的importBeanDefinitionResource中继续分析如何进行import标签解析的
DefaultBeanDefinitionDocumentReader.java |
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;
}
//解析系统属性,格式如:"${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
//判定location是决定URI还是相对URI
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
}
//如果是绝对URI则自己根据地址加载对应的配置文件
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
...
}
catch (BeanDefinitionStoreException ex) {
..
}
}
else {
//如果是相对地址则根据相对地址计算出绝对地址
try {
int importCount;
//Resource存在多个子实现类,而每个resource的createRelative方式实现都不一样,所以这里先使用子类的方法尝试解析
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
//如果解析不成功,则使用默认的解析器ResourcePatternResolver进行解析
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
...
}
catch (IOException ex) {
...
}
}
//解析后进行监听器激活处理
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
流程分析:
参考链接:
第三章 IoC 资源读取及注册