美文网首页
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