文前说明
作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。
本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。
1. 概述
- Spring 提供有两种解析 Bean 的方式。
- 如果根节点或者子节点采用默认命名空间,则调用
parseDefaultElement()
方法,进行 默认标签解析。 - 否则,调用
BeanDefinitionParserDelegate#parseCustomElement()
方法,进行 自定义解析。
- 如果根节点或者子节点采用默认命名空间,则调用
// DefaultBeanDefinitionDocumentReader.java
public static final String IMPORT_ELEMENT = "import";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
public static final String NESTED_BEANS_ELEMENT = "beans";
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // import
importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // alias
processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // bean
processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // beans
// recurse
doRegisterBeanDefinitions(ele);
}
}
2. 原理
2.1 默认标签解析
- 例如:
<bean id="studentService" class="org.springframework.core.StudentService" />
2.1.1 解析 import 标签
- Spring 提供了一个分模块的思路,将配置分到不同的模块,再利用 import 标签导入。
- 如果有配置需要修改直接修改相应配置文件即可。
- 若有新的模块需要引入直接增加 import 即可。
<?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.xsd">
<import resource="spring-student.xml"/>
<import resource="spring-student-dtd.xml"/>
</beans>
2.1.1.1 importBeanDefinitionResource
// DefaultBeanDefinitionDocumentReader.java
/**
* Parse an "import" element and load the bean definitions
* from the given resource into the bean factory.
*/
protected void importBeanDefinitionResource(Element ele) {
// 1. 获取 resource 的属性值
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 为空,直接退出
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele); // 使用 problemReporter 报错
return;
}
// 2. 解析系统属性,格式如 :"${user.dir}"
// Resolve system properties: e.g. "${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
// 实际 Resource 集合,即 import 的地址,有哪些 Resource 资源
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 3. 判断 location 是相对路径还是绝对路径
// 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?
// 4. 绝对路径
if (absoluteLocation) {
try {
// 添加配置文件地址的 Resource 到 actualResources 中,并加载相应的 BeanDefinition 们
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);
}
// 5. 相对路径
} else {
// No URL -> considering resource location as relative to the current file.
try {
int importCount;
// 创建相对地址的 Resource
Resource relativeResource = getReaderContext().getResource().createRelative(location);
// 存在
if (relativeResource.exists()) {
// 加载 relativeResource 中的 BeanDefinition
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
// 添加到 actualResources 中
actualResources.add(relativeResource);
// 不存在
} else {
// 获得根路径地址
String baseLocation = getReaderContext().getResource().getURL().toString();
// 添加配置文件地址的 Resource 到 actualResources 中,并加载相应的 BeanDefinition 们
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);
}
}
// 6. 解析成功后,进行监听器激活处理
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
- 方法的调用流程。
- 步骤 1,获取 source 属性的值,该值表示要导入的资源的路径。
- 步骤 2,解析路径中的系统属性,如 " ${user.dir} " 。
- 步骤 3,判断资源路径 location 是绝对路径还是相对路径。
- 步骤 4,如果是 绝对路径,则递归调用 Bean 的解析过程,进行另一次的解析。
- 步骤 5,如果是 相对路径,则先计算出绝对路径得到 Resource,然后进行解析。
- 步骤 6,通知监听器,完成解析。
importBeanDefinitionResource 方法步骤 3 中的判断路径
absoluteLocation = ResourcePatternUtils.isUrl(location) // 1.
|| ResourceUtils.toURI(location).isAbsolute(); // 2.
- 判断绝对路径的规则。
- 步骤 1,以 classpath*: 或者 classpath: 开头的为绝对路径,或者能够通过该 location 构建出 java.net.URL 为绝对路径。
-
步骤 2,根据 location 构造 java.net.URI 判断调用
isAbsolute()
方法,判断是否为绝对路径。
importBeanDefinitionResource 方法步骤 4 中的处理绝对路径
- 如果 location 为绝对路径,则调用
loadBeanDefinitions()
方法。- 该方法在 org.springframework.beans.factory.support.AbstractBeanDefinitionReader 中定义。
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 1. 获得 ResourceLoader 对象
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 {
// 2. 获得 Resource 数组,因为 Pattern 模式匹配下,可能有多个 Resource 。例如说,Ant 风格的 location
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 加载 BeanDefinition 们
int count = loadBeanDefinitions(resources);
// 添加到 actualResources 中
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 resource = resourceLoader.getResource(location);
// 3. 加载 BeanDefinition
int count = loadBeanDefinitions(resource);
// 4. 添加到 actualResources 中
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
- 方法的调用流程。
-
步骤 1,获取
ResourceLoader
对象。 -
步骤 2,根据不同的
ResourceLoader
执行不同的逻辑,主要是可能存在多个Resource
。 -
步骤 3,都会回归到
XmlBeanDefinitionReader#loadBeanDefinitions()
方法,这是一个 递归 的过程。 -
步骤 4,获得到的
Resource
的对象或数组,都会添加到actualResources
中。
-
步骤 1,获取
importBeanDefinitionResource 步骤 5 中的处理相对路径
- 如果 location 是相对路径,则会根据相应的
Resource
计算出相应的相对路径的Resource
对象。- 若
Resource
存在,则调用XmlBeanDefinitionReader#loadBeanDefinitions()
方法,进行BeanDefinition
加载。 - 否则,构造一个绝对 location(即
StringUtils.applyRelativePath()
),调用loadBeanDefinitions()
方法,与 绝对路径 过程一样。
- 若
2.1.1.2 小结
- 解析 import 标签的过程是,获取 source 属性值,得到正确的资源路径,然后调用
XmlBeanDefinitionReader#loadBeanDefinitions()
方法,进行递归的BeanDefinition
加载。
2.1.2 解析 bean 标签
2.1.2.1 processBeanDefinition
// DefaultBeanDefinitionDocumentReader.java
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 进行 bean 元素解析。
// 1. 如果解析成功,则返回 BeanDefinitionHolder 对象。而 BeanDefinitionHolder 为 name 和 alias 的 BeanDefinition 对象
// 如果解析失败,则返回 null 。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2. 进行自定义标签处理
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 3. 进行 BeanDefinition 的注册
// 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);
}
// 4. 发出响应事件,通知相关的监听器,已完成该 Bean 标签的解析。
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
- 方法的调用流程。
-
步骤 1,进行元素解析。
- 如果解析失败,则返回 null,错误由
ProblemReporter
处理。 - 如果解析成功,则返回
BeanDefinitionHolder
实例 bdHolder 。BeanDefinitionHolder
为持有 name 和 alias 的BeanDefinition
。
- 如果解析失败,则返回 null,错误由
- 步骤 2,若实例 bdHolder 不为空,则进行自定义标签处理。
-
步骤 3,解析完成后,对 bdHolder 进行
BeanDefinition
的注册。 - 步骤 4,发出响应事件,通知相关的监听器,完成 Bean 标签解析。
-
步骤 1,进行元素解析。
parseBeanDefinitionElement(Element, BeanDefinitionParserDelegate)
- 进行
<bean>
元素的解析。
// BeanDefinitionParserDelegate.java
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 1.1 解析 id 和 name 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 1.2 计算别名集合
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// 3.1 beanName ,优先,使用 id
String beanName = id;
// 3.2 beanName ,其次,使用 aliases 的第一个
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");
}
}
// 2. 检查 beanName 的唯一性
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 4. 解析属性,构造 AbstractBeanDefinition 对象
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 3.3 再次使用 beanName 生成规则
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
// 3.3 生成唯一的 beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
} else {
// 3.3 生成唯一的 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;
}
}
// 5. 创建 BeanDefinitionHolder 对象
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
private final Set<String> usedNames = new HashSet<>();
/**
* Validate that the specified bean name and aliases have not been used already
* within the current level of beans element nesting.
*/
protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
// 寻找是否 beanName 已经使用
String foundName = null;
if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
foundName = beanName;
}
if (foundName == null) {
foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
}
// 若已使用,使用 problemReporter 提示错误
if (foundName != null) {
error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
}
// 添加到 usedNames 集合
this.usedNames.add(beanName);
this.usedNames.addAll(aliases);
}
- 方法的调用流程。
- 步骤 1.1、1.2,解析 id、name 属性,确定 aliases 集合。
- 步骤 2,检测 beanName 是否唯一。
- 步骤 3.1,如果 id 不为空,则 beanName = id。
- 步骤 3.2,如果 id 为空,但是 aliases 不空,则 beanName 为 aliases 的第一个元素。
- 步骤 3.3,如果两者都为空,则根据默认规则来设置 beanName 。
-
步骤 4,对属性进行解析并封装成
AbstractBeanDefinition
实例beanDefinition
。 -
步骤 5,根据所获取的信息(beanName、aliases、beanDefinition)构造
BeanDefinitionHolder
实例对象并返回。
parseBeanDefinitionElement(Element, String, BeanDefinition)
- 对属性进行解析并封装成
AbstractBeanDefinition
实例。
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 解析 class 属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 解析 parent 属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建用于承载属性的 AbstractBeanDefinition 实例
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析默认 bean 的各种属性(singleton,scope,abstract,lazy-init......)
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取 description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 以下是解析 <bean>......</bean> 内部的子元素,
// 解析出来以后的信息都放到 bd 的属性中
// 解析元数据 <meta />
parseMetaElements(ele, bd);
// 解析 lookup-method 属性 <lookup-method />
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 replaced-method 属性 <replaced-method />
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造函数参数 <constructor-arg />
parseConstructorArgElements(ele, bd);
// 解析 property 子元素 <property />
parsePropertyElements(ele, bd);
// 解析 qualifier 子元素 <qualifier />
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;
}
- 解析得到一个基本可用的
BeanDefinition
。
2.1.2.1.1 BeanDefinition
-
org.springframework.beans.factory.config.BeanDefinition 是一个接口,描述了一个 Bean 实例的定义,包括属性值、构造方法值和继承自它的类的更多信息。
- 解析 bean 标签的过程其实就是构造一个
BeanDefinition
对象的过程。 -
<bean>
元素标签拥有的配置属性,BeanDefinition
均提供了相应的属性,与之一一对应。
- 解析 bean 标签的过程其实就是构造一个
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
void setParentName(@Nullable String parentName);
@Nullable
String getParentName();
void setBeanClassName(@Nullable String beanClassName);
@Nullable
String getBeanClassName();
void setScope(@Nullable String scope);
@Nullable
String getScope();
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
void setDependsOn(@Nullable String... dependsOn);
@Nullable
String[] getDependsOn();
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
void setPrimary(boolean primary);
boolean isPrimary();
void setFactoryBeanName(@Nullable String factoryBeanName);
@Nullable
String getFactoryBeanName();
void setFactoryMethodName(@Nullable String factoryMethodName);
@Nullable
String getFactoryMethodName();
ConstructorArgumentValues getConstructorArgumentValues();
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
void setInitMethodName(@Nullable String initMethodName);
@Nullable
String getInitMethodName();
void setDestroyMethodName(@Nullable String destroyMethodName);
@Nullable
String getDestroyMethodName();
void setRole(int role);
int getRole();
void setDescription(@Nullable String description);
@Nullable
String getDescription();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
BeanDefinition getOriginatingBeanDefinition();
// AttributeAccessor.java
public interface AttributeAccessor {
void setAttribute(String name, @Nullable Object value);
@Nullable
Object getAttribute(String name);
@Nullable
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
}
// BeanMetadataElement.java
public interface BeanMetadataElement {
@Nullable
Object getSource();
}
2.1.2.1.1.1 BeanDefinition 的关系
-
BeanDefinition
继承AttributeAccessor
和BeanMetadataElement
接口。- org.springframework.cor.AttributeAccessor 接口,定义了与其它对象的(元数据)进行连接和访问的约定,即对属性的修改,包括获取、设置、删除。
-
org.springframework.beans.BeanMetadataElement 接口,Bean 元对象持有的配置元素可以通过
getSource()
方法获取。 -
ChildBeanDefinition
、RootBeanDefinition
、GenericBeanDefinition
三者都继承AbstractBeanDefinition
抽象类,即AbstractBeanDefinition
对三个子类的共同的类信息进行抽象。
- 配置文件中定义了父
<bean>
和 子<bean>
,父<bean>
使用RootBeanDefinition
表示,子<bean>
使用ChildBeanDefinition
表示,没有父<bean>
使用RootBeanDefinition
表示。
![](https://img.haomeiwen.com/i5714666/3605885d16cb6e34.png)
2.1.2.1.2 createBeanDefinition
- 创建
AbstractBeanDefinition
对象,委托BeanDefinitionReaderUtils
创建。
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 对象
GenericBeanDefinition bd = new GenericBeanDefinition();
// 设置 parentName
bd.setParentName(parentName);
if (className != null) {
// 设置 beanClass
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
// 设置 beanClassName
} else {
bd.setBeanClassName(className);
}
}
return bd;
}
- 创建
GenericBeanDefinition
对象,并设置 parentName、className、beanClass 属性。
2.1.2.1.3 parseBeanDefinitionAttributes
- 将创建好的
GenericBeanDefinition
实例当做参数,对 bean 标签的所有属性进行解析。
// BeanDefinitionParserDelegate.java
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析 scope 属性
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
} 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.
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 (DEFAULT_VALUE.equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 解析 autowire 属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析 depends-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 ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(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));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
属性 | 说明 |
---|---|
scop | 是否单例模式。 singleton:为单例模式,默认值; prototype:为原型模式,多例模式。 |
abstract | 是否为抽象类,默认值为 false,设为 true 不能被实例化。 |
lazy-init | 是否延时加载,默认值为 false。设为 true 应用第一次用到 bean 时才实例化对象,否则在初始化 Spring 容器时加载单例 bean 对象。 |
autowire | 配置 Spring 对象属性的默认的装配方式。 no:为不启用自动装配; byType:为根据属性类型来找到和配置文件中配置的 class 类型一致的 bean 来自动装配,如果找到多个类型一致的 bean,则抛异常,如果一个都没有找到,则不执行装配操作,也不抛出异常; byName:为根据名称自动装配,一般是 bean 的 name,如果不声明,默认值取的是 id; default:(默认)为采用父级标签 beans 中的 default-autowire 属性; constructor:为根据构造器中参数类型来自动装配,如果找到多个类型一致的 bean,则抛异常,如果一个都没有找到,则不执行装配操作,但是抛出异常(这是和 byType 不一样的地方)。 autodetect:Spring3 之前存在的值,通过 Bean 类的反省机制(introspection)决定是使用 constructor 还是使用 byType。 |
depends-on | 作用是一个 bean 实例化的过程需要依赖于另一个 bean 的初始化,也就是说被依赖的 bean 将会在需要依赖的 bean 初始化之前加载。多个依赖 bean 之间用 " , " 号分割。 |
autowire-candidate | 默认为 true,如果为 false,那么该 bean 不能作为其他 bean 自动装配的候选者。 |
primary | 当一个 bean 出现多个候选者时,设置 为 true,则优先使用该 bean 自动装配。 |
init-method | 作用是在创建一个 bean 之后调用该方法,初始化方法必须是一个无参方法。 |
destroy-method | 作用是在销毁 bean 之前可以执行指定的方法。必须满足 scope = "singleton",并且 destroy 方法参数个数不能超过 1,并且参数只能为 boolean 类型。 |
factory-bean | 将指定创建 bean 的工厂类对象(加载类),class 属性将失效。 |
factory-method | 指定创建 bean 的工厂方法(加载静态方法)。 |
2.1.2.1.4 parseMetaElements
- 解析 meta 子元素,元数据。
- meta 所声明的 key 并不会在 Bean 中体现,只是一个额外的声明,当需要使用里面的信息时,通过调用
BeanDefinition
的getAttribute()
方法获取。
// BeanDefinitionParserDelegate.java
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
// 遍历子节点
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// <meta key="special-data" value="sprecial stragey" />
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) { // 标签名为 meta
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE); // key
String value = metaElement.getAttribute(VALUE_ATTRIBUTE); // value
// 创建 BeanMetadataAttribute 对象
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
// 添加到 BeanMetadataAttributeAccessor 中
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
- 获取相应的 key - value 构建
BeanMetadataAttribute
对象,加入到AbstractBeanDefinition
中。
addMetadataAttribute
- 调用
BeanMetadataAttributeAccessor#addMetadataAttribute()
方法,添加BeanMetadataAttribute
加入到AbstractBeanDefinition
中。
// BeanMetadataAttributeAccessor.java
public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}
- 委托
AttributeAccessorSupport
实现。-
org.springframework.core.AttributeAccessorSupport 是接口
AttributeAccessor
的实现。AttributeAccessor
接口定义了与其他对象的元数据进行连接和访问的约定,可以通过该接口对属性进行获取、设置、删除操作。
-
org.springframework.core.AttributeAccessorSupport 是接口
// AttributeAccessorSupport.java
/** Map with String keys and Object values. */
private final Map<String, Object> attributes = new LinkedHashMap<>();
@Override
public void setAttribute(String name, @Nullable Object value) {
Assert.notNull(name, "Name must not be null");
if (value != null) {
this.attributes.put(name, value);
} else {
removeAttribute(name);
}
}
getAttribute
- 设置元数据后,则可以通过调用
BeanDefinition
的getAttribute()
方法获取属性。
// AttributeAccessorSupport.java
/** Map with String keys and Object values. */
private final Map<String, Object> attributes = new LinkedHashMap<>();
@Override
@Nullable
public Object getAttribute(String name) {
Assert.notNull(name, "Name must not be null");
return this.attributes.get(name);
}
2.1.2.1.5 parseLookupOverrideSubElements
- 获取器(lookup-method)的注入,是把一个方法声明为返回某种类型的 bean,但实际要返回的 bean 可以在配置文件中配置。
- 该方法可以用于设计一些可插拔的功能上,解除程序依赖,用例如下。
public interface Color {
void display();
}
public class Blue implements Color {
@Override
public void display() {
System.out.println("blue");
}
}
public class Red implements Color {
@Override
public void display() {
System.out.println("red");
}
}
public abstract class Display {
public void display(){
getColor().display();
}
public abstract Color getColor();
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
Display display = (Display) context.getBean("display");
display.display();
}
/**
输出:red
如果将 bean="red" 替换为 bean="blue"
输出:blue
**/
//spring.xml
<bean id="display" class="org.springframework.core.test.Display">
<lookup-method name="getColor" bean="red"/>
</bean>
- 解析 methodName、beanRef 构造一个
LookupOverride
对象,记录到AbstractBeanDefinition
的 methodOverrides 属性中。
// BeanDefinitionParserDelegate.java
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
// 遍历子节点
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) { // 标签名为 lookup-method
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE); // name
String beanRef = ele.getAttribute(BEAN_ELEMENT); // bean
// 创建 LookupOverride 对象
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
// 添加到 MethodOverrides 中
overrides.addOverride(override);
}
}
}
2.1.2.1.6 parseReplacedMethodSubElements
-
replaced-method 可以在运行时调用新的方法替换现有的方法,还能动态的更新原有方法的逻辑。
- 需要实现 org.springframework.beans.factory.support.MethodReplacer 接口,用例如下。
public class Method {
public void display(){
System.out.println("origin");
}
}
public class MethodReplace implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("replace");
return null;
}
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
Method method = (Method) context.getBean("method");
method.display();
}
/**
输出:replace
**/
//spring.xml
<bean id="methodReplace" class="org.springframework.core.test.MethodReplace"/>
<bean id="method" class="org.springframework.core.test.Method">
<replaced-method name="display" replacer="methodReplace"/>
</bean>
- 提取 name 和 replacer 属性构建
ReplaceOverride
对象,然后记录到AbstractBeanDefinition
中的 methodOverrides 属性中。
/**
* Parse replaced-method sub-elements of the given bean element.
*/
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
// 遍历子节点
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) { // 标签名为 replace-method
Element replacedMethodEle = (Element) node;
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE); // name
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE); // replacer
// 创建 ReplaceOverride 对象
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT); // arg-type 子标签
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE); // arg-type 子标签的 match 属性
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
// 添加到 MethodOverrides 中
overrides.addOverride(replaceOverride);
}
}
}
2.1.2.1.7 parseConstructorArgElements
- 配置文件中使用 constructor-arg 元素对其配置,该元素可以实现自动寻找对应的构造函数,并在初始化时将值当做参数进行设置,用例如下。
public class StudentService {
private String name;
private Integer age;
private BookService bookService;
StudentService(String name, Integer age, BookService bookService){
this.name = name;
this.age = age;
this.bookService = bookService;
}
}
//spring.xml
<bean id="bookService" class="org.springframework.core.service.BookService"/>
<bean id="studentService" class="org.springframework.core.service.StudentService">
<constructor-arg index="0" value="wang"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="bookService" ref="bookService"/>
</bean>
- 遍历所有子元素,如果为 constructor-arg 标签,则调用
parseConstructorArgElement(Element, BeanDefinition)
方法解析。
// BeanDefinitionParserDelegate.java
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { // 标签名为 constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}
parseConstructorArgElement
// BeanDefinitionParserDelegate.java
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取 index、type、name 属性值
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
// 如果有 index
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
} else {
try {
// 1.
this.parseState.push(new ConstructorArgumentEntry(index));
// 2. 解析 ele 对应属性元素
Object value = parsePropertyValue(ele, bd, null);
// 3. 根据解析的属性元素构造一个 ValueHolder 对象
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
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 {
// 4. 加入到 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);
}
} else {
try {
this.parseState.push(new ConstructorArgumentEntry());
// 解析 ele 对应属性元素
Object value = parsePropertyValue(ele, bd, null);
// 根据解析的属性元素构造一个 ValueHolder 对象
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 加入到 indexedArgumentValues 中
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
} finally {
this.parseState.pop();
}
}
}
- 首先获取 index、type、name 三个属性值,然后根据是否存在 index 来区分(有无 index 逻辑类似),执行后续逻辑。
- 方法的调用流程。
-
步骤 1,构造
ConstructorArgumentEntry
对象并将其加入到ParseState
队列中。ConstructorArgumentEntry
表示构造函数的参数。 - 步骤 2,解析 constructor-arg 子元素,返回结果值。
-
步骤 3,根据解析的结果值,构造
ConstructorArgumentValues.ValueHolder
实例对象,并将 type、name 设置到ValueHolder
中。 -
步骤 4,将
ValueHolder
实例对象添加到 indexedArgumentValues 集合中。
-
步骤 1,构造
- 无 index 的处理逻辑类似,区别在于。
- 构造
ConstructorArgumentEntry
对象时调用无参构造函数。 - 最后将
ValueHolder
实例添加到 genericArgumentValues 集合中。
- 构造
parsePropertyValue
- 解析 constructor-arg 子元素,返回结果值。
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// 1. 查找子节点中,是否有 ref、value、list 等元素
// Should only have one child element: ref, value, list, etc.
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// meta 、description 不处理
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;
}
}
}
// 1.1. 是否有 ref 属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 1.2. 是否有 value 属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 1.3. 多个元素存在,报错,存在冲突。
if ((hasRefAttribute && hasValueAttribute) || // 1. ref 和 value 都存在
((hasRefAttribute || hasValueAttribute) && subElement != null)) { // 2. ref he value 存在一,并且 subElement 存在
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
// 2. 将 ref 属性值,构造为 RuntimeBeanReference 实例对象
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
// 3. 将 value 属性值,构造为 TypedStringValue 实例对象
} else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
// 4. 解析子元素
} else if (subElement != null) {
return parsePropertySubElement(subElement, bd);
} else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
- 方法的调用流程。
-
步骤 1,提取 constructor-arg 的子元素、ref 属性值和 value 属性值,对其进行判断。
- ref 和 value 属性同时存在 。
- 存在 ref 或者 value 且又有子元素。
-
步骤 2,若存在 ref 属性,则获取其值并将其封装进
RuntimeBeanReference
实例对象中。 -
步骤 3,若存在 value 属性,则获取其值并将其封装进
TypedStringValue
实例对象中。 -
步骤 4,如果子元素不为空,则调用
parsePropertySubElement()
方法,对子元素进一步解析。
-
步骤 1,提取 constructor-arg 的子元素、ref 属性值和 value 属性值,对其进行判断。
parsePropertySubElement
- 对于 constructor-arg 子元素的嵌套子元素进一步处理。
@Nullable
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 标签
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;
}
}
2.1.2.1.8 parsePropertyElements
- 配置文件中使用 property 元素配置对象属性,用例如下。
<bean id="studentService" class="org.springframework.core.service.StudentService">
<property name="name" value="wang"/>
<property name="age" value="18"/>
</bean>
- Spring 调用
parsePropertyElements()
方法,对 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);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { // property 标签
parsePropertyElement((Element) node, bd);
}
}
}
parsePropertyElement
- 同样遍历所有的 property 元素,调用
parsePropertyElement()
进行解析。
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 {
// 如果存在相同的 name ,报错
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 1. 解析属性值
Object val = parsePropertyValue(ele, bd, propertyName);
// 2. 创建 PropertyValue 对象
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
// 3. 添加到 PropertyValue 集合中
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
}
}
- 方法的调用流程。
- 步骤 1, 解析子元素属性值。
-
步骤 2,根据该值构造
PropertyValue
实例对象。 -
步骤 3,将
PropertyValue
添加到BeanDefinition
中的MutablePropertyValues
中。
2.1.2.1.9 parseQualifierElements
- 完成 qualifier 子元素的解析。
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 解析 type 属性
String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
if (!StringUtils.hasLength(typeName)) { // 必须有 type
error("Tag 'qualifier' must have a 'type' attribute", ele);
return;
}
this.parseState.push(new QualifierEntry(typeName));
try {
// 创建 AutowireCandidateQualifier 对象
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
// 解析 value 属性,并设置到 AutowireCandidateQualifier 中
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)) { // attribute 标签
Element attributeEle = (Element) node;
String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE); // attribute 标签的 key 属性
String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE); // attribute 标签的 value 属性
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
// 创建 BeanMetadataAttribute 对象
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle));
// 添加到 attributes 中
qualifier.addMetadataAttribute(attribute);
} else {
error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
return;
}
}
}
// 添加到 qualifiers 中
bd.addQualifier(qualifier);
} finally {
this.parseState.pop();
}
}
- 用例如下。
<qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="限定标识符"/>
- 其中 type 属性可选,指定类型,默认是
Qualifier
注解类,value 是 Bean 的候选者指定限定标识符,一个 Bean 定义中只允许指定类型不同的<qualifier>
,如果有多个相同 type 后面指定的将覆盖前面的。
2.1.2.2 decorateBeanDefinitionIfRequired
- 在 <2.1.2.1> 解析过程中,首先调用
parseBeanDefinitionElement()
方法,完成默认标签的解析,如果解析成功(返回的bdHolder != null
),则调用decorateBeanDefinitionIfRequired()
方法完成 自定义标签元素 的解析。
// BeanDefinitionParserDelegate.java
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = definitionHolder;
// 1. 遍历属性,查看是否有适用于装饰的【属性】
// 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);
}
// 2. 遍历子节点,查看是否有适用于修饰的【子节点】
// 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;
}
- 方法的调用流程。
- 步骤 1,遍历属性(attributes)。
- 步骤 2,遍历子节点(childNodes)。
- 最终都调用
decorateIfRequired()
方法,装饰对应的节点(Node)。
decorateIfRequired
// BeanDefinitionParserDelegate.java
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
// 1. 获取自定义标签的命名空间
String namespaceUri = getNamespaceURI(node);
// 2. 过滤掉默认命名标签
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
// 2. 获取相应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
// 3. 进行装饰处理
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/")) {
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;
}
- 方法的调用流程。
- 步骤 1,首先获取自定义标签的命名空间。
- 步骤 2,如果不是 默认的命名空间,则根据该命名空间获取相应的处理器。
- 步骤 3,如果处理器存在,则进行装饰处理。
2.2 自定义标签
- 例如:
<tx:annotation-driven>
。
2.2.1 使用自定义标签
- 扩展 Spring 自定义标签配置一般需要以下步骤。
- 步骤 1,创建一个需要扩展的组件(Bean)。
- 步骤 2,定义一个 XSD 文件,用于描述组件内容。
- 步骤 3,创建一个实现 org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser 接口的类,用于解析 XSD 文件中的定义和组件定义。
-
步骤 4,创建一个
Handler
,继承 org.springframework.beans.factory.xml.NamespaceHandlerSupport 抽象类 ,用于将组件注册到 Spring 容器。 - 步骤 5,编写 spring.handlers 和 Spring.schemas 文件。
创建组件
- 创建一个普通的 Java Bean。
public class User {
private String id;
private String userName;
private String email;
}
定义 XSD 文件
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/schema/user" targetNamespace="http://www.test.com/schema/user" elementFormDefault="qualified">
<xsd:element name="user">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="userName" type="xsd:string" />
<xsd:attribute name="email" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
- xmlns="http://www.test.com/schema/user"
- targetNamespace="http://www.test.com/schema/user"
定义 Parser 类
- 定义一个
Parser 类
,继承AbstractSingleBeanDefinitionParser
,并实现getBeanClass()
和doParse()
两个方法。- 主要用于解析 XSD 文件中的定义和组件定义。
public class UserDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String id = element.getAttribute("id");
String userName = element.getAttribute("userName");
String email = element.getAttribute("email");
if (StringUtils.hasText(id)) {
builder.addPropertyValue("id", id);
}
if (StringUtils.hasText(userName)) {
builder.addPropertyValue("userName", userName);
}
if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
}
}
定义 NamespaceHandler 类
- 定义
NamespaceHandler
类,继承NamespaceHandlerSupport
,将组件注册到 Spring 容器中。
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserDefinitionParser());
}
}
定义 spring.handlers 文件
http\://www.test.com/schema/user=org.springframework.core.customelement.UserNamespaceHandler
定义 Spring.schemas 文件
http\://www.test.com/schema/user.xsd=user.xsd
运行
- 在 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"
xmlns:myTag="http://www.test.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.test.com/schema/user http://www.test.com/schema/user.xsd">
<myTag:user id="user" email="test@163.com" userName="wang" />
</beans>
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
User user = (User) context.getBean("user");
System.out.println(user.getId() + "----" + user.getUserName() + "----" + user.getEmail());
}
/**print
user----wang----test@163.com
**/
2.2.2 解析自定义标签
-
解析自定义标签 由
BeanDefinitionParserDelegate
的parseCustomElement()
方法实现。
parseCustomElement
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 1. 获取 namespaceUri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 2. 根据 namespaceUri 获取相应的 Handler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 3. 调用自定义的 Handler 处理
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
@Nullable
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
- 方法的调用流程。
-
步骤 1,调用
getNamespaceURI()
方法,获取namespaceUri
。 -
步骤 2,调用
XmlReaderContext#getNamespaceHandlerResolver()
,获得命名空间的解析器。- 根据
namespaceUri
获取相应的Handler
对象。这个映射关系在 spring.handlers 中定义,只需要查找到该类,进行初始化返回。
- 根据
-
步骤 3,调用
NamespaceHandler#parse()
自定义的Handler
处理。
-
步骤 1,调用
2.2.2.1 getNamespaceHandlerResolver
- 调用
XmlReaderContext
的getNamespaceHandlerResolver()
方法,返回的命名空间的解析器。
/**
* NamespaceHandler 解析器
*/
private final NamespaceHandlerResolver namespaceHandlerResolver;
public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
return this.namespaceHandlerResolver;
}
2.2.2.1.1 NamespaceHandlerResolver 的初始化
-
【Spring 笔记】BeanDefinition 装载与注册相关整理 文中 <2.3 BeanDefinition 的注册> 由
XmlBeanDefinitionReader
的createReaderContext()
方法初始化了 namespaceHandlerResolver 对象。
// XmlBeanDefinitionReader.java
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
getNamespaceHandlerResolver
// XmlBeanDefinitionReader.java
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
-
NamespaceHandlerResolver
对象的最终类型是 org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver。
2.2.2.2 resolve
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
// 1. 获取所有已经配置的 Handler 映射
Map<String, Object> handlerMappings = getHandlerMappings();
// 2. 根据 namespaceUri 获取 handler 的信息
Object handlerOrClassName = handlerMappings.get(namespaceUri);
// 3.1. 不存在
if (handlerOrClassName == null) {
return null;
// 3.2. 已经初始化
} else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
// 3.3. 需要进行初始化
} else {
String className = (String) handlerOrClassName;
try {
// 获得类,并创建 NamespaceHandler 对象
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 初始化 NamespaceHandler 对象
namespaceHandler.init();
// 添加到缓存
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
} catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
} catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
- 方法的调用流程。
-
步骤 1,调用
getHandlerMappings()
方法,获取所有配置文件中的映射关系 handlerMappings 。 - 步骤 2,根据 namespaceUri 获取 handler 的信息。
- 步骤 3.1,如果 handlerOrClassName 不存在,则返回 null 空。
-
步骤 3.2,如果 handlerOrClassName 已经初始化成
NamespaceHandler
对象,直接返回。 -
步骤 3.3,如果 handlerOrClassName 还是类路径,则创建
NamespaceHandler
对象,并调用NamespaceHandler#init()
方法,初始化NamespaceHandler
对象。- 创建的
NamespaceHandler
对象,会添加到 handlerMappings 中,进行缓存。
- 创建的
-
步骤 1,调用
getHandlerMappings
- 通过延迟加载(lazy-init)方式,加载 handlerMappingsLocation 中配置的
NamespaceHandler
的映射,到 handlerMappings 中。 - andlerMappings 的值属性有 2 种情况。
-
未初始化时,对应的
NamespaceHandler
的类路径。 -
已初始化时,对应的
NamespaceHandler
对象。
-
未初始化时,对应的
/** ClassLoader to use for NamespaceHandler classes. */
@Nullable
private final ClassLoader classLoader;
/**
* NamespaceHandler 映射配置文件地址
*
* Resource location to search for.
*/
private final String handlerMappingsLocation;
/**
* Stores the mappings from namespace URI to NamespaceHandler class name / instance.
*
* NamespaceHandler 映射。
*
* key:命名空间
* value:分成两种情况
*/
@Nullable
private volatile Map<String, Object> handlerMappings;
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
// 双重检查锁,延迟加载
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
// 读取 handlerMappingsLocation
Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
// 初始化到 handlerMappings 中
handlerMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
} catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
init
- 假设还是使用之前 <2.2.1> 中的定义
NamespaceHandler
类,将自定义标签解析器进行注册。
// UserNamespaceHandler.java
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserDefinitionParser());
}
}
- 调用父类
NamespaceHandlerSupport
的registerBeanDefinitionParser()
方法,注册指定元素的BeanDefinitionParser
解析器。
registerBeanDefinitionParser
- 注册指定元素的
BeanDefinitionParser
解析器。- 将映射关系放在一个 Map 结构的 parsers 对象中。
// NamespaceHandlerSupport.java
/**
* Stores the {@link BeanDefinitionParser} implementations keyed by the
* local name of the {@link Element Elements} they handle.
*
* key:元素名
* value:对应 BeanDefinitionParser 的解析器
*/
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
2.2.2.3 parse
- 开始自定义标签的解析。
// NamespaceHandlerSupport.java
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 1. 获得元素对应的 BeanDefinitionParser 对象
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// 2. 执行解析
return (parser != null ? parser.parse(element, parserContext) : null);
}
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 获得元素名
String localName = parserContext.getDelegate().getLocalName(element);
// 获得 BeanDefinitionParser 对象
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
- 方法的调用流程。
-
步骤 1,获取对应的
BeanDefinitionParser
实例。实际上是获取在NamespaceHandlerSupport
的registerBeanDefinitionParser()
方法里面注册的实例对象。- 获取 localName(上例中为 user)。
- 从 Map 实例 parsers 中获取
BeanDefinitionParser
对象(上例中为UserBeanDefinitionParser
对象)。
-
步骤 2,返回
BeanDefinitionParser
对象后,调用其parse()
方法。该方法在AbstractBeanDefinitionParser
中实现。
-
步骤 1,获取对应的
// AbstractBeanDefinitionParser.java
@Override
@Nullable
public final BeanDefinition parse(Element element, ParserContext parserContext) {
// 内部解析,返回 AbstractBeanDefinition 对象
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
// 解析 id 属性
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
// 解析 aliases 属性
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// 创建 BeanDefinitionHolder 对象
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
// 注册 BeanDefinition
registerBeanDefinition(holder, parserContext.getRegistry());
// 触发事件
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
} catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
parseInternal
- 解析 XML 元素为
AbstractBeanDefinition
对象。
// AbstractSingleBeanDefinitionParser.java
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
// 创建 BeanDefinitionBuilder 对象
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
// 获取父类元素
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 获取自定义标签中的 class,这个时候会去调用自定义解析中的 getBeanClass()
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
} else {
// beanClass 为 null,意味着子类并没有重写 getBeanClass() 方法,则尝试去判断是否重写了 getBeanClassName()
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
// 设置 source 属性
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
// 设置 scope 属性
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
// Inner bean definition must receive same scope as containing bean.
builder.setScope(containingBd.getScope());
}
// 设置 lazy-init 属性
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
// 调用子类的 doParse() 进行解析
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
2.2.3 小结
- 自定义标签的解析的整个过程。
- 加载 spring.handlers 文件,将其中内容进行一个解析,形成 <namespaceUri, 类路径> 这样的一个映射。
- 根据获取的 namespaceUri 可以得到相应的类路径,对其进行初始化,得到相应的
NamespaceHandler
对象。 - 调用该
NamespaceHandler
的parse()
方法,根据标签的 localName 得到相应的BeanDefinitionParser
实例对象。 - 调用该
BeanDefinitionParser
的 parse()` 方法。- 方法定义在
AbstractBeanDefinitionParser
抽象类中,核心逻辑封装在其parseInternal()
方法中,该方法返回一个AbstractBeanDefinition
实例对象,其主要是在AbstractSingleBeanDefinitionParser
中实现。 - 对于自定义的
Parser
类,需要实现getBeanClass()
或者getBeanClassName()
任一方法,和doParse()
方法。
- 方法定义在
网友评论