zoukankan      html  css  js  c++  java
  • 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完成注册。

  • 相关阅读:
    我要AFO啦好伤感啊
    noip2012~2015刷题小记录
    【20161114模拟赛】
    第5模块闯关CSS练习题
    HTML练习题
    Mysql常用命令行大全
    mysql破解密码安装与基本管理
    python 闯关之路四(下)(并发编程与数据库编程) 并发编程重点
    Python/ selectors模块及队列
    python3 中 Event.wait 多线程等待
  • 原文地址:https://www.cnblogs.com/monkey0307/p/8509701.html
Copyright © 2011-2022 走看看