Spring
是一个开源的设计层面框架,解决了业务逻辑层和其他各层的松耦合问题,将面向接口的编程思想贯穿整个系统应用,同时它也是Java工作中
必备技能之一...
<!-- more -->
前言
紧跟上篇 Spring解密 - XML解析 与 Bean注册 ,我们接着往下分析源码
解密
在 Spring
的 XML 配置里面有两大类声明,一个是默认的如 <bean id="person" class="com.battcn.bean.Person"/>
,另一类就是自定义的如<tx:annotation-driven />
,两种标签的解析方式差异是非常大的。parseBeanDefinitions
方法就是用来区分不同标签所使用的解析方式。通过 node.getNamespaceURI()
方法获取命名空间,判断是默认命名空间还是自定义命名空间,并与 Spring
中固定的命名空间 http://www.springframework.org/schema/beans
进行比对,如果一致则采用parseDefaultElement(ele, delegate);
否则就是delegate.parseCustomElement(ele);
默认标签的解析
parseDefaultElement
对 4 种不同的标签 import、alias、bean、beans
做了不同的处理,其中 bean
标签的解析最为复杂也最为重要,所以我们将从 bean
开始深入分析,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解。上一篇中只是简单描述了一下,本篇我们围绕解析模块详细的探讨一下
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// import 标签解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// alias 标签解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// bean 标签解析
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// import 标签解析
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// beans标签解析 递归方式
doRegisterBeanDefinitions(ele);
}
}
}
首先我们来分析下当类中的 processBeanDefinition(ele, delegate)
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 解析完成后需要对解析后的bdHolder进行注册,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 最后发出响应事件,通知相关监听器这个bean已经被加载
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这段代码中:
- 首先委托
BeanDefinitionParseDelegate
对节点做了解析,并返回了一个BeanDefinitionHolder
的实例,在这个实例中已经包含了配置文件中配置的各种属性了 - 如果在当前子节点中存在自定义属性,则还需要对自定义标签进行解析
- 解析完成后,需要对解析后的
bdHolder
进行注册,同样注册操作委托给了BeanDefinitionReaderUtils
- 最后发出响应事件,通知相关监听器这个
bean
已经被加载
下面我们详细分析下,Spring
是如何解析各个标签和节点的
bean 标签解析
public class BeanDefinitionParserDelegate {
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取Bean标签的ID属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取Bean标签的Name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
// 将name属性的值通过,; 进行分割 转为字符串数字(即在配置文件中如配置多个name 在此做处理)
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
// 如果ID为空 使用配置的第一个name属性作为ID
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 校验beanName和aliases的唯一性
// 内部核心为使用usedNames集合保存所有已经使用了的beanName和alisa
checkNameUniqueness(beanName, aliases, ele);
}
// 进一步解析其他所有属性到GenericBeanDefinition对象中
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 如果bean没有指定beanName 那么使用默认规则为此Bean生成beanName
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
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.isDebugEnabled()) {
logger.debug("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、alias
等相关属性,生成了 beanName
,并且在重载函数 parseBeanDefinitionElement(ele, beanName, containingBean)
方法中完成核心的标签解析。
接下来重点分析parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
看下它是如何完成标签解析操作的
bean 节点与属性解析
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 获取Bean标签的class属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 获取Bean标签的parent属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建用于承载属性的AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 获取bean标签各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 解析description标签
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析meta标签
parseMetaElements(ele, bd);
// 解析lookup-method标签
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replaced-method标签
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析constructor-arg标签
parseConstructorArgElements(ele, bd);
// 解析property标签
parsePropertyElements(ele, bd);
// 解析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;
}
进一步解析其他属性和元素(元素和属性很多,所以这是一个庞大的工作量)并统一封装至 GenericBeanDefinition
中, 解析完成这些属性和元素之后,如果检测到 bean
没有指定的 beanName
,那么便使用默认的规则为 bean
生成一个 beanName
。
// BeanDefinitionParserDelegate.java
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
public class BeanDefinitionReaderUtils {
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
// parentName可能为空
bd.setParentName(parentName);
// 如果classLoader不为空
// 则使用传入的classLoader同一虚拟机加载类对象 否则只记录classLoader
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
}
BeanDefinition
是 <bean>
在容器中的内部表示形式,BeanDefinition
和 <bean>
是一一对应的。同时 BeanDefinition
会被注册到 BeanDefinitionRegistry
中,BeanDefinitionRegistry
就像 Spring
配置信息的内存数据库。
至此 createBeanDefinition(className, parent);
已经说完了,而且我们也获得了 用于承载属性的AbstractBeanDefinition
,接下来看看 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
是如何解析 bean
中的各种标签属性的
public class BeanDefinitionParserDelegate {
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// ...省略详细代码,该部分代码主要就是通过 if else 判断是否含有指定的属性,如果有就 bd.set(attribute);
return bd;
}
}
bean
标签的完整解析到这就已经全部结束了,其中 bean
标签下的元素解析都大同小异,有兴趣的可以自己跟踪一下源代码看看 qualifier、lookup-method
等解析方式(相对 bean
而言不复杂)。自定义标签内容较多会在下一章详细介绍。
最后将获取到的信息封装到 BeanDefinitionHolder
实例中
// BeanDefinitionParserDelegate.java
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// ...
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
注册解析的 BeanDefinition
在解析完配置文件后我们已经获取了 bean
的所有属性,接下来就是对 bean
的注册了
public class BeanDefinitionReaderUtils {
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用 beanName 做唯一标识符
String beanName = definitionHolder.getBeanName();
// 注册bean的核心代码
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 为bean注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
}
以上代码主要完成两个功能,一是使用 beanName
注册 beanDefinition
,二是完成了对别名的注册
BeanName 注册 BeanDefinition
public class DefaultListableBeanFactory {
@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);
}
}
BeanDefinition oldBeanDefinition;
// 获取缓存中的 beanDefinition
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
// 如果缓存中存在 判断是否允许覆盖
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 如果允许覆盖,保存beanDefinition到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 判断是否已经开始创建bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 保存beanDefinition到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 更新已经注册的beanName
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
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 {
// 还没开始创建bean
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
// 重置beanName对应的缓存
resetBeanDefinition(beanName);
}
}
}
- 对
AbstractBeanDefinition
的校验,主要是针对AbstractBeanDefinition
的methodOverrides
属性的 - 对
beanName
已经注册的情况的处理,如果设置了不允许bean
的覆盖,则需要抛出异常,否则直接覆盖 - 使用
beanName
作为 key,beanDefinition
为 Value 加入beanDefinitionMap
存储 - 如果缓存中已经存在,并且该
bean
为单例模式则清楚beanName
对应的缓存
注册别名
注册好了 beanDefinition
,接下来就是注册 alias
。注册的 alias
和 beanName
的对应关系存放在了 aliasMap
中,沿着类的继承链会发现 registerAlias
的方法是在 SimpleAliasRegistry
中实现的
public class SimpleAliasRegistry {
/** Map from alias to canonical name */
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
if (alias.equals(name)) {
// 如果beanName与alias相同的话不记录alias 并删除对应的alias
this.aliasMap.remove(alias);
} else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// 如果别名已经注册过并且指向的name和当前name相同 不做任何处理
return;
}
// 如果alias不允许被覆盖则抛出异常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 校验循环指向依赖 如A->B B->C C->A则出错
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
}
通过 checkForAliasCircle()
方法来检查 alias
循环依赖,当 A -> B 存在时,若再次出现 A -> C -> B 则会抛出异常:
protected void checkForAliasCircle(String name, String alias) {
if (hasAlias(alias, name)) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
public boolean hasAlias(String name, String alias) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String registeredAlias = entry.getKey();
return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
}
}
return false;
}
至此,注册别名也完成了,主要完成了以下几个工作
- 如果
beanName
与alias
相同的话不记录alias
并删除对应的alias
- 如果别名已经注册过并且指向的name和当前name相同 不做任何处理
- 如果别名已经注册过并且指向的name和当前name不相同 判断是否允许被覆盖
- 校验循环指向依赖 如A->B B->C C->A则出错
发送通知
通知监听器解析及注册完成
//DefaultBeanDefinitionDocumentReader.java
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
通过 fireComponentRegistered
方法进行通知监听器解析及注册完成工作,这里的实现只为扩展,当程序开发人员需要对注册 BeanDefinition
事件进行监听时,可以通过注册监听器的方式并将处理逻辑写入监听器中,目前 Spring
中并没有对此事件做任何处理
其中 ReaderContext
是在类 XmlBeanDefinitionReader
中调用 createReaderContext
生成的,然后调用 fireComponentRegistered()
alias 标签解析
Spring
提供了 <alias name="person" alias="p"/>
方式来进行别名的配置,该标签解析是在 processAliasRegistration(Element ele)
方法中完成的
public class DefaultBeanDefinitionDocumentReader {
protected void processAliasRegistration(Element ele) {
// 获取 alisa 标签 name 属性
String name = ele.getAttribute(NAME_ATTRIBUTE);
// 获取 alisa 标签 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 {
// 进行别名注册
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));
}
}
}
首先对 alias
标签属性进行提取校验,校验通过后进行别名注册,别名注册和 bean
标签解析中的别名注册一直,此处不再赘述
import 标签解析
public class DefaultBeanDefinitionDocumentReader {
protected void importBeanDefinitionResource(Element ele) {
// 获取import标签的resource属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果不存在则不做任何处理
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// 解析占位符属性 格式如"${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 判断资源是绝对路径还是相对路径
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*:"
}
// 如果是绝对路径则直接根据地址加载对应的配置文件
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("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 {
try {
int importCount;
// 根据相对路径加载资源
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.isDebugEnabled()) {
logger.debug("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[actualResources.size()]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
}
完成了对 import
标签的处理,首先就是获取 <import resource="beans.xml"/>
resource 属性所表示的路径,接着解析路径中的属性占位符 如 ${user.dir}
,然后判定 location
是绝对路径还是相对路径,如果是绝对路径则递归调用 bean
的解析过程(loadBeanDefinitions(location, actualResources);
),进行另一次解析,如果是相对路径则计算出绝对路径并进行解析,最后通知监听器,解析完成
总结
熬过几个无人知晓的秋冬春夏,撑过去一切都会顺着你想要的方向走...
说点什么
全文代码:https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter1
- 个人QQ:1837307557
- battcn开源群(适合新手):391619659
微信公众号:battcn
(欢迎调戏)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。