通過上一篇源碼的分析已經完成了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完成注冊。
