上一节分析了Spring将xml文件转换为Document对象的过程,接下来Spring会对已转换的Document对象进行解析。打开XmlBeanDefinitionReader类的registerBeanDefinitions方法。打开DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法。
1. 引言
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 1、创建documentReader对象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 2、读取已经注册的bean个数 int countBefore = getRegistry().getBeanDefinitionCount(); // 3、解析Document并注册BeanDefinition documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 4、返回本次注册的bean个数 return getRegistry().getBeanDefinitionCount() - countBefore; }
该方法总共分为了四步,第三步是执行对Document对象解析和注册BeanDefinition。先来看对Document对象的解析过程。
2.doRegisterBeanDefinitions方法分析
protected void doRegisterBeanDefinitions(Element root) { // 1、创建BeanDefinitionParserDelegate对象,用来解析Element元素 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); // 2、解析并验证profile节点,如果配置了profile属性,则验证当前环境是否激活了对应的profile节点, // 用于多开发环境配置,该方式在开发中已不多见。 // 例如:System.setProperty("spring.profiles.active", "dev"); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } // 3、解析前置处理,空的模板方法 preProcessXml(root); // 4、解析并注册BeanDefinition parseBeanDefinitions(root, this.delegate); // 5、解析后置处理,空的模板方法 postProcessXml(root); this.delegate = parent; }
doRegisterBeanDefinitions方法并没有自己去执行对xml节点的解析而是委托给了BeanDefinitionParserDelegate,通过代理去解析xml节点。Spring的xml文件又可能包含默认命名空间和自定义命名空间,我们来看Spring是如何进行处理的。
3.parseBeanDefinitions方法简析
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 1、解析默认命名空间 if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 默认命名空间 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } // 自定义命名空间 else { delegate.parseCustomElement(ele); } } } } // 2、解析自定义命名空间 else { delegate.parseCustomElement(root); } }
这与上一节的xml解析分析大致相同,只是增加了对命名空间的判断,针对不同的命名空间使用不同的方法进行解析。先来看对默认命名空间中的标签的解析。
4.parseDefaultElement解析默认标签
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { // 1、解析import标签,并注册beanDefinition if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } // 2、解析alias标签,并注册别名 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } // 3、解析bean标签,并注册beanDefinition else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } // 4、解析beans标签 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { doRegisterBeanDefinitions(ele); } }
代码到这里,相信大家已经有所了然了,日常开发中的常见标签已经展现出来了(虽然现在提倡无配置文件,但是还是要对其原理有所了解)。parseDefaultElement方法对import、alias、bean、beans四种标签分别做了单独处理,bean标签的解析最复杂也是一个比较基础的操作,先来看对bean标签的解析,如果大家能够了解bean的解析,那么其他的解析过程相信大家都能看懂,因为相对而言要比解析bean标签简单的多。
5.解析Bean标签
/** * 解析bean标签将其转换为definition并注册到BeanDefinitionRegistry * Process the given bean element, parsing the bean definition and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 1、将解析的节点信息封装至BeanDefinitionHolder对象 // BeanDefinitionHolder-->封装了BeanDefinition,beanName以及aliases BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 2、装饰BeanDefinition bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. // 3、执行注册 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. // 4、发送注册事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
可以看到Spring将解析之后的内容封装至了BeanDefinitionHolder类,该类封装了BeanDefinition,beanName以及aliases,BeanDefinition封装了bean标签的各种信息。
该方法还执行了对BeanDefinition的注册,留在下节分析。来看bean标签解析的具体过程。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // 1、解析bean的id和bean的别名 // 获取id标签属性 String id = ele.getAttribute(ID_ATTRIBUTE); // 获取name(别名)标签属性,缓存至List集合 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } // 如果bean的id为空,但是别名不为空的话,那么默认采用第一个别名作为beanName // 例如:<bean class="com.lyc.cn.v2.day01.Dog" name="myDog1,myDog2"/>,使用myDog1作为beanName String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); } // 2、containingBean不为空,则检查beanName和别名是否被使用 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 3、解析bean标签,将其转换为BeanDefinition对象 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); // 4、如果beanDefinition且未配置bean的id属性,name属性,则为当前bean生成id和别名 // 例如:<bean class="com.lyc.cn.v2.day01.Dog"/>,<bean factory-bean="dog4"/>,<bean parent="outer"/>等 if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { // containingBean不为null,则当前bean是内部bean,使用BeanDefinitionReaderUtils生成beanName if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true); } else { // 否则,使用XmlReaderContext对象生成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. // 如果生成器返回的是类名加上后缀,则为普通bean类名注册一个别名。这在Spring 1.2/2.0的向后兼容性中是可以实现的。 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } // 5、创建并返回BeanDefinitionHolder对象 String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
首先对id,name属性进行解析,该过程比较简单,接下来就是对bean中其他属性的解析。来看parseBeanDefinitionElement方法。
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); // 1、解析class属性 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } // 2、解析parent属性 String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { // 3、创建AbstractBeanDefinition对象 AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 4、解析bean标签属性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // 5、解析meta标签 parseMetaElements(ele, bd); // 6、解析lookup-method属性 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // 7、解析replace-method属性 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 8、解析构造函数参数 parseConstructorArgElements(ele, bd); // 9、解析property属性 parsePropertyElements(ele, bd); // 10、解析qualifier属性 parseQualifierElements(ele, bd); // 11、设置bean定义来源和元数据的来源 bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null; }
该方法涉及的细节较多,篇幅限制,只对其中比较重要比较常见的方法进行分析。分析第四步解析bean标签属性。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { // 1、设置bean作用域 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. // 未明确指定bean的作用域,且当前被解析bean是内部bean的话, // 则默认使用outer bean的的作用域作为当前bean的作用域 // 例如:下面的配置,解析到inner属性时,inner未指定作用域,则使用outer的作用域,也就是prototype /** <bean id="outer" class="com.lyc.cn.v2.day01.inner.Outer" scope="prototype"> <property name="inner"> <bean id="inner" class="com.lyc.cn.v2.day01.inner.Inner"/> </property> </bean> **/ bd.setScope(containingBean.getScope()); } // 2、设置abstract属性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } // 3、设置lazy-init(延迟加载)属性; // 如果该属性为true的话,ApplicationContext容器在初始化时不会加载该bean; // 而是在第一次向容器索取该bean时才会被初始化 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); // 4、设置autowire属性,此属性默认不开启 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); // 5、设置depends-on属性,如果BeanA依赖于BeanB,可通过depends-on属性使BeanB在BeanA之前完初始化 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } // 6、设置autowire-candidate属性,当一个接口有多个实现类时, // 配置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)); } // 7、设置primary属性,当byType注入有多个类型时, // 可以指定primary="true",提高注入的优先级,避免抛出异常 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } // 8、设置init-method,bean初始化完成后回调该方法 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } else if (this.defaults.getInitMethod() != null) { // 尝试从DocumentDefaultsDefinition对象中获取init-method属性 // DocumentDefaultsDefinition对象保存了Spring bean的一些简单设置, // 我们可以通过该类设定通用的bean属性模板 bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } // 9、设置destroy-method属性,bean销毁后回调该方法 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } else if (this.defaults.getDestroyMethod() != null) { // 尝试从DocumentDefaultsDefinition对象中获取destroy-method属性 bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } // 10、设置factory-method属性,该属性可指定静态工厂或实例工厂方法实例化bean if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } // 11、设置factory-bean属性,一般和factory-method属性一起使用, // 指定工厂bean和工厂bean的工厂方法 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
其他的方法大家可以通过在配置文件中配置属性,debug跟踪调试,不在一一赘述。到这里Spring已经将xml中的配置信息封装至BeanDefinitionHolder对象,接下来就可以执行对bean的注册了。
另外对bean name的生成策略简单分析一下:
上面parseBeanDefinitionElement方法已经有所简介,来看一下具体过程:
public static String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException { // 1、获取bean的className String generatedBeanName = definition.getBeanClassName(); // 2、如果generatedBeanName为null, // 则判断其是否有parent属性或者factory-bean并生成generatedBeanName if (generatedBeanName == null) { // parent属性不为空 if (definition.getParentName() != null) { generatedBeanName = definition.getParentName() + "$child"; } // factory-bean属性不为空 else if (definition.getFactoryBeanName() != null) { generatedBeanName = definition.getFactoryBeanName() + "$created"; } } // 3、如果上述方法均无法生成generatedBeanName,则抛出异常 if (!StringUtils.hasText(generatedBeanName)) { throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " + "'class' nor 'parent' nor 'factory-bean' - can't " + "generate bean name"); } // 4、判断是否内部bean,并根据生成的generatedBeanName,拼接最终的beanName返回 String id = generatedBeanName; if (isInnerBean) { // Inner bean: generate identity hashcode suffix. // GENERATED_BEAN_NAME_SEPARATOR--># id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); } else { // Top-level bean: use plain class name. // Increase counter until the id is unique. int counter = -1; while (counter == -1 || registry.containsBeanDefinition(id)) { counter++; // GENERATED_BEAN_NAME_SEPARATOR--># id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter; } } return id; }
这一部分的代码比较多,也比较细,一时间找不到应该如何写才能让读者更清晰的了解解析过程,感兴趣的多多调试跟踪代码吧。