Springソース分析-beanの解析(3)

16507 ワード

Springソース分析-beanの解析(3)
現在のバージョンSpring 4.3.8
カスタムラベルの解析
カスタムラベルの使用
多くの場合、デフォルトの設定を使うのは煩雑すぎると、解析作業は考えざるを得ない負担かもしれません.Springは拡張可能Schemeのサポートを提供しています.大体以下のステップが必要です.
  • は、拡張が必要なコンポーネント
    package io.github.binglau.bean;
    
    import lombok.Data;
    
    /**
     *     :
     */
    
    @Data
    public class User {
        private String userName;
        private String email;
    }
    
    
  • を作成する.
  • は、XSDファイル記述コンポーネントの内容を定義する
    
    
    
        
            
                
                
                
            
        
    
    
  • は、XSDファイルの定義とコンポーネント定義
    package io.github.binglau;
    
    import io.github.binglau.bean.User;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
    import org.springframework.util.StringUtils;
    import org.w3c.dom.Element;
    
    /**
     *     :
     */
    
    public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
        // Element     
        @Override
        protected Class> getBeanClass(Element element) {
            return User.class;
        }
    
        //   element            
        @Override
        protected void doParse(Element element, BeanDefinitionBuilder builder) {
            String userName = element.getAttribute("userName");
            String email = element.getAttribute("email");
            //          BeanDefinitionBuilder  ,       bean           beanFactory  
            if (StringUtils.hasText(userName)) {
                builder.addPropertyValue("userName", userName);
            }
            if (StringUtils.hasText(email)) {
                builder.addPropertyValue("email", email);
            }
        }
    }
    
    
  • を解析するためのファイルを作成します.
  • は、Namespace HandlerSupportから拡張されたハーndlerファイルを作成し、目的は、Spring容器
    package io.github.binglau;
    
    import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
    
    /**
     *     :
     */
    
    public class MyNamespaceHandler extends NamespaceHandlerSupport {
        @Override
        public void init() {
            registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
        }
    }
    
    
  • にコンポーネントを登録することである.
  • Spring.handersとSpring.schemasファイルを作成します.
    # Spring.handlers
    http\://www.binglau.com/schema/user=io.github.binglau.MyNamespaceHandler
    
    # Spring.schemas
    http\://www.binglau.com/schema/user.xsd=user-xsd.xsd
    
  • テスト:
    
    
    
        
    
        
    
    
    
    package io.github.binglau;
    
    import io.github.binglau.bean.User;
    import org.junit.Test;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.ClassPathResource;
    
    /**
     *     :
     */
    
    public class BeanFactoryTest {
        @Test
        public void testSimpleLoad() {
            BeanFactory context = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
            User user = (User)context.getBean("testbean");
            System.out.println(user);
        }
    }
    
    /**
      :
    User(userName=aaa, email=bbb)
    **/
    
    カスタムラベル解析
        /**
         * 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)) {
                            parseDefaultElement(ele, delegate);
                        }
                        else {
                            delegate.parseCustomElement(ele);
                        }
                    }
                }
            }
            else {
                delegate.parseCustomElement(root);
            }
        }
    
    BeanDefinitionParserは、カスタムラベルの解析である.delegate.parseCustomElement(ele)
        public BeanDefinition parseCustomElement(Element ele) {
            return parseCustomElement(ele, null);
        }
        // containingBd     bean,             null
        public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
            //          
            String namespaceUri = getNamespaceURI(ele);
            //             NamespaceHandler
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            if (handler == null) {
                error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
                return null;
            }
            //        NamespaceHandler     
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    
    タグの名前空間を取得
    直接呼び出しBeanDefinitionParserDelegate.parseCustomElementの方法
        public String getNamespaceURI(Node node) {
            return node.getNamespaceURI();
        }
    
    カスタムラベルプロセッサを抽出org.w3c.dom.Node、すなわちprivate final XmlReaderContext readerContextnew ClassPathResource("beanFactory.xml")が初期化されると、その属性readerContextnamespaceHandlerResolverの例に初期化されているので、ここでは実際にDefaultNamespaceHandlerResolverの方法が呼び出されている.
        /**
         * Locate the {@link NamespaceHandler} for the supplied namespace URI
         * from the configured mappings.
         * @param namespaceUri the relevant namespace URI
         * @return the located {@link NamespaceHandler}, or {@code null} if none found
         */
        @Override
        public NamespaceHandler resolve(String namespaceUri) {
            //           handler   
            Map handlerMappings = getHandlerMappings();
            //              
            Object handlerOrClassName = handlerMappings.get(namespaceUri);
            if (handlerOrClassName == null) {
                return null;
            }
            else if (handlerOrClassName instanceof NamespaceHandler) {
                //          ,       
                return (NamespaceHandler) handlerOrClassName;
            }
            else {
                //       ,        
                String className = (String) handlerOrClassName;
                try {
                    //              
                    Class> handlerClass = ClassUtils.forName(className, this.classLoader);
                    if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                        throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                                "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                    }
                    //     
                    NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                    //        NamespaceHandler       
                    namespaceHandler.init();
                    //      
                    handlerMappings.put(namespaceUri, namespaceHandler);
                    return namespaceHandler;
                }
                catch (ClassNotFoundException ex) {
                    throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                            namespaceUri + "] not found", ex);
                }
                catch (LinkageError err) {
                    throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                            namespaceUri + "]: problem with handler class file or dependent class", err);
                }
            }
        }
    
    上記の呼び出しDefaultNamespaceHandlerResolverでは、以前のカスタムラベルを参照して使用します.
        public void init() {
            registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
        }
    
    カスタマイズされた名前空間を取得した後、馬に戻ってnamespaceHandler.init();の登録を行い、カスタムラベルをサポートします.
    登録後、名前空間プロセッサは、ラベルによって異なる解像度を呼び出して解析することができます.BeanDefinitionParserの主な機能は、getHandlerMappingsのプロファイルを読み出し、設定ファイルをmapにキャッシュすることである.
        /**
         * Load the specified NamespaceHandler mappings lazily.
         */
        private Map getHandlerMappings() {
            //               
            if (this.handlerMappings == null) {
                synchronized (this) {
                    if (this.handlerMappings == null) {
                        try {
                            // this.handlerMappingsLocation             META-INF/Spring.handlers
                            Properties mappings =
                                    PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                            if (logger.isDebugEnabled()) {
                                logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                            }
                            Map handlerMappings = new ConcurrentHashMap(mappings.size());
                            //   Properties         Map     handlerMappings  
                            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;
        }
    
    タブ解析Spring.handlers
        /**
         * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
         * registered for that {@link Element}.
         */
        @Override
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            //             
            return findParserForElement(element, parserContext).parse(element, parserContext);
        }
        /**
         * Locates the {@link BeanDefinitionParser} from the register implementations using
         * the local name of the supplied {@link Element}.
         */
        private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
            //       ,        user,        ,    localName   user
            String localName = parserContext.getDelegate().getLocalName(element);
            //    user         ,     
            // registerBeanDefinitionParser("user", new UserBeanDefinitionParser())       
            BeanDefinitionParser parser = this.parsers.get(localName);
            if (parser == null) {
                parserContext.getReaderContext().fatal(
                        "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
            }
            return parser;
        }
    
    パース方法に対する処理NamespaceHandlerSupport#parse
        @Override
        public final BeanDefinition parse(Element element, ParserContext parserContext) {
            //        
            AbstractBeanDefinition definition = parseInternal(element, parserContext);
            if (definition != null && !parserContext.isNested()) {
                try {
                    String id = resolveId(element, definition, parserContext);
                    if (!StringUtils.hasText(id)) {
                        parserContext.getReaderContext().error(
                                "Id is required for element '" + parserContext.getDelegate().getLocalName(element)
                                        + "' when used as a top-level tag", element);
                    }
                    String[] aliases = null;
                    if (shouldParseNameAsAliases()) {
                        String name = element.getAttribute(NAME_ATTRIBUTE);
                        if (StringUtils.hasLength(name)) {
                            aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
                        }
                    }
                    //   AbstractBeanDefinition     BeanDefinitionHolder    
                    BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
                    registerBeanDefinition(holder, parserContext.getRegistry());
                    if (shouldFireEvents()) {
                        //             
                        BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
                        postProcessComponentDefinition(componentDefinition);
                        parserContext.registerComponent(componentDefinition);
                    }
                }
                catch (BeanDefinitionStoreException ex) {
                    parserContext.getReaderContext().error(ex.getMessage(), element);
                    return null;
                }
            }
            return definition;
        }
    
    AbstractBeanDefinitionParser#parse
        /**
         * Creates a {@link BeanDefinitionBuilder} instance for the
         * {@link #getBeanClass bean Class} and passes it to the
         * {@link #doParse} strategy method.
         * @param element the element that is to be parsed into a single BeanDefinition
         * @param parserContext the object encapsulating the current state of the parsing process
         * @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
         * @throws IllegalStateException if the bean {@link Class} returned from
         * {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}
         * @see #doParse
         */
        @Override
        protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            String parentName = getParentName(element);
            if (parentName != null) {
                builder.getRawBeanDefinition().setParentName(parentName);
            }
            //           class,             UserBeanDefinitionParser    getBeanClass   
            Class> beanClass = getBeanClass(element);
            if (beanClass != null) {
                builder.getRawBeanDefinition().setBeanClass(beanClass);
            }
            else {
                //         getBeanClass               getBeanClassName   
                String beanClassName = getBeanClassName(element);
                if (beanClassName != null) {
                    builder.getRawBeanDefinition().setBeanClassName(beanClassName);
                }
            }
            builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
            if (parserContext.isNested()) {
                //             scope   
                // Inner bean definition must receive same scope as containing bean.
                builder.setScope(parserContext.getContainingBeanDefinition().getScope());
            }
            if (parserContext.isDefaultLazyInit()) {
                // Default-lazy-init applies to custom bean definitions as well.
                //       
                builder.setLazyInit(true);
            }
            //         doParse       
            doParse(element, parserContext, builder);
            return builder.getBeanDefinition();
        }
    
        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
            doParse(element, builder);
        }
    
    参考書
    『Springソースの深さ解析』