Spring源码解析3——默认标签的解析(二)
1.3、AbstractBeanDefinition属性
1.3.1、AbstractBeanDefinition.java概要
至此我们便完成了对XML 文档到GenericBeanDefinition.java 的转换, 也就是说到这里, XML中所有的配置都可以在GenericBeanDefinition.java 的实例类中找到对应的配置。
GenericBeanDefinition.java 只是子类实现,而大部分的通用属性都保存在了AbstractBeanDefinition.java中。AbstractBeanDefinition.java部分代码如下:
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
/**
* Constant for the default scope name: {@code ""}, equivalent to singleton
* status unless overridden from a parent bean definition (if applicable).
*/
public static final String SCOPE_DEFAULT = "";
/**
* Constant that indicates no external autowiring at all.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
/**
* Constant that indicates autowiring bean properties by name.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
/**
* Constant that indicates autowiring bean properties by type.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
/**
* Constant that indicates autowiring a constructor.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
/**
* Constant that indicates determining an appropriate autowire strategy
* through introspection of the bean class.
* @see #setAutowireMode
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* use annotation-based autowiring for clearer demarcation of autowiring needs.
*/
@Deprecated
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
/**
* Constant that indicates no dependency check at all.
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_NONE = 0;
/**
* Constant that indicates dependency checking for object references.
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_OBJECTS = 1;
/**
* Constant that indicates dependency checking for "simple" properties.
* @see #setDependencyCheck
* @see org.springframework.beans.BeanUtils#isSimpleProperty
*/
public static final int DEPENDENCY_CHECK_SIMPLE = 2;
/**
* Constant that indicates dependency checking for all properties
* (object references as well as "simple" properties).
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_ALL = 3;
/**
* Constant that indicates the container should attempt to infer the
* {@link #setDestroyMethodName destroy method name} for a bean as opposed to
* explicit specification of a method name. The value {@value} is specifically
* designed to include characters otherwise illegal in a method name, ensuring
* no possibility of collisions with legitimately named methods having the same
* name.
* <p>Currently, the method names detected during destroy method inference
* are "close" and "shutdown", if present on the specific bean class.
*/
public static final String INFER_METHOD = "(inferred)";
@Nullable
private volatile Object beanClass;
//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;
//依赖检查
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
//表示一个bean的实例化依靠另一个bean先实例化,对应bean属性dependOn
@Nullable
private String[] dependsOn;
/*
* autowireCandidate属性设置为false,这样容器在查找自动装配对象时,将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,
* 但是该bean本身还是可以使用自动装配来注入其他bean的
* */
private boolean autowireCandidate = true;
/*
* 自动装配时,当出现多个bean候选者时,将作为首选者,对应bean属性primary
* * */
private boolean primary = false;
/*
* 用于记录Qualifier,对应子元素qualifier
* */
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
@Nullable
private Supplier<?> instanceSupplier;
/*
* 允许访问非公开的构造器方法,程序设置
* */
private boolean nonPublicAccessAllowed = true;
/*
* 是否以一种宽松的模式解析构造函数,默认为true,
* 如果为false,则在如下情况
* interface ITest{}
* class ITestImpl implements ITest{}
* class Main{
* Main(ITest i){}
* Main(ITestImpl i){}
* }
* 抛出异常,因为Spring无法准确定位哪个构造函数
* 程序设置
* */
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、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;
/*
* 定义这个bean的应用,APPLICATION:用户INFRASTRUCTURE:完全内部使用,与用户无关,SUPPORT:某些复杂配置的一部分
* */
private int role = BeanDefinition.ROLE_APPLICATION;
/*
* bean的描述信息
* */
@Nullable
private String description;
/*
* 这个bean定义的资源
* */
@Nullable
private Resource resource;
}
1.4、解析默认标签中的自定义标签元素

1.2内容已经完成了默认标签的解析,如上图中篮色框标注的部分。接着要对红色框的部分进行分析。红色框适用场景:
<bean id="test" class="testMyClass">
<mybean:user username="aaa"/>
</bean>
1.2的节详细内容,请查看:Spring源码解析2——默认标签的解析(一)
当Spring 中的bean 使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时。
Spring中一种是默认类型的解析,另一种是自定义类型的解析,这不正是自定义类型的解析吗?为什么会在默认类型解析中单独添加一个方法处理呢?这个自定义类型并不是以Bean 的形式出现的呢?我们之前讲过的两种类型的不同处理只是针对Bean 的,这里我们看到,这个自定义类型其实是属性。

这里将函数中第三个参数设置为空,那么第三个参数是做什么用的呢?什么情况下不为空呢?其实这第三个参数是父类bean ,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition 。分析源码得知这里传递的参数其实是为了使用父类的scope 属性,以备子类若没有设置scope 时默认使用父类的属性,这里分析的是顶层配置, 所以传递null 。将第三个参数设置为空后进一步跟踪函数:

上面的代码,我们看到函数分别对元素的所有属性以及子节点进行了decoratelfRequired()函数的调用:

①、首先获取属性或者元素的命名空间, 以此来判断该元素或者属性是否适用于自定义标签的解析条件。
②、对于程序默认的标签的处理其实是直接略悔过的,因为默认的标签到这里已经被处理完了, 这里只对自定义的标签或者说对bean 的自定义属性感兴趣。
③、找出自定义类型所对应的NamespaceHandler 并进行进一步解析。
1.5、注册解析BeanDefinition
1.5.1、通过beanName注册BeanDefinition


@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);
}
}
//在spring容器中查找是否已经存在该beanName的Bean
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//如果已经存在同名的Bean,则抛出异常。
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 {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//处理并发访问
synchronized (this.beanDefinitionMap) {
//注册beanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
//记录beanName
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
//重置所有beanName对应的缓存
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
上述代码的主要逻辑如下:
①、对AbstractBeanDefinition 的校验。在解析XML 文件的时候我们提过校验,但是此校验非彼校验,之前的校验时针对于XML 格式的校验,而此时的校验时针是对于AbstractBeanDefinition的methodOverrides 属性的。
②、对beanName 已经注册的情况的处理。如果设置了不允许bean 的覆盖,则需要抛出异常,否则直接覆盖。
③、加入map 缓存。
④、清除解析之前留下的对应beanName的缓存。
1.5.2、通过别名注册BeanDefinition
@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) {
if (alias.equals(name)) {
//如果beanName与alias相同的话,不记录alias,并删除对应的alias
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->B->C时,则会抛出异常
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
上述代码的主要逻辑如下:
①、 alias 与beanName 相同情况处理。若alias 与beanName 的名称相同则不需要处理并删除掉原有alias 。
②、 alias 覆盖处理,若aliasName 已经使用并已经指向了另一个beanName 则需要用户的设置进行处理。
③、 alias 循环检查。当 A-> B 存在时,若再次出现A->C -> B 时候则会抛出异常。
④、 注册alias。
2、alias标签的解析
在对bean 进行定义时,除了使用id 属性来指定名称之外,为了提供多个名称,可以使用alias 标签来指定。而所有的这些名称都指向同一个bean ,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易地对公共组件进行引用。
然而,在定义bean 时就指定所有的别名并不是总是恰当的。有时我们期望能在当前位置为那些在别处定义的bean 引入别名。在XML配置文件中,可用单独的<alias>元素来完成bean 别名的定义。如配置文件中定义了一个JavaBean:
<bean id="testBean" class="com.test" />
要给这个JavaBean 增加别名,以方便不同对象来调用。我们就可以直接使用bean 标签中的name 属性:
<bean id="testBean" name="testBean1,testBean2" class="com.test" />
同样, Spring 还有另外一种声明别名的方式:
<bean id="testBean" class="com.test" />
<alias name="testBean" alias="testBean1,testBean2"/>
考虑一个更为具体的例子:
组件A 在XML 配置文件中定义了一个DataSource.java 类型的componentA ,但组件B 却想在其XML 文件中以componentB 命名来引用此DataSource.java类型的bean。
而且在主程序MyApp 的XML配置文件中,希望以myApp 的名字来引用此DataSource.java类型的bean。最后容器加载3 个XML 文件来生成最终的ApplicationContext 。在此情况下,可通过在配置文件中添加下列alias 元素来实现:
<alias name="componentA" alias="componentB"/>
<alias name="componentA" alias="myApp"/>
下面是Spring源码对于<alias>标签的解析:
/**
* Process the given alias element, registering the alias with the registry.
*/
protected void processAliasRegistration(Element ele) {
//获取name属性,name属性代表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));
}
}

浙公网安备 33010602011771号