美文网首页
Spring IOC的深入理解(三)

Spring IOC的深入理解(三)

作者: chengcongyue | 来源:发表于2019-04-15 11:43 被阅读0次

引言

从第三章开始,我们从源码的角度,对spring IOC进行分析

核心类的了解

图片.png
第一个要了解的类是DefaultListtableBeanFactory.Spring Bean的创建是典型的工厂模式,上面的工厂也是一个个不同的IOC容器.其中他们的父类接口为BeanFactory.定义了容器的基本功能规范.然后它有三个子类ListableBeanFactory,HierachicaBeanFactory,AutoWireCapableBeanFactory.最终实现了全部的功能的容器为DefaultListableBeanFactory.这就是第一个核心的类.DefaultListableBeanFactory是Spring注册以及加载的核心实现.然后就是XmlBeanFactory,主要是读取Xml配置文件中的BeanDefinition,然后bean的注册和获取是通过他的父类DefaultListableBeanFactory实现,唯一不同的就是增加了XmlBeanDefinitionReader的reader属性.XmlBeanDefinition主要是通过reader属性对资源文件进行读取和注册.
第二个核心类就是XMLBeanDefinitionReader,我们可以通过这个类和其中的功能对资源文件的解析,加载,注册进行梳理
//它不是XMLBeanDefinitionReader类中的属性,而是它父类中的属性
private ResourceLoader resourceLoader;
//它继承了这个接口,也就是这个接口完成了从资源文件的读取并实现转化成BeanDefinition的操作
public interface BeanDefinitionReader 
public interface EnvironmentCapable
//将资源文件转化成Document的操作
private DocumentLoader documentLoader = new DefaultDocumentLoader();
BeanDefinitionDocumentReader
public class BeanDefinitionParserDelegate

我们大致来说一下Xml配置文件读取的大概步骤

  • 通过继承AbstractBeanDefinitionReader中ResourceLoader,使用它将资源文件路径转化为Resource文件.
  • 将Resource文件通过 DocumentLoader 转化为Document文件
  • 通过BeanDefinitionDocumentReader读取Document并注册BeanDefinition
  • BeanDefinitionParserDelegate来对Element进行读取
    我们来画一个图解释一下


    图片.png

容器XMLBeanFactory的初始化

我们先来看这样的一段代码

XMLBeanFactory beanFactory=new XMLBeanFactory(new ClassPathResource("text.xml"));
图片.png

我们首先来看看ClassPathResource是如何将text.xml转化成Resource的.

配置文件的封装

ClassPathResource是实现了Resource接口的,抽象了所有Spring内部用到的资源,File,URL,ClassPath下的资源和Byte Array.它只有一个方法的定义
getInputStream().对于不同的资源文件都有不同的实现,ClassPathResource是对ClassPath资源进行读取的.
如下的代码

Resource rs=new ClassPathResource("text.xml");
InputStream in=rs.getInputStream();

我们了解了Resource,然后我们再来了解一下ClassPathResource,它是通过class或者classloader提供的底层方法进行读取的

public InputStream getInputStream() throws IOException {
        InputStream is;
        if (this.clazz != null) {
            is = this.clazz.getResourceAsStream(this.path);
        }
        else if (this.classLoader != null) {
            is = this.classLoader.getResourceAsStream(this.path);
        }
        else {
            is = ClassLoader.getSystemResourceAsStream(this.path);
        }
        if (is == null) {
            throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
        }
        return is;
    }

看上面的clazz和ClassLoader.
这个时候我们完成了配置文件到Resource文件转化.我们在回到我们的测试代码

XMLBeanFactory beanFactory=new XMLBeanFactory(new ClassPathResource("text.xml"));

下面执行的就是XMLBeanFactory的构造方法

    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);就是核心的资源加载的核心体现.但是我们也注意到super(parentBeanFactory);它实际我们只要ctrl+鼠标一直找到它真正的实现.

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) 
throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
        super(parentBeanFactory);
    }
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
        this();
        setParentBeanFactory(parentBeanFactory);
    }
public AbstractAutowireCapableBeanFactory() {
        super();
        ignoreDependencyInterface(BeanNameAware.class);
        ignoreDependencyInterface(BeanFactoryAware.class);
        ignoreDependencyInterface(BeanClassLoaderAware.class);
    }

我们来看一下ignoreDependencyInterface接口的含义,忽略接口的自动装配能力,大概是A中有属性B,加载A是会默认初始化B,这就是自动装配,但是当B实现了BeanNameAware的接口时,就无法实现.
将无关紧要的属性分析完之后,我们就可以分析核心代码了this.reader.loadBeanDefinitions(resource),首先我们先分析一下this.reader是XmlBeanDefinitionReader的reader.注意:
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);this就是将工厂自己传给reader
这个代码是整个资源加载的切入点,也是IOC容器初始化的切入点
首先我们reader将读取的Resource文件进行封装,通过EncodedResource进行封装

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

在reader中体现了EncodedResource的主要逻辑,如果有编码,就把有编码的InputStream返回回去,如果没有编码,就直接返回对应的InputStream

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

然后我们来看看loadBeanDefinitions的方法

public int loadBeanDefinitions(EncodedResource encodedResource)
 throws BeanDefinitionStoreException {
    ...
    //从encodedResource中获取Resource对象,然后在获取inputStream 
    InputStream inputStream = encodedResource.getResource().getInputStream();
    ...
 //最后进入核心部分
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}

然后我们来看一下doLoadBeanDefinitions的方法

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
   ....
}
  • 获取Xml文件的验证模式
  • 加载XMl文件,并得到对应的document
  • 返回document并注册Bean信息
    然后继续分析,对于将Resource文件转化成为document的文件,这件事情是交给DocumentLoader来做的.DocumentLoader是个接口,实际上实现的是它的实现类
Document doc = doLoadDocument(inputSource, resource);
->>>>>>
    return this.documentLoader.loadDocument(inputSource, getEntityResolver()
, this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
->>>>>>
@Override
    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);
    }

这里并非是重点方法,这里就是告诉你DTD在哪儿,由程序来寻找DTD,避免了通过网络寻找.
其中将传入InputSource,然后通过DocumentBuilderFactory来解析 inputSource从而返回Document对象.
其中的EntityResolver值得我们思考,它是如何传入的?它是如何起作用的?
对于它是如何传入的,我们返回前几层的代码,看到

return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());

其中的getEntityResolver()方法就是用来获得getEntityResolver的,我们在进入这个方法看一下

protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            // Determine default EntityResolver to use.
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            }
            else {
                this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return this.entityResolver;
    }

然后就是解析并注册BeanDefinition了,回到上面的方法

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
   ....
}

然后就是去执行registerBeanDefinitions,这个时候我们有了document的对象

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了,它是通过createBeanDefinitionDocumentReader创建的,创建的实际上是它的实现类

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

doRegisterBeanDefinitions(doc.getDocumentElement());就是注册BeanDefinition的核心,这个时候就开始解析和注册了

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

处理profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
我们先来聊一下profile属性的作用:切换开发和部署环境
parseBeanDefinitions(root, this.delegate);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //root的意思就是对应的beans属性
          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)) {
                                                //两种处理方式1
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                                                 //两种处理方式2
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

其中一类是默认的parseDefaultElement(ele, delegate);,令一类是自定义的
delegate.parseCustomElement(ele);

相关文章

网友评论

      本文标题:Spring IOC的深入理解(三)

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