Spring源码解析(三)BeanDefinition的载入、解析和注册
通过上一篇源码的分析已经完成了BeanDefinition资源文件的定位,本篇继续分析BeanDefinition资源文件的载入和解析。
AbstractBeanDefinitionReader的loadBeanDefinitions(String location,Set<Resouce> actualResouces)方法完成定位,紧接着调用loadBeanDefinitions()方法:
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { counter += loadBeanDefinitions(resource); } return counter; }
循环逐个解析资源文件,具体操作是由AbstractBeanDefinitionReader的子类完成的。
我们通过xml文件定义的bean,所以调用的是XmlBeanDefinitionReader的loadBeanDefinitions方法:
1 /** 2 * Load bean definitions from the specified XML file. 3 * @param encodedResource the resource descriptor for the XML file, 4 * allowing to specify an encoding to use for parsing the file 5 * @return the number of bean definitions found 6 * @throws BeanDefinitionStoreException in case of loading or parsing errors 7 */ 8 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { 9 Assert.notNull(encodedResource, "EncodedResource must not be null"); 10 if (logger.isInfoEnabled()) { 11 logger.info("Loading XML bean definitions from " + encodedResource.getResource()); 12 } 13 14 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); 15 if (currentResources == null) { 16 currentResources = new HashSet<EncodedResource>(4); 17 this.resourcesCurrentlyBeingLoaded.set(currentResources); 18 } 19 if (!currentResources.add(encodedResource)) { 20 throw new BeanDefinitionStoreException( 21 "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); 22 } 23 try { 24 //根据Resource获取输入流 25 InputStream inputStream = encodedResource.getResource().getInputStream(); 26 try { 27 InputSource inputSource = new InputSource(inputStream); 28 if (encodedResource.getEncoding() != null) { 29 inputSource.setEncoding(encodedResource.getEncoding()); 30 } 31 //Spring凡是do开头的方法都是具体干活的 32 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 33 } 34 finally { 35 inputStream.close(); 36 } 37 } 38 catch (IOException ex) { 39 throw new BeanDefinitionStoreException( 40 "IOException parsing XML document from " + encodedResource.getResource(), ex); 41 } 42 finally { 43 currentResources.remove(encodedResource); 44 if (currentResources.isEmpty()) { 45 this.resourcesCurrentlyBeingLoaded.remove(); 46 } 47 } 48 }
该方法通过入参encodedResource.getResource()获取到inputStream,最后调用了doLoadBeanDefinitions()方法。
1 /** 2 * Actually load bean definitions from the specified XML file. 3 * @param inputSource the SAX InputSource to read from 4 * @param resource the resource descriptor for the XML file 5 * @return the number of bean definitions found 6 * @throws BeanDefinitionStoreException in case of loading or parsing errors 7 */ 8 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 9 throws BeanDefinitionStoreException { 10 try { 11 int validationMode = getValidationModeForResource(resource); 12 //取得xml文件的Document对象 13 Document doc = this.documentLoader.loadDocument( 14 inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 15 return registerBeanDefinitions(doc, resource); 16 } 17 ......//省略40 }
获取XML文件校验方式
首先,调用getValidationModeForResource方法
1 /** 2 * Gets the validation mode for the specified {@link Resource}. If no explicit 3 * validation mode has been configured then the validation mode is 4 * {@link #detectValidationMode detected}. 5 * <p>Override this method if you would like full control over the validation 6 * mode, even when something other than {@link #VALIDATION_AUTO} was set. 7 */ 8 protected int getValidationModeForResource(Resource resource) { 9 int validationModeToUse = getValidationMode(); 10 if (validationModeToUse != VALIDATION_AUTO) { 11 return validationModeToUse; 12 } 13 int detectedMode = detectValidationMode(resource); 14 if (detectedMode != VALIDATION_AUTO) { 15 return detectedMode; 16 } 17 // Hmm, we didn't get a clear indication... Let's assume XSD, 18 // since apparently no DTD declaration has been found up until 19 // detection stopped (before finding the document's root tag). 20 return VALIDATION_XSD; 21 }
然周调用了detectValidationMode方法:
1 protected int detectValidationMode(Resource resource) { 2 if (resource.isOpen()) { 3 throw new BeanDefinitionStoreException( 4 "Passed-in Resource [" + resource + "] contains an open stream: " + 5 "cannot determine validation mode automatically. Either pass in a Resource " + 6 "that is able to create fresh streams, or explicitly specify the validationMode " + 7 "on your XmlBeanDefinitionReader instance."); 8 } 9 10 InputStream inputStream; 11 try { 12 inputStream = resource.getInputStream(); 13 } 14 catch (IOException ex) { 15 throw new BeanDefinitionStoreException( 16 "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " + 17 "Did you attempt to load directly from a SAX InputSource without specifying the " + 18 "validationMode on your XmlBeanDefinitionReader instance?", ex); 19 } 20 21 try { 22 return this.validationModeDetector.detectValidationMode(inputStream); 23 } 24 catch (IOException ex) { 25 throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + 26 resource + "]: an error occurred whilst reading from the InputStream.", ex); 27 } 28 }
最终,调用了XmlValidationModeDetector的detectValidationMode方法:
public int detectValidationMode(InputStream inputStream) throws IOException { // Peek into the file to look for DOCTYPE. BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); try { boolean isDtdValidated = false; String content; while ((content = reader.readLine()) != null) { content = consumeCommentTokens(content); if (this.inComment || !StringUtils.hasText(content)) { continue; } if (hasDoctype(content)) { isDtdValidated = true; break; } if (hasOpeningTag(content)) { // End of meaningful data... break; } } return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD); } catch (CharConversionException ex) { // Choked on some character encoding... // Leave the decision up to the caller. return VALIDATION_AUTO; } finally { reader.close(); } }
如果xml文档中包含了“DOCTYPE”关键字,就是DTD方式校验,否则就是XSD方式校验。以下是DTD方式的xml配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
获取Document
取得了xml文件的验证方式以后就可以加载XML文件了,加载的工作委托给了DefaultDocumentLoader:
/** * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured * XML parser. */ public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }
loadDocument方法涉及到一个参数EntityResolver,这个参数保存了xsd文件本次存放的路径,在spring各个包下的resources/META-INF/spring.schemas中定义了本地xsd文件存放的路径。
解析、注册BeanDefinition
获得Document以后调用XmlBeanDefinitionReader的registerBeanDefinitions(Document doc,Resouce resouce)方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(getEnvironment()); //获取BeanDefinition的registry对象,DefaultListableBeanFactory,初始化XmlBeanDefinitionReader时进行的设置 int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
在这个方法里创建了BeanDefinitionDocumentReader,解析、注册BeanDefinition的工作又委托给了它。
/** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). * <p>Opens a DOM Document; then initializes the default settings * specified at the {@code <beans/>} level; then parses the contained bean definitions. */ public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { //BeanDefinition读取过程中传递的上下文,封装相关的的配置和状态 this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
第二个入参XmlReaderContext可以关注下:
/** * Extension of {@link org.springframework.beans.factory.parsing.ReaderContext}, * specific to use with an {@link XmlBeanDefinitionReader}. Provides access to the * {@link NamespaceHandlerResolver} configured in the {@link XmlBeanDefinitionReader}. * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 */ public class XmlReaderContext extends ReaderContext { private final XmlBeanDefinitionReader reader;//注册BeanDefinition用实际上就是DefaultListableBeanFacotry private final NamespaceHandlerResolver namespaceHandlerResolver;//根据xml文件命名空间,查找对应的Handler去解析属性(除了固定的属性的自定义属性)
//////////省略......................
我们看一下NamespaceHandlerResolver的默认实现DefaultNamespaceHandlerResolver:
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver { /** * The location to look for the mapping files. Can be present in multiple JAR files. */ public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers"; /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); /** ClassLoader to use for NamespaceHandler classes */ private final ClassLoader classLoader; /** Resource location to search for */ private final String handlerMappingsLocation; /** Stores the mappings from namespace URI to NamespaceHandler class name / instance */ private volatile Map<String, Object> handlerMappings; //省略........................ /** * Load the specified NamespaceHandler mappings lazily. */ private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(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 this.handlerMappings; }
getHandlerMappings()方法会加载classpath下所有的"META-INF/spring.handlers"文件,并存放在handlerMappings(Map)中,在后续的解析xml自定义属性时会根据命名空间在handlerMappings中查找NamespaceHandler去解析
自定义的属性。
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
这是spring-beans中的spring。handlers文件。这些自定义的NamespaceHandler都必须实现NamespaceHandler接口或继承NamespaceHandlerSupport,比如自定义的标签Dubbo:
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
* DubboNamespaceHandler * * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); } }
DubboBeanDefinitionParser是用来解析自定义属性的,它需要实现BeanDefinitionParser接口:
public class DubboBeanDefinitionParser implements BeanDefinitionParser {
public interface BeanDefinitionParser {
/**
* Parse the specified {@link Element} and register the resulting
* {@link BeanDefinition BeanDefinition(s)} with the
* {@link org.springframework.beans.factory.xml.ParserContext#getRegistry() BeanDefinitionRegistry}
* embedded in the supplied {@link ParserContext}.
* <p>Implementations must return the primary {@link BeanDefinition} that results
* from the parse if they will ever be used in a nested fashion (for example as
* an inner tag in a {@code <property/>} tag). Implementations may return
* {@code null} if they will <strong>not</strong> be used in a nested fashion.
* @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
* @param parserContext the object encapsulating the current state of the parsing process;
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* @return the primary {@link BeanDefinition}
*/
BeanDefinition parse(Element element, ParserContext parserContext);
}
继续看解析过程:
/** * Register each bean definition within the given root {@code <beans/>} element. */ protected void doRegisterBeanDefinitions(Element root) { //beans标签的profile属性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(this.readerContext, root, parent); preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
1.方法开始判断<beans>是否包含profile属性,如果存在校验环境变量是进行了设置。profile的作用类似maven的profile,可以做到开发、测试、生产环境的切换。
<?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:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <bean id="userService" class="org.springframework.example.service.impl.UserServiceImpl" lazy-init="true"></bean> <beans profile="dev"> <bean id="testService" class="org.springframework.example.service.impl.TestServiceImpl" scope="prototype" lazy-init="true"> <property name="userService" ref="userService"></property> </bean> </beans> <beans profile="test"> </beans>
这样就实现了通过profile标记不同的环境,接下来就可以通过设置spring.profiles.default和spring.profiles.active这两个属性来激活和使用对应的配置文件。default为默认,如果没有通过active来指定,那么就默认使用default定义的环境。
这两个属性可以通过多种方法来设置:
- 在web.xml中作为web应用的上下文参数context-param;
- 在web.xml中作为DispatcherServlet的初始化参数;
- 作为JNDI条目;
- 作为环境变量;
- 作为JVM的系统属性;
- 在集成测试类上,使用@ActiveProfiles注解配置。
前两者都可以在web.xml文件中设置:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:/applicationContext*.xml </param-value> </context-param> <!-- 在上下文context-param中设置profile.default的默认值 --> <context-param> <param-name>spring.profiles.default</param-name> <param-value>development</param-value> </context-param> <!-- 在上下文context-param中设置profile.active的默认值 --> <!-- 设置active后default失效,web启动时会加载对应的环境信息 --> <context-param> <param-name>spring.profiles.active</param-name> <param-value>dev</param-value> </context-param> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 在DispatcherServlet参数中设置profile的默认值,active同理 --> <init-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
激活指定的环境,也可以通过JVM参数来设置,可以在tomcat的启动脚本中加入以下JVM参数来激活:
-Dspring.profiles.active="dev"
在程序中,也可以通过 @Profile("...") 对某些资源进行注解,这样只有当选择对应的环境时,才会产生对应的bean,如:
@Bean @Profile("dev") public DataSource jndiDataSource(){ JndiObjectFactoryBean jofb=new JndiObjectFactoryBean(); jofb.setJndiName("jndi/iDS"); jofb.setResourceRef(true); jofb.setProxyInterface(xxx.class); return (DataSource) jofb.getObject(); } }
2.创建BeanDefinitionParserDelegate,xml具体的解析工作都是由它完成。
// Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(this.readerContext, root, parent);
因为<beans>里是可以嵌套<beans>的,所以递归调用这个方法时root是不一样的,需要重新生成BeanDefinitionParserDelegate 。
3.委托BeanDefinitionParserDelegate解析xml节点
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 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)) { //解析固定的元素,beans、import、alias、bean等 parseDefaultElement(ele, delegate); } else { //解析自定义元素 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
4.解析spring默认的标签
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); } //解析beans,递归调用doRegisterBeanDefinitions else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
bean标签是最长用到的,看一下它是如何解析的:
/** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //解析bean节点,返回BeanDefinitionHolder BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //将BeanDefinition注册到DefaultListableBeanFacotry BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
具体解析工作是有BeanDefinitionParserDelegate 完成
/** * 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}. */ public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //获取id属性 String id = ele.getAttribute(ID_ATTRIBUTE); //获取name属性,多个可以,或;分隔 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); /*name属性是别名,id属性才是beanName*/ List<String> aliases = new ArrayList<String>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = 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"); } } //校验beanName和别名不能和其他<bean>的重复 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } //解析<bean> AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { //如果没有设置id和name属性,默认取包名.类名#count, //如果是子类,没设置class,默认parent$child#count 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)) { //没有设置id和name,别名就是包名.类名 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); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
具体解析内容:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); //解析bean标签的属性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //bean标签的子元素meta parseMetaElements(ele, bd); //lookup-method parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //replaced-method,运行时进行方法替换 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);//bean的子标签property 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; }
解析bean标签:
/** * Apply the attributes of the given bean element to the given bean * definition. * @param ele bean declaration element * @param beanName bean name * @param containingBean containing bean definition * @return a bean definition initialized according to the bean element attributes */ public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) { /** * scope:bean的作用域, * --singleton:单例 * --prototype:多实例 * scope=“singleton”,singleton=true只能二选一 */ if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { // Spring 2.x "scope" attribute bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Specify either 'scope' or 'singleton', not both", ele); } } /** * singleton:是否单例 * --true * --false */ else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // Spring 1.x "singleton" attribute bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ? BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE); } else if (containingBean != null) { // Take default from containing bean in case of an inner bean definition. bd.setScope(containingBean.getScope()); } /** * abstract: * --true:spring不会初始化该bean * --false:如果是抽象类设置为false,会抛异常 */ if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } /** * lazy-init:延迟加载 * --default:取beans标签default-lazy-init属性,如果没有默认false * --true * --false */ String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); /** * autowire:自动装配 * --default:如果bean上指定了default,它会去beans标签去找default-autowire属性,beans不设置默认no * --no:spring不帮忙去匹配,但是bean的property属性必须定义ref * --byName:根据名字匹配(id和name),实际是根据属性的set方法的名称匹配,例如属性是service,但是set方法是setUserService,这个时候匹配是名称为userService的bean * --byType:根据类型匹配,如果找到多个相同类型的bean会抛异常 * --constructor:根据构造器参数名匹配 */ String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); /** * dependency-check:依赖检查,3.x版本已经废弃 * --simple:对基本类型、字符串和数组进行依赖检查 * --object:对依赖的对象进行检查 * --all:对全部属性进行检查 * --none:不进行依赖检查 */ String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE); bd.setDependencyCheck(getDependencyCheck(dependencyCheck)); if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } /** * autowire-candidate:自动装配候选人 * true: 默认 * false:容器在为其他bean装配属性时将不考虑该bean */ 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:优先选择状态,一个接口多个实现,按类型自动装填时会报错,设置该属性为true,可以优先装填,不会报错 * true、false */ if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } /** * init-method:初始化bean的时候调用 */ if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); if (!"".equals(initMethodName)) { bd.setInitMethodName(initMethodName); } } else { if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } } /** * destroy-method:销毁bean之前调用 */ if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); if (!"".equals(destroyMethodName)) { bd.setDestroyMethodName(destroyMethodName); } } else { if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } } /** * factory-method:创建bena的工厂方法 */ if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } /** * factory-bean:创建bean的工厂bean */ if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
解析bean的子标签property:
/** * Parse a property element. */ public void parsePropertyElement(Element ele, BeanDefinition bd) { String propertyName = ele.getAttribute(NAME_ATTRIBUTE); //校验property name属性不能为空 if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }
解析property 的value值:
/** * Parse a value, ref or collection sub-element of a property or * constructor-arg element. * @param ele subelement of property element; we don't know which yet * @param defaultValueType the default type (class name) for any * {@code <value>} tag that might be created */ public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } else if (nodeNameEquals(ele, REF_ELEMENT)) { // 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 the same XML file. refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); 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', 'local' 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)) { return parseIdRefElement(ele); } else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } else if (nodeNameEquals(ele, NULL_ELEMENT)) { // 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)) { return parseArrayElement(ele, bd); } else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
在这个方法中会解析各种的property属性,包括直接ref引用,List,Map,Set等等,我们就找个List的解析看一下,其他集合类似:
/** * Parse a list element. */ public List parseListElement(Element collectionEle, BeanDefinition bd) { String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); NodeList nl = collectionEle.getChildNodes(); //继承ArrayList,实现Mergeable接口,用来做集合value值合并 ManagedList<Object> target = new ManagedList<Object>(nl.getLength()); target.setSource(extractSource(collectionEle)); target.setElementTypeName(defaultElementType); //是否可以合并,merge=true,会将父类相同List属性的值与子类的List值合并 target.setMergeEnabled(parseMergeAttribute(collectionEle)); parseCollectionElements(nl, target, bd, defaultElementType); return target; } protected void parseCollectionElements( NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) { for (int i = 0; i < elementNodes.getLength(); i++) { Node node = elementNodes.item(i); if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//递归解析 target.add(parsePropertySubElement((Element) node, bd, defaultElementType)); } } }
解析后的BeanDefinition被封装为BeanDefinitionHolder返回。接下来就是就是BeanDefinition的注册。
我们返回DefaultBeanDefinitionDocumentReader:
/** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //解析bean节点,返回BeanDefinitionHolder BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //将BeanDefinition注册到DefaultListableBeanFacotry BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
调用BeanDefinitionReaderUtils.registerBeanDefinition方法进行BeanDefinition的注册,其中入参registry其实就是BeanFactory:DefaultListableBeanFacotry,它本身实现了BeanDefinitionRegistry接口.
/** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreException if registration failed */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
DefaultListableBeanFactory具体注册方法:
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 { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; synchronized (this.beanDefinitionMap) { oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } this.beanDefinitionMap.put(beanName, beanDefinition); } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
到这,BeanDefinition的加载、解析和注册过程就完成了。
总结:
1.AbstractApplicationContext 的refresh()定义容器初始化的整个流程,它是个模板方法具体实现都有子类去完成。
2.由AbstractApplicationContext的子类AbstractRefreshableApplicationContext来完成容器的刷新工作,由它来创建了新的容器,DefaultListableBeanFactory。
3.BeanDefinition资源不同(XML、注解等),需要由不同的子类去继承AbstractRefreshableApplicationContext进行资源的定位,例如AbstractXmlApplicationContext。
4.AbstractXmlApplicationContext自己本身不会去定位解析BeanDefinition,它创建了BeanDefinitionReader,委托的它来完成定位、加载工作。
5.BeanDefinitionReader完成定位、加载工作。也就是先获取Resource,通过Resource获取InputStream,根据InputStream获取Document。然后创建了BeanDefinitionDocumentReader。
6.BeanDefinitionDocumentReader最后完成xml的解析工作获得BeanDefinitionHolder。
7.最后BeanDefinitionDocumentReader委托DefaultListableBeanFactory完成注册。