美文网首页Spring
Spring源码之Bean容器的基本实现

Spring源码之Bean容器的基本实现

作者: 九点半的马拉 | 来源:发表于2019-08-17 18:07 被阅读0次

    前言

    作为Spring源码的第一篇,首先先简单介绍Spring的整体架构


    1.png
    1. Core Container(核心容器)
      它包含了Core、Beans、Context和Expression Language模块。
      Core和Beans模块是框架的基础部分,提供控制反转和依赖注入特性。基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。Core模块主要包含Spring模块基本的核心工具类,Context模块构建于Core和Beans模块基础之上,提供一种类似于JDNI注册器的框架式的对象访问方法。ApplicationContext接口是Context模块的关键。
    2. Data Access / Integration
      包含了JDBC、ORM、OXM、JMS和Transaction模块。
      ORM模块为流行的对象-关系映射API,如JPA、JDO、Hibernate、iBatis等,提供了一个交互层。
    3. WEB
      Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。
    4. AOP
      它让你可以定义例如方法拦截器和切点,从而将逻辑代码分开,降低它们之间的耦合性。Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务,通过使用Spring AOP,不用依赖EJB组件,就可以将声明性事务管理集成到应用程序中。

    Bean容器

    bean是Spring中最核心的东西,因为Spring就像是个大水桶,而bean就像是容器中的水,水桶脱离了水也就没什么作用了。在这里我讲解一下利用读取XML文件进行Bean容器的基本实现。


    2.png

    核心类一: DefaultListableBeanFactory
    XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载bean的默认实现,对于XmlBeanFactory,使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取。
    现在先简单介绍上图中各个类的作用,具体的使用信息在后面的文章会讲到。

    • singletonBeanRegistry: 定义对单例的注册及获取。
    • BeanFactory: 定义获取bean及bean的各种属性。
    • DefaultSingletonBeanRegistry: 对接口SingletonBeanRegistry各函数的实现。
    • HierarchicalBeanFactory: 继承BeanFactory,增加了对parentFactory的支持。
    • BeanDefinitionRegistry: 定义对BeanDefinition的各种增删改操作。
    • ConfigurableBeanFactory: 提供配置Factory的各种方法。
    • ListableBeanFactory: 根据各种 条件获取bean的配置清单。
    • AutowireCapableBeanFactory: 提供创建bean、自动注入、初始化以及应用bean的后处理器。
      DefaultListableBeanFactory综合了上面的所有功能,主要是对bean注册后的处理。
      XmlBeanFactory对DefaultListableBeanFactory类进行了扩展,主要用于从XML文档中读取BeanDefinition,以及注册及获取bean。
      核心类二: XmlBeanDefinitionReader
      对资源文件进行读取、解析及注册
      简单介绍该类继承的抽象类和接口
    • ResourceLoader: 定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource.
    • BeanDefinitionReader: 主要定义资源文件读取并转换为BeanDefinition的各个功能。
    • BeanDefinitionDocumentReader: 定义读取Document并注册BeanDefinition.

    容器的基础XmlBeanFactory

    当前获取创建beanFactory的代码如下

    BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))

    首先,读取配置文件,用ClassPathResource进行封装。
    在Java中,将不同来源的资源抽象成URL,通过注册不同的handler来处理不同来源的资源的读取逻辑。Spring抽象出一个统一的接口来对这些底层资源进行统一访问,即Resource.

    public interface InputStreamSource { 
    InputStream getInputStream() throws IOException; 
    } 
    
    public interface Resource extends InputStreamSource { 
        boolean exists(); 
        boolean isReadable(); 
        boolean isOpen(); 
        URL getURL() throws IOException; 
        URI getURI() throws IOException; 
        File getFile() throws IOException; 
        long contentLength() throws IOException; 
        long lastModified() throws IOException; 
        Resource createRelative(String relativePath) throws IOException; 
        String getFilename(); 
        String getDescription(); 
    } 
    

    对不同来源的资源文件都有相应的Resource实现:

    • 文件 FileSystemResource
    • Classpath资源 ClassPathResource
    • URL资源 UrlResource
    • InputStream资源 InputStreamResource
    • Byte数组 ByteArrayResource
    1. XmlBeanFactory loadBeanDefinitions(Resource) --> 进行资源加载
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    
    public XmlBeanFactory(Resource resource) throws BeansException {
                this(resource, null);
        }
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
                super(parentBeanFactory);
                this.reader.loadBeanDefinitions(resource);
        }
    
    
    @Override
        public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
                        return loadBeanDefinitions(new EncodedResource(resource));
        }
    

    从上面可知,主要加载Bean的步骤

    • 封装资源文件,当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodedResource类进行封装。
    • 获取输入流。从Resource中获取对应的InputStream并构造InputSource
    • 通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions.
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
                Assert.notNull(encodedResource, "EncodedResource must not be null");
                if (logger.isInfoEnabled()) {
                    logger.info("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 {
                    //从encodedResource中获取封装的Resource对象并再次从Resource中获取其中的inputStream
                    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();
                    }
                }
            }
    

    核心代码doLoadBeanDefinitions

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
                try {
                    Document doc = doLoadDocument(inputSource, resource);
                    return registerBeanDefinitions(doc, resource);
                }
                catch (BeanDefinitionStoreException ex) {
                    throw ex;
                }
                catch (SAXParseException ex) {
                    throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                            "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
                }
                catch (SAXException ex) {
                    throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                            "XML document from " + resource + " is invalid", ex);
                }
                catch (ParserConfigurationException ex) {
                    throw new BeanDefinitionStoreException(resource.getDescription(),
                            "Parser configuration exception parsing XML from " + resource, ex);
                }
                catch (IOException ex) {
                    throw new BeanDefinitionStoreException(resource.getDescription(),
                            "IOException parsing XML document from " + resource, ex);
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(resource.getDescription(),
                            "Unexpected exception parsing XML document from " + resource, ex);
                }
        }
    

    主要步骤:

    1. 获取对XML文件的检验方式
    2. 加载XML文件,并得到对应的Document.
    3. 根据返回的Document注册Bean信息。
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
               //为DefaultBeanDefinitionDocumentReader
                BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
               //记录统计前的BeanDefinition
                int countBefore = getRegistry().getBeanDefinitionCount();
              //加载及注册Bean
                documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
             //记录本次加载的BeanDefinition个数。
                return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    

    之后调用DefaultBeanDefinitionDocumentReader的registerBeanDefinitions()

    @Override
        public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
                this.readerContext = readerContext;
                logger.debug("Loading bean definitions");
                Element root = doc.getDocumentElement();
                doRegisterBeanDefinitions(root);
        }
    

    注意,下面是最最最核心的代码

    protected void doRegisterBeanDefinitions(Element root) {
            // 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(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);
                        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                            if (logger.isInfoEnabled()) {
                                logger.info("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是两个抽象方法,是为了子类而设计的,这是设计模式的模板方法模式,如果子类想在Bean解析前后做一些处理,那么只需处理这两个方法即可。

    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)) {
                                //对bean处理。
                                parseDefaultElement(ele, delegate);
                            }
                            else {
                                //对bean处理
                                delegate.parseCustomElement(ele);
                            }
                        }
                    }
                }
                else {
                    delegate.parseCustomElement(root);
                }
            }
    

    如果采用Spring默认的配置,Spring当然知道怎么做,但是如果是自定义的,那么就需要用户实现一些接口及配置了。具体的元素解析在下一篇编写。

    相关文章

      网友评论

        本文标题:Spring源码之Bean容器的基本实现

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