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 &lt;bean&gt;} 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 &lt;value&gt;} 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完成注册。

 

posted @ 2018-03-15 18:43  两条闲鱼  阅读(5766)  评论(0编辑  收藏  举报