美文网首页
spring(一) 容器的基本实现

spring(一) 容器的基本实现

作者: chengcongyue | 来源:发表于2019-04-08 12:40 被阅读0次

    1,核心类的介绍

    1.1

    public class DefaultListableBeanFactory 
    

    bean加载的核心部分,xmlBeanFactory中定义了XML的读取器,xmlBeanFactory对DefaultListableBeanFactory的拓展,注册和获取bean都是从父类继承的方法,增加了reader的属性,通过reader属性对资源文件读取和注册

    1.2

    public class XmlBeanDefinitionReader
    

    读取xml配置的过程如下


    读取xml文件的过程

    2,xmlBeanFactory容器的基础

    我们要深入理解一下代码的过程

    new XmlBeanFactory(new ClassPathResource("......xml"));
    

    我们先通过ClassPathResource读取xml配置文件,生成Resource对象,后续的处理就可以通过Resource提供的各种服务进行操作了

    2.1 了解配置文件的读取(resource接口)

    /**
     * Interface for a resource descriptor that abstracts from the actual
     * type of underlying resource, such as a file or class path resource.
     *
     * <p>An InputStream can be opened for every resource if it exists in
     * physical form, but a URL or File handle can just be returned for
     * certain resources. The actual behavior is implementation-specific.
     *
     * @author Juergen Hoeller
     * @since 28.12.2003
     * @see #getInputStream()
     * @see #getURL()
     * @see #getURI()
     * @see #getFile()
     * @see WritableResource
     * @see ContextResource
     * @see UrlResource
     * @see FileUrlResource
     * @see FileSystemResource
     * @see ClassPathResource
     * @see ByteArrayResource
     * @see InputStreamResource
     */
    public interface Resource extends InputStreamSource {
    
    
    
    /**
     * Simple interface for objects that are sources for an {@link InputStream}.
     *
     * <p>This is the base interface for Spring's more extensive {@link Resource} interface.
     *
     * <p>For single-use streams, {@link InputStreamResource} can be used for any
     * given {@code InputStream}. Spring's {@link ByteArrayResource} or any
     * file-based {@code Resource} implementation can be used as a concrete
     * instance, allowing one to read the underlying
     content stream multiple times.
     * This makes this interface useful as an abstract content source for mail
     * attachments, for example.
     *
     * @author Juergen Hoeller
     * @since 20.01.2004
     * @see java.io.InputStream
     * @see Resource
     * @see InputStreamResource
     * @see ByteArrayResource
     */
    public interface InputStreamSource {
    

    通过Resource和InputStreamSource就可以加载文件,按照原来的方式进行开发,如下

    Resource resource=new ClassPathResource("文件名称");
    InputStream in=resource.getInputStream();
    

    读取完毕之后,工作就交给XmlReaderDefinitionReader进行处理

    2.2 XmlFactoryFactory

    2.2.1

    //XmlFactoryFactory的构造方法
    public XmlBeanFactory(Resource resource) throws BeansException {
            this(resource, null);
        }
    
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
            super(parentBeanFactory);//用于合并
            this.reader.loadBeanDefinitions(resource);
        }
    
    

    通过阅读以上的构造方法,this.reader.loadBeanDefinitions(resource)为核心代码,xmlBeanFactoryDefinition就是在这段代码中完成的,我们首先进入super(parentBeanFactory)研究一下

    public AbstractAutowireCapableBeanFactory() {
            super();
            ignoreDependencyInterface(BeanNameAware.class);
            ignoreDependencyInterface(BeanFactoryAware.class);
            ignoreDependencyInterface(BeanClassLoaderAware.class);
        }
    
    摘自书籍

    接下来我们介绍XMLBeanFactoryDefinitionReader,整个资源的加载的切入点

    @Override
        public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            return loadBeanDefinitions(new EncodedResource(resource));
        }
    

    其中EncodedResource的作用大概是对资源文件进行编码的,编码的逻辑在EncodedResource中的getReader方法中,如下

    public Reader getReader() throws IOException {
            if (this.charset != null) {
                return new InputStreamReader(this.resource.getInputStream(), this.charset);
            }
            else if (this.encoding != null) {
                return new InputStreamReader(this.resource.getInputStream(), this.encoding);
            }
            else {
                return new InputStreamReader(this.resource.getInputStream());
            }
        }
    

    对EncodedResource的研究到此为止,然后我们转入研究loadBeanDefinitions

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
            Assert.notNull(encodedResource, "EncodedResource must not be null");
            if (logger.isTraceEnabled()) {
                logger.trace("Loading XML bean definitions from " + encodedResource);
            }
    
            Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
            if (currentResources == null) {
                currentResources = new HashSet<>(4);
                this.resourcesCurrentlyBeingLoaded.set(currentResources);
            }
            if (!currentResources.add(encodedResource)) {
                throw new BeanDefinitionStoreException(
                        "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
            }
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                }
                finally {
                    inputStream.close();
                }
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "IOException parsing XML document from " + encodedResource.getResource(), ex);
            }
            finally {
                currentResources.remove(encodedResource);
                if (currentResources.isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }
            }
        }
    

    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());是此方法的核心逻辑,我们在进入这个方法,此方法最终的三个地方如下(没有截所有的方法)

    try {
                Document doc = doLoadDocument(inputSource, resource);
                int count = registerBeanDefinitions(doc, resource);
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + count + " bean definitions from " + resource);
                }
                return count;
            }
    

    最重要的registerBeanDefinitions,根据这个注册bean的信息

    3 获取document

    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
                ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    
            DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
            if (logger.isTraceEnabled()) {
                logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
            }
            DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
            return builder.parse(inputSource);
        }
    

    大概操作就是创建对象先是DocumentBuilderFactory,再是 DocumentBuilder
    最后通过DocumentBuilder解析inputSource,来获取document的对象,这是我么注意到传入的参数中有EntityResolver类型的参数,那么此类型的参数的作用是?
    大概意思如下:通过EntityResolver来寻找xml文件的DTD的所在位置,我们进入到EntityResolver的接口中

     public abstract InputSource resolveEntity (String publicId,
                                                   String systemId)
            throws SAXException, IOException;
    

    非重点内容,大概了解

    4 BeanDefinitions

    此时我们拥有了document的文件,这是我们就开始注册并且提取bean
    重点方法

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            int countBefore = getRegistry().getBeanDefinitionCount();
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    
    //BeanDefinitionDocumentReader是一个接口
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    
    //获得已经加载好的bean的个数
    int countBefore = getRegistry().getBeanDefinitionCount();
    
    //加载并且注册bean
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    
    //返货本次加载的bean的个数
    return getRegistry().getBeanDefinitionCount() - countBefore;
    

    第一句代码的createBeanDefinitionDocumentReader实际上创建的是一个
    defaultBeanDefinitionDocumentReader,我们在进入defaultBeanDefinitionDocumentReader,查看代码如下

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
            this.readerContext = readerContext;
            doRegisterBeanDefinitions(doc.getDocumentElement());
        }
    

    在进入doRegisterBeanDefinitions的方法中,如下

    protected void doRegisterBeanDefinitions(Element root) {
        
            BeanDefinitionParserDelegate parent = this.delegate;
            this.delegate = createDelegate(getReaderContext(), root, parent);
    
            if (this.delegate.isDefaultNamespace(root)) {
                String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                if (StringUtils.hasText(profileSpec)) {
                    String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                    // We cannot use Profiles.of(...) since profile expressions are not supported
                    // in XML config. See SPR-12458 for details.
                    if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                    "] not matching: " + getReaderContext().getResource());
                        }
                        return;
                    }
                }
            }
    
            preProcessXml(root);
            parseBeanDefinitions(root, this.delegate);
            postProcessXml(root);
    
            this.delegate = parent;
        }
    

    其中的preProcessXml和postProcessXml方法,进入其中是空的,此类是面向继承而创建的,模板设计模式

    4.1 profile属性的使用

    在配置文件中,方便切换开发和部署的环境,最常用的就是更换不同的数据库

    4.2解析并注册BeanDefinition

    这是我们就可以对xml进行读取了

    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);
            }
        }
    

    相关文章

      网友评论

          本文标题:spring(一) 容器的基本实现

          本文链接:https://www.haomeiwen.com/subject/fljuiqtx.html