内容简介:Spring解密 - 默认标签的解析
Spring
是一个开源的设计层面框架,解决了业务逻辑层和其他各层的松耦合问题,将面向接口的编程思想贯穿整个系统应用,同时它也是 Java工作中
必备技能之一…
前言
紧跟上篇 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` 实例中 ``` java // 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);
),进行另一次解析,如果是相对路径则计算出绝对路径并进行解析,最后通知监听器,解析完成
总结
熬过几个无人知晓的秋冬春夏,撑过去一切都会顺着你想要的方向走…
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring解密 - 自定义标签与解析
- Java之DES加解密解析
- Spring解密 - XML解析 与 Bean注册
- APICloud解密本地资源到逆向APP算法到通用资源解密
- NodeJS加密解密,node-rsa加密解密用法
- CMSEasy企业建站源代码解密工具,适用于纯本地解密机制!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Art of Computer Programming, Volumes 1-3 Boxed Set
Donald E. Knuth / Addison-Wesley Professional / 1998-10-15 / USD 199.99
This multivolume work is widely recognized as the definitive description of classical computer science. The first three volumes have for decades been an invaluable resource in programming theory and p......一起来看看 《The Art of Computer Programming, Volumes 1-3 Boxed Set》 这本书的介绍吧!