前言
承接上篇“容器创建”,作为后续。上篇讲到 String—>Resource—>Document,这篇主要将如何对Document继续解析,到BeanDefinition的封装、注册。
源码解读
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 获取根 <beans>进行解析
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
/**
* 任何嵌套的<beans>元素都将导致此方法的递归。
* 为了正确传播和保存<beans> default- *属性,请跟踪当前(父)委托,该委托可能为空。
* 创建新的(子)委托,并引用父对象作为回退目的,然后最终将this.delegate重置为其原始(父级)引用。
* 这种行为模拟了一堆委托,而实际上并不需要一个委托
*/
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 这一步主要是对 profile属性的校验:即spring的环境切换,使得激活环境对应的配置生效
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 主要是这一步判断的,可以看到如果不是对应激活环境的配置,直接 return不解析
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// do nothing 子类扩展
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
// do nothing 子类扩展
postProcessXml(root);
this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// http://www.springframework.org/schema/beans
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;
// http://www.springframework.org/schema/beans
if (delegate.isDefaultNamespace(ele)) {
// 此处是对默认标签的解析
parseDefaultElement(ele, delegate);
} else {
// 此处是对自定义标签的解析
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
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);
}
// <bean>标签解析
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// <beans>标签解析
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 递归调用 doRegisterBeanDefinitions
doRegisterBeanDefinitions(ele);
}
}
}
DefaultBeanDefinitionDocumentReader是 BeanDefinitionDocumentReader的唯一实现类。这里面隐藏了一个扩展点(profile),也就是根据不同环境(sit、pre、prd),Spring会加载不同配置。比如测试环境的数据源跟生产环境的肯定不一致,我们需要在不同阶段激活不同配置:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>sit</param-value>
</context-param>
当然还可以注解激活,不是本篇重点。从上面代码可以看出,解析标签时区分默认标签和自定义标签的,默认标签就只有<import>、<alias>、<bean>、<beans>这四个。自定义标签就多了,例如<component-scan>等等,下面的源码是对默认标签的解析,自定义标签在下一节讲解。
<import>解析
<!--例子-->
<import resource="classpath:spring/spring-mybatis.xml" />
这个标签的作用,就是引入其他的配置文件,避免将大量的配置集中在一个配置文件中(可以理解为方法封装,将相似的逻辑封装到一起)。
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
protected void importBeanDefinitionResource(Element ele) {
// 获取 resouce属性以及必填校验
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
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<Resource>(4);
// 判断location是绝对url还是相对url
boolean absoluteLocation = false;
try {
/**
* ResourcePatternUtils.isUrl会判断是否以 classpath*: classpath:开头 或 URL
* ResourceUtils.toURI转化为 URI,然后判断是否为绝对路径
*/
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*:"
}
// 只要能加载成功,就会调用 loadBeanDefinitions重新进行解析
// 绝对url处理:直接根据地址加载配置文件
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
......// 日志
} catch (BeanDefinitionStoreException ex) {
// 将抛异常封装成方法——ProblemReporter.error,可借鉴
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
} else {
// 相对地址处理:根据相对地址计算绝对地址
try {
int importCount;
// 尝试使用 createRelative创建相对路径 Resource
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();
/**
* StringUtils.applyRelativePath:工具类,根据根路径和相对路径补全为绝对路径
*/
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
.......
} //... catch处理
}
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
/**
* 在解析后会回调 ReaderEventListener.importProcessed
* 默认调用 EmptyReaderEventListener,空实现
*/
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
}
通过获取resource指定的路径,重新调用上篇解析的逻辑(String—>Document),这里面我们可以看到这个方法的最后调用了fireImportProcessed方法,接下来的<alias>、<bean>解析后都会调用类似的方法,该方法最终会回调(扩展点) ReaderEventListener的各个方法:
- <import>:对应 void importProcessed( ImportDefinition importDefinition);
- <alias>:对应 void aliasRegistered( AliasDefinition aliasDefinition);
- <bean>:对应 void componentRegistered( ComponentDefinition componentDefinition)。
默认的实现 EmptyReaderEventListener为空实现,如果需要自定义,需要实现 ReaderEventListener 然后设置到 XmlBeanDefinitionReader:
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
private ReaderEventListener eventListener = new EmptyReaderEventListener();
public void setEventListener(ReaderEventListener eventListener) {
this.eventListener = (eventListener != null ?
eventListener : new EmptyReaderEventListener());
}
}
<alias>解析
<!--别名使用-->
<bean id="app:dataSource" class="...">
<alias name="app:dataSoure" alias="user:dataSoure"/>
<alias name="app:dataSoure" alias="device:dataSoure"/>
</bean>
<!--别名的另一种方式-->
<bean id="app:dataSource" class="..." name="user:dataSoure,device:dataSoure" />
这个标签的作用,将一个或多个别名跟 bean关联起来,在其他引用处可以使用别名指定。
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
protected void processAliasRegistration(Element ele) {
// <alias name="" alias="" />
String name = ele.getAttribute(NAME_ATTRIBUTE);
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);
}
/**
* 在解析后会回调 ReaderEventListener.aliasRegistered
* 默认调用 EmptyReaderEventListener,空实现
*/
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
}
public class SimpleAliasRegistry implements AliasRegistry {
// key-别名 value-beanName
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
// 首先移除 name和 alias相同的(即无需别名)
if (alias.equals(name)) {
this.aliasMap.remove(alias);
} else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// 存在的别名,无需注册
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 检查 name是否已注册 alias,防止循环注册
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
}
}
使用了 Map类型存储别名和 beanName的映射关系。
<bean>解析
在看解析源码前,我们先看看封装后的 BeanDefinition长什么样子,我们选择其抽象实现 AbstractBeanDefinition来看(因为BeanDefinition仅定义了接口层面)。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
private volatile Object beanClass;
// <bean>属性 scope,bean的作用范围
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;
// 依赖检查,3.0后弃用
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
// <bean>属性 depend-on,实例创建依赖其他实例创建
private String[] dependsOn;
// <bean>属性 autowire-candidate,是否作为其他对象自动装配时的候选者
private boolean autowireCandidate = true;
// <bean>属性 primary,自动装配多个候选者时是否作为首选
private boolean primary = false;
// <bean>子元素 <qualifer>
private final Map<String, AutowireCandidateQualifier> qualifiers =
new LinkedHashMap<String, AutowireCandidateQualifier>(0);
// 是否允许访问非公开的构造器和方法
private boolean nonPublicAccessAllowed = true;
private boolean lenientConstructorResolution = true;
// <bean>子元素 <contractor-arg>
private ConstructorArgumentValues constructorArgumentValues;
// <bean>子元素 <property>集合
private MutablePropertyValues propertyValues;
// <bean>子元素 <lookup-method>、<replaced-method>,方法重写
private MethodOverrides methodOverrides = new MethodOverrides();
// <bean>属性 factory-bean
private String factoryBeanName;
// <bean>属性 factory-method
private String factoryMethodName;
// <bean>属性 init-method
private String initMethodName;
// <bean>属性 destory-method
private String destroyMethodName;
// 是否执行 init-method
private boolean enforceInitMethod = true;
// 是否执行 destory-method
private boolean enforceDestroyMethod = true;
private boolean synthetic = false;
/**
* 定义bean的角色 APPLICATION:用户定义
* INFRASTRUCTURE:完全内部使用
* SUPPORT:某些复杂配置的一部分
*/
private int role = BeanDefinition.ROLE_APPLICATION;
private String description;
private Resource resource;
......
}
里面有用于存放解析结果的成员变量,其实跟我们的业务实体类很相似,不过他接收的是配置文件,我们接收的是页面请求,最终这些实体终将被各种业务类所使用。
解析封装
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
/**
* 第一步:将 <bean>标签解析成 BeanDefinition,并包裹于 BeanDefinitionHolder
* BeanDefinitionHolder 包装了 BeanDefinition、beanName、alias[]
*/
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 第二步:装饰 BeanDefinitionHolder
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 第三步:注册装饰后的 BeanDefinitionHolder
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
/**
* 在解析后会回调 ReaderEventListener.componentRegistered
* 默认调用 EmptyReaderEventListener,空实现
*/
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
}
首先,会把<bean>标签的各个属性解析后设置到AbstracBeanDefinition,然后再封一层BeanDefinitionHolder:
public class BeanDefinitionParserDelegate {
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// <bean id="" name="" />解析
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
// <bean name="alias1;alias2">
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 校验 id、name是否已经被使用过
checkNameUniqueness(beanName, aliases, ele);
}
// 对其他属性的处理
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 如果 id没有指定,spring会默认生成
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
} else {
/**
* 如果已注册过 beanName,会以“className#count”做区分
*/
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
/**
* 这里就是同一个 bean对应多个 beanName,例如 user#1,user#2...
* 然后使用 beanClassName作为别名
*/
aliases.add(beanClassName);
}
}
....// 日志
} catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 封装成 BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
// 基于 Stack,用于在解析过程中跟踪逻辑位置
this.parseState.push(new BeanEntry(beanName));
// <bean class="" />解析
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
// <bean parent="" />解析
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
/**
* BeanDefinitionReaderUtils.createBeanDefinition创建
* 结果 GenericBeanDefinition,封装了 beanClass/beanClassName、parent
*/
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
/**
* <bean scope="" abstract=" lazy-init="" autowire=""
* dependency-check="" depends-on="" autowire-candidate=""
* primary="" init-method="" destroy-method=""
* factory-method"="" factory-bean=""/> 解析
*/
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
/**
* <description>获取填充
*/
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
/**
* <meta key="" value="" /> 获取封装成 BeanMetadataAttribute
*/
parseMetaElements(ele, bd);
/**
* <lookup-method name="" bean="" /> 获取封装成 LookupOverride
*/
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
/**
* <replaced-method name="" replacer="" >
* <arg-type match="" />
* </replaced-method> 获取封装 ReplaceOverride
*/
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
/**
* <constructor-arg index="" name="" type="" /> 获取封装 ConstructorArgumentValues.ValueHolder
*/
parseConstructorArgElements(ele, bd);
/**
* <property name="" value=""/> 获取封装 PropertyValue
*/
parsePropertyElements(ele, bd);
/**
* <qualifier type="" value="" /> 获取封装 AutowireCandidateQualifier
*/
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
} catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
} catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
} catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
} finally {
this.parseState.pop();
}
return null;
}
}
注册操作
public class BeanDefinitionReaderUtils {
// 委托给 BeanDefinitionRegistry注册
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
/**
* 入参 getReaderContext().getRegistry():return this.reader.getRegistry()
* 结合之前创建时传入的参数 new XmlBeanDefinitionReader(beanFactory)
* 可查出 registry为 DefaultListableBeanFactory
*/
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
// 注册别名
registry.registerAlias(beanName, aliase);
}
}
}
}
注册分别是注册 BeanDefinition(用于后续的实例创建)和注册别名。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
......// 入参断言校验
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
/**
* 对于 methodOverrides的校验,即 lookup-method和 replaced-method
* 1. <factory-method>并存会抛出异常
* 2. 校验是否重载过(同名方法数目 > 1,为0会抛出异常)
*/
((AbstractBeanDefinition) beanDefinition).validate();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 处理已注册的 beanName
if (oldBeanDefinition != null) {
// 如果不允许覆盖,则抛出异常
if (!this.allowBeanDefinitionOverriding) {
......// 省略日志
}
// 判断角色权重
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
......// 省略日志
} else if (!beanDefinition.equals(oldBeanDefinition)) {
......// 省略日志
} else {
......// 省略日志
}
// 替换
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
// 检查这个工厂的 bean创建阶段是否已经开始(即是否有 bean在此期间被标记为已创建)
if (hasBeanCreationStarted()) {
// 加锁防止其他线程修改已收集元素(用于安全的迭代)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
// 拷贝更新 beanDefinitionNames
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 从手动注册单例集合中移除该 beanName
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
} else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 如果之前已存在 BeanDefinition(允许覆盖)并且单例注册列表存在该 bean
if (oldBeanDefinition != null || containsSingleton(beanName)) {
// 重置该 BeanDefinition
resetBeanDefinition(beanName);
}
}
}
注册操作其实就是将 beanName和 BeanDefinition通过 Map建立映射关系。
<beans>标签的解析就是递归调用 doRegisterBeanDefinitions。
总结
本篇主要是对4个默认标签的解析,仅靠默认标签,其实也就只能做一些 bean管理、别名,其他的例如包扫描、aop配置、定时任务等等都是在自定义标签解析处完成的,下一节将对自定义的解析作分析。