文章目录
默认标签的解析
接着上文Spring——1. BeanFactory容器的初始化,在Spring中,XML配置里面有两大类型的Bean声明:默认类型Bean 和 自定义类型Bean;对于不同的类型解析的方式差别很大,这里先进行默认类型标签的解析。
DefaultBeanDefinitionDocumentReader.java
parseDefaultElement(ele, delegate);
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// import标签的解析;
// 当项目比较庞大的时候,需要进行分模块,可以使用import来导入其他模块的配置文件
// <import resource="dependency-lookup-context.xml"/>
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// alias标签的解析;
// 在定义bean时就指定所有的别名并不总是恰当的,有时候期望能在当前位置为在别处定义的bean引入别名;
// <bean id="user" class="org...."/>
// <alias name="user" alias="user-bgy1,user-bgy2"/>
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// bean标签的解析和注册
// <bean id="" class="" />
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 嵌入式 beans标签的解析;递归调用beans的解析过程
// <beans>
// <bean></bean>
// </beans>
// recurse
doRegisterBeanDefinitions(ele);
}
}
1. bean标签的解析及注册
这四种标签的解析中,bean标签的解析是最为核心的地方,也是最复杂的,所以先从bean标签的解析开始。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 委托BeanDefinitionParserDelegate 类的 parseBeanDefinitionElement 方法进行元素解析,返回BeanDefinitionHolder 类型的实例;
// BeanDefinitionHolder:Holder for a BeanDefinition with name and aliases.
// bdHolder实例已经包含了配置文件中配置的各种属性了(class、name、id、alias)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 如果需要的话,就对BeanDefinition进行装饰
/**
* 适用场景:
* 当spring中的bean使用的是默认的标签配置,但是其中的子元素是自定义配置(子元素,不是以bean的形式存在,是一个属性)
*
* <bean id="test" class="test.MyClass">
* <mybean:user username="aaa"/>
* </bean>
*/
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 解析、装饰 完成后,对于得到的BeanDefinition已经可以满足后续的使用要求了,只需要进行注册了;
// 对解析后的 BeanDefinition 进行注册
try {
// 注册操作委托给了 BeanDefinitionReaderUtils
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发出响应事件,通知相关的监听器,这个bean的解析和注册 已经完成了
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
代码上的注释基本都解释了每个步骤的内容,下面来具体看看各个步骤的实现。
1.1 解析BeanDefinition
BeanDefinitionParserDelegate.java
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取id
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取name
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 分割name属性(可能有多个name),设置别名
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
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.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 解析标签其他属性,并封装到 GenericBeanDefinition
/**
* 在这里是完成,从 XML文档 到 GenericBeanDefinition 的转换,也就是说
* XML中的所有配置,都可以在 GenericBeanDefinition 的实例中找到对应的配置属性
*/
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
// 如果beanName不存在,就根据spring中提供的命名规则为当前的bean生成对应的beanName
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
// 生成beanName
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
} catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 将获取到的信息封装到 BeanDefinitionHolder 实例中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
这个方法中完成了以下工作:
- 获取到元素的id以及name属性,并且检查beanName是否已经被使用了;
- 解析元素的其他所有属性,封装到 GenericBeanDefinition的实例中;即把XML配置文件转换成Java对象的过程;
- 检测到如果这个bean没有指定beanName,就使用默认规则为这个bean生成一个beanName;
-
- 将这些解析到的结果封装到 BeanDefinitionHolder中;
进一步查看步骤2中,对XML配置文件其他属性的解析过程:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 解析class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// 解析parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建用于承载属性的 AbstractBeanDefinition类型的 GenericBeanDefinition(<bean>元素标签在容器中的内部表示形式)
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 硬编码解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
/**
* <bean id="user" class="org.springframework.beans.example.bean.User">
* <meta key="testStr" value="aaa"/>
* </bean>
*
*/
// 解析子元素meta
parseMetaElements(ele, bd);
// 解析子元素 lookup-method 属性
// 获取器注入:获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里配置的;
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析子元素 replaced-method属性
// 方法替换:可以在运行时使用新的方法替换现有的方法(不但可以动态地替换返回实体bean,还能动态地更改原有方法的逻辑)
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析子元素 constructor-arg
/**
* <bean id="testBean" class="org.TestBean>
* <constructor-arg>
* <value>a</value>
* </constructor-arg>
* </bean>
*/
parseConstructorArgElements(ele, bd);
// 解析 Property子元素
/**
* <bean id="testBean" class="org.TestBean>
* <property name="testStr" value="aaa"/>
* </bean>
*
* 或
*
* <bean id="testBean" class="org.TestBean>
* <property name="testList">
* <list>
* <value>aa</value>
* <value>bb</value>
* </list>
* </property>
* </bean>
*
* property中必须含有name属性
*/
parsePropertyElements(ele, bd);
// 解析 Qualifier子元素
/**
* spring允许通过Qualifier指定注入bean的名称
*
* <bean id="myTestBean" class="bean.MyTestBean">
* <qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="qf"/>
* </bean>
*
* Qualifier中必须含有type属性
*/
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;
}
bean标签中的所有属性都在这里被解析完成,下面继续看一些复杂标签的属性解析:
1.1.1 创建用于承载属性的 BeanDefinition
BeanDefinition是一个接口,抽象类 AbstractBeanDefinition实现了这个接口;AbstractBeanDefinition是配置文件 元素标签在容器中的内部表现形式。元素标签中有 class、scope、lazy-init等属性,AbstractBeanDefinition中则提供了相应的 beanClass、scope、lazyInit属性,AbstractBeanDefinition和中的属性是一一对应的。
1.1.1.1 AbstractBeanDefinition
AbstractBeanDefinition.java
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
// bean的作用范围,对应bean属性 scope
@Nullable
private String scope = SCOPE_DEFAULT;
// 是否是抽象,对应bean属性 abstract
private boolean abstractFlag = false;
// 是否延迟加载,对应bean属性 lazy-init
@Nullable
private Boolean lazyInit;
// 自动注入模式,对应bean属性 autowire
private int autowireMode = AUTOWIRE_NO;
// 依赖检查,3.0 后弃用此属性
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
// 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性 depend-on
@Nullable
private String[] dependsOn;
// autowire-candidate 属性设置为false,容器在查找自动装配对象时,将不考虑该bean;
// 即它不会被考虑作为其他bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他 bean
// 对应bean属性 autowire-candidate
private boolean autowireCandidate = true;
// 自动装配时出现多个bean后选择,将作为首选者,对应bean属性 primary
private boolean primary = false;
// 用于记录Qualifier,对应bean属性 子元素qualifiers
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
@Nullable
private Supplier<?> instanceSupplier;
// 允许访问非公开的构造器和方法,程序设置
private boolean nonPublicAccessAllowed = true;
// 是否以一种宽松的模式解析构造函数,默认为true
private boolean lenientConstructorResolution = true;
// 对应bean属性 factory-bean
/**
* 用法:
* <bean id="instanceFactoryBean" class="example.chapter3.InstanceFactoryBean"/>
* <bean id="currentTime" factory-bean="instanceFactoryBean" factory-method="createTime"/>
*/
@Nullable
private String factoryBeanName;
// 对应bean属性 factory-method
@Nullable
private String factoryMethodName;
// 记录构造函数注入属性,对应bean属性 子元素 constructor-arg
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
// 记录普通属性集合,对应bean属性 子元素 property
@Nullable
private MutablePropertyValues propertyValues;
// 方法重写的持有者,记录lookup-method、replaced-method元素,对应bean属性 子元素 lookup-method、replaced-method
private MethodOverrides methodOverrides = new 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;
private int role = BeanDefinition.ROLE_APPLICATION;
// bean的描述信息
@Nullable
private String description;
// 这个bean定义的资源
@Nullable
private Resource resource;
//省略其他方法
}
抽象类AbstractBeanDefinition有三种具体实现:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。其中RootBeanDefinition是常用的实现类,对应一般性的元素标签;GenericBeanDefinition是2.5版本后新加入的实现类,是一站式服务类,基本实现了所有属性;在XML配置文件中可以定义父和子,父用RootBeanDefinition表示,子用ChildBeanDefinition表示;
1.1.1.2 GenericBeanDefinition
GenericBeanDefinition.java
public class GenericBeanDefinition extends AbstractBeanDefinition {
@Nullable
private String parentName;
// 忽略其他方法
}
Spring通过 BeanDefinition将配置文件中的配置信息转换成了容器的内部表示形式。因此,要解析属性首先就要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例:
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
BeanDefinitionReaderUtils.java
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
// 如果classLoader不为空,就使用传入的classLoader同一虚拟机加载类对象,否则只是记录 className
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
在这里如果传入的classLoader不为空,就直接使用这个classLoader根据解析出来的className通过反射创建对应的Class对象;如果为空,就只是设置className。
1.1.2 解析各种属性
在创建了bean信息的承载实例后,就可以进行bean信息的各种属性的解析了:
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
// 解析scope属性
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
} else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
// 在嵌入 BeanDefinition情况下,且没有单独制定scope属性,则使用父类默认的属性
bd.setScope(containingBean.getScope());
}
// 解析 abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 解析 lazy-init属性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
// 如果没有设置lazy-init属性,或者被设置为其他字符,都会被设置为false
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 解析 autowire属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
// 设置自动装配的模式
bd.setAutowireMode(getAutowireMode(autowire));
// 解析 dependency-on 属性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 解析 autowire-candidate 属性
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
} else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 解析primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 解析 init-method 属性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
} else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 解析 destroy-method 属性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
} else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析 factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析 factory-bean属性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
在这个方法里面,Spring对bean的所有属性进行一一的解析,解析之后设置到标签的属性承载实例 GenericBeanDefinition中。
1.1.3 解析子元素 meta
parseMetaElements(ele, bd);
元数据meta的使用:
<bean id="user" class="org.springframework.beans.example.bean.User">
<meta key="testStr" value="aaa"/>
</bean>
标签并不会体现在User的属性中,而是一个额外的声明,当需要使用里面的信息的时候,可以通过 BeanDefinition的getAttribute(key)方法进行获取。
回到代码:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element)node;
// 提取meta
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
// 使用key value构造BeanMetadataAttribute
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
1.1.4 解析子元素 lookup-method
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
子元素 lookup-method不是很常用,但是在某些时候也是非常有用的属性,通常称它为 获取器注入。
获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean;但实际上要返回的具体bean是在xml配置文件里配置的,这个方法就可以用在设计有些可插拔的功能上。
使用示例:
- 首先创建一个父类:
public class User {
public void showMe() {
System.out.println("i am user.");
}
}
- 创建一个子类并覆盖父类的showMe()方法:
public class Teacher extends User {
@Override
public void showMe() {
System.out.println("i am teacher.");
}
}
- 创建一个调用类,使用获取器注入:
public abstract class LookupTest {
// 返回一个User类型的bean,但可以有User的不同实现类
public abstract User getUser();
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:lookup-method-context.xml");
LookupTest lookupTest = (LookupTest)applicationContext.getBean("lookupTest");
User user = lookupTest.getUser();
user.showMe();
}
}
到这里整个测试方法已经完成了,但是会有一个疑问:这个抽象类的抽象方法都没有被实现,怎么可以直接调用呢?
原因就是需要使用Spring提供的获取器注入,xml配置文件为:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="lookupTest" class="org.bgy.spring.study.spring.bean.definition.lookup.method.LookupTest">
<lookup-method name="getUser" bean="teacher"/>
</bean>
<bean id="teacher" class="org.bgy.spring.study.spring.bean.definition.lookup.method.Teacher"/>
</beans>
在这个配置文件中,使用了 lookup-method 子元素,这个配置的功能就是动态地将teacher的bean,作为getUser()方法的返回值,实现了把bean注入到LookupTest中。
此时控制台会打印:i am teacher.
如果业务变更了,我们需要有不同的User来实现不同的showMe()的逻辑的时候,我们可以增加新的逻辑类:
public class Student extends User {
@Override
public void showMe() {
System.out.println("i am student.");
}
}
同时修改配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="lookupTest" class="org.bgy.spring.study.spring.bean.definition.lookup.method.LookupTest">
<lookup-method name="getUser" bean="student"/>
</bean>
<bean id="teacher" class="org.bgy.spring.study.spring.bean.definition.lookup.method.Teacher"/>
<bean id="student" class="org.bgy.spring.study.spring.bean.definition.lookup.method.Student"/>
</beans>
此时控制台会打印:i am student.
回到代码:
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 仅在spring默认bean的子元素下,且为 <lookup-method>时有效
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element)node;
// 获取要修饰的方法
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
// 获取配置中要返回的 bean
String beanRef = ele.getAttribute(BEAN_ELEMENT);
// 构造一个 LookupOverride 属性(也是MethodOverride属性)
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
// 记录在了BeanDefinition的 methodOverride属性中
overrides.addOverride(override);
}
}
}
1.1.5 解析子元素 replaced-method
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
方法替换:Spring可以在运行时用新的方法替换现有的方法;不但可以动态地替换返回的bean类型,还能动态地更改原有方法的逻辑。
使用示例:
- 先创建一个带有changeMe()方法的测试类,并且增加main()方法进行测试:
public class ReplacedTest {
public void changeMe() {
System.out.println("change me.");
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:replaced-method-context.xml");
ReplacedTest replacedTest = (ReplacedTest)applicationContext.getBean("replacedTest");
replacedTest.changeMe();
}
}
- 在需要改变changeMe()方法的逻辑的时候,创建一个替换类:
public class MethodReplacerTest implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("changed.");
return null;
}
}
- 在xml文件中进行配置,使替换类生效:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="replacedTest" class="org.bgy.spring.study.spring.bean.definition.replaced.method.ReplacedTest">
<replaced-method name="changeMe" replacer="methodReplacerTest"/>
</bean>
<bean id="methodReplacerTest" class="org.bgy.spring.study.spring.bean.definition.replaced.method.MethodReplacerTest"/>
</beans>
此时控制台会打印:changed.
使用了标签,把replacedTest的bean的changeMe()方法的返回值和具体的实现逻辑,动态替换成了methodReplacerTest的bean中的重载方法。
回到代码:
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 仅在spring默认bean的子元素下,且为 <replaced-method>时有效
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element)node;
// 获取要替换的旧的方法
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
// 获取对应的新的替换方法
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
// 构造一个 ReplaceOverride 属性(也是MethodOverride属性)
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
// 记录参数
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
// 记录在了BeanDefinition的 methodOverride属性中
overrides.addOverride(replaceOverride);
}
}
这里基本就只比上面的子元素 lookup-method 多了一个记录参数的步骤;最终都是把 methodOverride 记录在BeanDefinition中。
1.1.6 解析子元素 constructor-arg
parseConstructorArgElements(ele, bd);
xml中使用构造函数注入,应该是使用最多的方式之一,所以对构造函数的解析是非常常用的,但是也是非常复杂的。
使用示例:
- 首先创建一个有两个属性的bean类:
public class ConstructorArg {
private String name;
private String age;
public ConstructorArg() {
}
public ConstructorArg(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
- 使用xml对这个bean进行构造器注入:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="constructorArg" class="org.bgy.spring.study.spring.bean.definition.constructor.arg.ConstructorArg">
<!-- 通过index属性-->
<constructor-arg index="0" value="bgy">
<!-- <value>bgy</value> -->
</constructor-arg>
<!-- 通过name属性-->
<constructor-arg name="age" value="25">
<!-- <value>25</value> -->
</constructor-arg>
</bean>
</beans>
这样就可以实现,对constructorArg这个bean自动寻找对应这两个参数的构造函数,并在初始化的时候将设置的参数传入进去。
回到代码:
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 遍历所有子元素,提取所有的 constructor-arg,进行解析
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 解析 constructor-arg
parseConstructorArgElement((Element)node, bd);
}
}
}
跟进:
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取index属性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取type属性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 如果配置了 index属性
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
} else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 解析ele对应的属性元素
Object value = parsePropertyValue(ele, bd, null);
// 创建一个 ConstructorArgumentValues.ValueHolder 实例,用于封装解析出来的元素;
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 并将type、name、index等属性一起封装到ConstructorArgumentValues.ValueHolder中
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
} else {
// 添加到当前BeanDefinition的ConstructorArgumentValues的 indexedArgumentValues属性中
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
} finally {
this.parseState.pop();
}
}
} catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
// 如果没有index属性则忽略此属性,自动寻找
} else {
try {
this.parseState.push(new ConstructorArgumentEntry());
// 解析ele对应的属性元素
Object value = parsePropertyValue(ele, bd, null);
// 创建一个 ConstructorArgumentValues.ValueHolder 实例,用于封装解析出来的元素;
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 并将type、name、index等属性一起封装到ConstructorArgumentValues.ValueHolder中
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 添加到当前BeanDefinition的ConstructorArgumentValues的 genericArgumentValues属性中
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
} finally {
this.parseState.pop();
}
}
}
在这个方法中:
- 提取constructor-arg上必要的元素(index、type、name);
- 判断是否指定了index属性,如果指定了先判断index不小于0;
- 解析constructor-arg的子元素;
- 使用 ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素;将type、name、index等属性都set进ConstructorArgumentValues.ValueHolder中;
- 如果指定了index,就把index和valueHolder添加到当前的BeanDefinition的 ConstructorArgumentValues 的 indexedArgumentValues 属性中;
- 如果没有指定index,就把ValueHolder添加到当前的 BeanDefinition的 ConstructorArgumentValues 的 genericArgumentValues 属性中;
可以看到,对于是否指定index,Spring的处理流程是不同的,关键在于属性信息的保存位置是indexedArgumentValues 还是 genericArgumentValues。
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
// 一个属性只能对应一种类型:ref、value、list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果是 description或者meta就不处理
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
} else {
subElement = (Element)node;
}
}
}
// 提取 <constructor-arg>标签中的 ref和value属性,以便于根据规则验证正确性;
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 不能同时有 ref和value;
// 或有ref或者value的时候,不能有子元素
if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
// ref 属性的处理
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
// 使用RuntimeBeanReference封装对应的 ref名称, 如 <constructor-arg ref="a">
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
} else if (hasValueAttribute) { // value属性的处理
// 使用TypedStringValue 封装对应的 value, 如 <constructor-arg value="a">
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
} else if (subElement != null) {
// 解析子元素
/**
* <constructor-arg>
* <map>
* <entry key="key" value="value"/>
* </map>
* </constructor-arg>
*/
return parsePropertySubElement(subElement, bd);
} else {
// Neither child element nor "ref" or "value" attribute found.
// 既没有 ref有没有value,也没有子元素,会报错
error(elementName + " must specify a ref or value", ele);
return null;
}
}
这个方法不止用于解析constructor-arg标签的元素,还可以用于解析其他的property元素;如果用于解析constructor-arg元素的时候,propertyName为null。
解析步骤:
- 一个constructor-arg标签对应于构造函数中的一个属性,一个属性只能有一种类型(ref、value、list)。
先遍历constructor-arg下所有子元素,如果是description或者meta属性,直接跳过不处理;否则记录下子元素,如果有多个,抛出error错误。 - 提取constructor-arg标签上的ref和value属性,并且根据规则验证其正确性;规则:
- 同时既有ref属性又有value属性,错误;
- 存在ref属性或者value属性的时候,又有子元素,错误;
- 根据constructor-arg标签指定的属性情况,进行处理:
- 如果是指定了ref属性,使用 RuntimeBeanReference 封装ref对应的bean名称,如:,并且返回RuntimeBeanReference;
- 如果是指定了value属性,使用TypedStringValue封装value对应的具体指,如:,并且返回使用TypedStringValue封装value对应的具体指;
- 如果都没有指定,且subElement不为null,则是使用了子元素的方式进行指定,则需要解析子元素;
- 如果都不满足,抛出error错误;
继续跟进对于各种子元素的分类处理:
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
if (!isDefaultNamespace(ele)) {
// 如果不是默认命名空间,调用解析自定义元素的方法进行解析;(会根据自定义的解析器进行解析)
return parseNestedCustomElement(ele, bd);
} else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
// 如果子元素的bean,则又递归调用bean标签的解析过程;
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
} else if (nodeNameEquals(ele, REF_ELEMENT)) { // 对ref子元素的解析
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
} else if (nodeNameEquals(ele, IDREF_ELEMENT)) { // 对idref子元素的解析
return parseIdRefElement(ele);
} else if (nodeNameEquals(ele, VALUE_ELEMENT)) { // 对value子元素的解析
return parseValueElement(ele, defaultValueType);
} else if (nodeNameEquals(ele, NULL_ELEMENT)) { // 对null子元素的解析
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
} else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { // 解析array子元素
return parseArrayElement(ele, bd);
} else if (nodeNameEquals(ele, LIST_ELEMENT)) { // 解析list子元素
return parseListElement(ele, bd);
} else if (nodeNameEquals(ele, SET_ELEMENT)) { // 解析set子元素
return parseSetElement(ele, bd);
} else if (nodeNameEquals(ele, MAP_ELEMENT)) { // 解析map子元素
return parseMapElement(ele, bd);
} else if (nodeNameEquals(ele, PROPS_ELEMENT)) { // 解析props子元素
return parsePropsElement(ele);
} else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
解析步骤:
- 首先判断是不是自定义子元素,如果是的话就调用parseNestedCustomElement()方法进行解析,这个方法里面会获取到自定义元素的自定义解析器NamespaceHandler(下一章会讲)进行解析;
- 如果子元素是bean,则又会递归调用bean标签的解析步骤;
- 剩下的就是对ref、value、list…等元素的解析;
总的来说,在这个方法中,实现了所有支持的子元素的解析处理,到这里构造函数的解析流程基本完成。
1.1.7 解析子元素 property
parsePropertyElements(ele, bd);
xml中使用setter方法注入,也是使用得最多的方式之一。
使用示例:
<bean id="people" class="org.bgy.spring.study.spring.bean.definition.property.People">
<property name="name" value="bgy"/>
<property name="age" value="25"/>
</bean>
现在来看一下对于setter方法注入的property子元素的解析:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 遍历所有元素,解析property属性的元素
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element)node, bd);
}
}
}
跟进:
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 获取配置元素中 name 的值
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
// 不允许多次对同一属性进行配置
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 解析ele对应的属性元素
Object val = parsePropertyValue(ele, bd, propertyName);
// 创建一个 PropertyValue 实例,用于封装解析出来的元素
PropertyValue pv = new PropertyValue(propertyName, val);
// 可能存在 meta,解析meta
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
// 添加到当前BeanDefinition的MutablePropertyValues的 propertyValueList属性中
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
}
}
解析步骤:
- 这里先通过BeanDefinition的processedProperties属性判断当前property属性是否已经被解析过了,如果解析过了直接抛出error异常;
- 根据propertyName来解析property元素;
- 把解析出来的元素封装到PropertyValue中;
- 记录到过BeanDefinition的processedProperties属性中;
这里你会发现,解析元素的的时候,使用的是我们上面的parsePropertyValue方法,只是propertyName参数不是null了,而是property标签的propertyName;所以解析方式跟上面一样。
1.1.8 解析子元素 qualifier
parseQualifierElements(ele, bd);
qualifier属性的使用:
- 在Spring中,进行自动注入的时候,Spring容器中匹配的bean的数量必须有且只有一个;如果按照类型匹配,可能出现两个相同类型的bean,这个时候Spring容器就会抛出 NoUniqueBeanDefinitionException的异常,并且指出期望的是单个匹配的bean。
这个时候,就可以使用qualifier标签来手动指定匹配的bean:<bean id="myTestBean" class="bean.MyTestBean"> <qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="qf"/> </bean>
- 不过实际中,我们接触更多的是使用 @Qualifier注解的形式,特别是使用@Autowired注解时;详细示例可见:@Qualifier 详细解析
回到代码:
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 遍历所有元素,解析qualifier属性的元素
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
parseQualifierElement((Element)node, bd);
}
}
}
跟进:
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 获取配置元素中 type的值
String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
if (!StringUtils.hasLength(typeName)) {
error("Tag 'qualifier' must have a 'type' attribute", ele);
return;
}
this.parseState.push(new QualifierEntry(typeName));
try {
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
String value = ele.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(value)) {
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
}
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
Element attributeEle = (Element)node;
String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle));
qualifier.addMetadataAttribute(attribute);
} else {
error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
return;
}
}
}
// 添加到当前BeanDefinition的 qualifiers属性中
bd.addQualifier(qualifier);
} finally {
this.parseState.pop();
}
}
这里主要就是创建一个AutowireCandidateQualifier类的实例qualifier,然后把解析到的各个属性封装到这个实例中,最终把这个类的实例添加到BeanDefinition的 qualifiers属性中。
接着,设置资源,返回BeanDefinition:
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
至此,我们完成了整个对XML文档到 GenericBeanDefinition的转换过程,也就是说,XML文档中的所有配置都可以在 GenericBeanDefinition的实例中找到对应的配置。
回到这里把BeanDefinition封装到BeanDefinitionHolder中,并返回BeanDefinitionHolder的实例bdHolder;返回之后,要做的是:如果需要的话,就对BeanDefinition进行装饰:
DefaultBeanDefinitionDocumentReader.java
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 如果需要的话,就对BeanDefinition进行装饰
/**
* 适用场景:
* 当spring中的bean使用的是默认的标签配置,但是其中的子元素是自定义配置(子元素,不是以bean的形式存在,是一个属性)
*
* <bean id="test" class="test.MyClass">
* <mybean:user username="aaa"/>
* </bean>
*/
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 省略其他部分
}
}
适用场景在源码注解中也已经写了:XML文件中的标签使用的是默认标签,但是其中的子元素使用了自定义的标签元素(这个自定义类型不是以Bean的形式出现的,而是以属性的形式出现的),这个时候就需要再对子元素的自定义标签进行解析。
1.2 解析默认标签中的自定义标签元素
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
BeanDefinitionParserDelegate.java
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
这里的第三个参数是父类BeanDefinition(作用为使用父类的scope属性),当对某个嵌套配置进行分析时,这里需要传递父类的BeanDefinition;这里解析的是顶层配置,所以传递null。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();
// 遍历所有的属性,看看是否有适合于修饰的属性
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();
// 遍历所有的子元素,看看是否有适合修饰的子元素
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
在上面的代码中,分别对这个自定义元素的所有属性 以及子节点进行了decorateIfRequired,继续跟进:
public BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
// 获取自定义标签的命名空间
String namespaceUri = getNamespaceURI(node);
// 对于非默认标签进行修饰(对于默认标签的处理是直接跳过的,因为默认标签到这里已经被处理完了,这里只对自定义的标签或者bean的自定义属性处理)
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
// 根据命名空间找到对应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
// 进行修饰
BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
} else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
} else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
在这里首先获取自定义标签的命名空间,并且只处理非默认命名空间的标签(因为默认标签的解析到这里是已经被处理完了的);然后根据自定义标签的命名空间,找到对应的自定义解析器NamespaceHandler进行解析。(跟这里的解析一样)
1.3 注册解析到的BeanDefinition
来到这里,对于XML配置文件,解析也解析完了装饰也装饰完了,这个时候的BeanDefinition已经可以满足后续的使用要求了,现在只需要再对BeanDefinition进行注册:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 委托BeanDefinitionParserDelegate 类的 parseBeanDefinitionElement 方法进行元素解析,返回BeanDefinitionHolder 类型的实例;
// BeanDefinitionHolder:Holder for a BeanDefinition with name and aliases.
// bdHolder实例已经包含了配置文件中配置的各种属性了(class、name、id、alias)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 如果需要的话,就对BeanDefinition进行装饰
/**
* 适用场景:
* 当spring中的bean使用的是默认的标签配置,但是其中的子元素是自定义配置(子元素,不是以bean的形式存在,是一个属性)
*
* <bean id="test" class="test.MyClass">
* <mybean:user username="aaa"/>
* </bean>
*/
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 解析、装饰 完成后,对于得到的BeanDefinition已经可以满足后续的使用要求了,只需要进行注册了;
// 对解析后的 BeanDefinition 进行注册
try {
// 注册操作委托给了 BeanDefinitionReaderUtils
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发出响应事件,通知相关的监听器,这个bean的解析和注册 已经完成了
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
BeanDefinitionReaderUtils.java
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用beanName做唯一注册标识
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
// 使用beanName的方式 注册 BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 遍历注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 通过别名的方式 注册BeanDefinition
registry.registerAlias(beanName, alias);
}
}
}
在上面的代码中,把解析得到的BeanDefinition注册到了BeanDefinitionRegistry类型的实例registry中,而对于BeanDefinition的注册分为两个部分:通过beanName的方式注册 以及 通过别名的方式注册。
1.3.1 通过beanName注册BeanDefinition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
DefaultListableBeanFactory.java
//---------------------------------------------------------------------
// Implementation of BeanDefinitionRegistry interface
//---------------------------------------------------------------------
@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文件校验
* 主要是对于AbstractBeanDefinition 属性中的methodOverrides校验
* 校验methodOverrides 是否与工厂方法并存,或者methodOverrides对应的方法根本不存在
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 使用了 ConcurrentHashMap,线程安全
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 如果这个beanName已经注册了
if (existingDefinition != null) {
// 如果不允许被覆盖,直接抛出异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("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.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 加入map缓存,注册BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 这个beanName没有被注册
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 加入map缓存,注册BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
// 加入map缓存,注册BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置beanName对应的缓存
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
主要步骤:
- 最后再对BeanDefinition进行一次校验,这里校验的主要是对于 methodOverrides 属性的校验:
- methodOverrides属性和工厂方法一起存在时,抛出异常;
- 检查methodOverrides对应的方法不存在时,抛出异常;
- beanName已经注册的情况下,如果设置了不允许bean的覆盖,则抛出异常;否则直接覆盖并加入map缓存,即注册BeanDefinition :
this.beanDefinitionMap.put(beanName, beanDefinition);
- beanName没有被注册的情况下,使用同步锁进行加入map缓存,,即注册BeanDefinition :
this.beanDefinitionMap.put(beanName, beanDefinition);
- 清除解析之前的beanName对应的缓存;
1.3.2 通过别名注册BeanDefinition
void registerAlias(String name, String alias);
SimpleAliasRegistry.java
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) {
// 如果beanName与alias相同的话,不记录alias,并删除对应的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);
// 如果这个alias已经被注册了
if (registeredName != null) {
// 并且注册的beanName是当前要注册的beanName,直接返回,不需要重复注册
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 并且注册的beanName不是当前的,也就是说被指向了别的beanName;
// 并且还不允许覆盖的话,抛出异常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// alias循环检查
checkForAliasCircle(name, alias);
// 加入map,注册alias
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
主要步骤:
- 如果beanName与alias相同的话则不需要处理,并且删除原有aliasMap缓存中的alias;
- 如果这个alias已经被注册过了,分为两种情况:
- 注册的beanName就是当前的beanName,则不需要重复注册,直接返回;
- 如果注册的beanName不是当前的beanName,再判断是否允许覆盖,如果不允许则抛出异常;
- alias的循环检查:若 A -> B 存在时,如果再出现 A -> C -> B 的情况,则抛出异常;
- 把alias和对应的beanName注册到aliasMap缓存中;
1. 4 解析及注册完成,通知监听器
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
这里的实现只是为了扩展,目前Spring中没有对这个事件做任务逻辑的处理;当开发人员需要对注册BeanDefinition事件进行监听时,可以通过注册监听器的方式,并将处理逻辑写入监听器中。
2. 嵌入式beans标签的解析
回到开头,我们对配置文件的解析包括了:import标签、alias标签、bean标签、beans标签;通过上面的内容我们已经完成了对最复杂的bean标签的解析,接下就继续对beans标签的解析。
嵌入式beans标签的使用示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean></bean>
<beans>
<bean></bean>
</beans>
</beans>
DefaultBeanDefinitionDocumentReader.java
doRegisterBeanDefinitions(ele);
protected void doRegisterBeanDefinitions(Element root) {
// 专门处理解析
// BeanDefinitionParserDelegate 用于解析 xml bean 的状态委托类
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 在这里如果是默认的命名空间,即:http://www.springframework.org/schema/beans
if (this.delegate.isDefaultNamespace(root)) {
// 处理profile属性(可以配置多套不同的开发环境,便于切换)
// 获取bean节点是否定义了profile属性,
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
// profile可以同时指定多个
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
// 判断每一个profile是否都符合环境变量中所定义的,如果不符合则不会浪费性能去解析
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 解析前处理,留给子类实现(空方法,面向继承设计的,如果继承自DefaultBeanDefinitionDocumentReader的类,
// 需要在Bean解析前后做一些处理的话,就可以重写这两个方法)
preProcessXml(root);
// 正式解析 BeanDefinition
parseBeanDefinitions(root, this.delegate);
// 解析后处理,留给子类实现(空方法,面向继承设计的,如果继承自DefaultBeanDefinitionDocumentReader的类,
// 需要在Bean解析前后做一些处理的话,就可以重写这两个方法)
postProcessXml(root);
this.delegate = parent;
}
可以看到在这里调用了上一篇文章Spring——1. BeanFactory容器的初始化中 2.2.1 解析BeanDefinitions 讲到的doRegisterBeanDefinitions()方法,其实就是递归调用注册解析BeanDefinitions的过程,等于把本篇文章的所有流程再走一遍。
3. import标签的解析
在比较庞大的Spring项目中,如果整个项目使用一个Spring的配置文件,那么编写和维护起来都会特别麻烦。
Spring提供了import的方法,可以使得用户对于项目中的进行分模块编写配置文件,然后使用import方法导入到 applicationContext.xml 配置文件中:
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<import resource="customerContext.xml" />
<import resource="manageContext.xml" />
<import resource="systemContext.xml" />
</beans>
如果以后有新模块的加入,就可以简单的修改这个配置文件进行导入;这样简化了配置文件的维护,并且使得项目模块化,易于管理。
回到代码看看Spring是如何解析import标签的:
importBeanDefinitionResource(ele);
protected void importBeanDefinitionResource(Element ele) {
// 获取resource属性所表示的路径
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
if (!StringUtils.hasText(location)) {
// 如果不存在resource,不做任何处理
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// 解析路径中的系统属性,格式如 ${user.dir}
// Resolve system properties: e.g. "${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 判断location是相对URI还是绝对URI
// Discover whether the location is an absolute or relative URI
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?
if (absoluteLocation) {
// 如果是绝对URI,则直接根据地址加载对应的配置文件
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isTraceEnabled()) {
logger.trace("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存在多个实现类,如 ClassPathResource、FileSystemResource等;
// 而每个resource的createRelative方式实现都不一样,所以这里先使用子类的方法尝试解析
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isTraceEnabled()) {
logger.trace("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));
}
主要步骤:
- 获取resource属性中指定的资源路径location,如果没有指定则直接返回不做任何处理;
- 解析指定的路径中的系统属性,因为可以通过"${user.dir}"等方式直接设置路径值;
- 判断location是绝对路径还是相对路径;
- 如果是绝对路径,则递归调用XML文档的解析过程,进行另一个XML文档的解析;
- 如果是相对路径,则直接根据这个相对路径创建出一个相对资源relativeResource,并判断这个资源是否存在:
- 如果存在,也是递归调用XML文档的解析过程;
- 如果不存在,则拿到父XML文档的路径,并且跟相对路径拼接出来一个绝对路径,再递归调用XML文档的解析过程;
- 解析完成后,通知监听器;
这里的递归调用XML文档的解析过程,其实就是根据location获取到resource,然后调用上一篇文章中讲的 2. 加载BeanDefinitions 步骤进行解析:
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
4. alias标签的解析
在对bean进定义的时候,除了使用id属性来指定名称之外,还可以提供多个名称,Spring中使用alias标签来实现这个功能。alias标签中所有的名称都指向同一个bean,在某些情况下提供别名非常有用,比如让应用的每个组件都能更容易地对公用组件进行引用。
使用方式:
- 直接使用bean标签中的name属性:
<bean id="user" name="user1,user2" class="org...."/>
- 单独使用alias标签来指定别名:
<bean id="user" class="org...."/>
<alias name="user" alias="user1,user2"/>
一个具体的使用示例:
模块A在XML配置文件中定义了一个名为 componentA的DataSource类型的bean;但模块B想在自己的XML文件中以componentB来引用此bean;模块C又要想在自己的XML文件中以componentC来引用此bean;这样的话就可以这样配置:
<bean id="componentA" class="org...."/>
<alias name="componentA" alias="componentB"/>
<alias name="componentA" alias="componentC"/>
这样一来,各个模块都可以通过唯一的名字来引用同一个数据源,而互不干扰,结构和使用都更清晰。
回到代码:
processAliasRegistration(ele);
protected void processAliasRegistration(Element ele) {
// 获取name
String name = ele.getAttribute(NAME_ATTRIBUTE);
// 获取 alias
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
// 必须要有name
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
// 必须要有alias
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
// name和alias都有的情况下,验证通过,valid为true
if (valid) {
try {
// 注册alias,将alias与beanName组成一对注册到registry中
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
// 注册alias之后,通知监听器做相应的处理
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
这里主要就是先对name和alias做验证,并且在name和alias都存在的情况下才去解析;解析的过程,就是上面bean中 通过别名注册BeanDefinition 的过程。
5. 整体流程思维导图
最后附上一个整体流程的思维导图:Spring容器初始化体系,本篇文章对应其中的 默认标签的解析 部分。