Spring源码深度解析-容器的实现(二)

容器的基础XmlBeanFactory

  • 在上一遍博客中我们大概了解了DefaultListableBeanFactory和XmlBeanDefinitionReader的作用。
  • DefaultListableBeanFactory的作用主要是Spring的注册,加载bean的默认实现。
  • XmlBeanDefinitionReader的作用主要是对配置文件(xml文件)的加载解析。

现在我们具体说下XMLBeanFactory,XmlBeanFactory其实就是继承了DefaultListableBeanFactory,并且自己实现了XML的读取器XmlBeanDefinitionReader,实现个性化的BeanDefinitionReader的读取

首先我们分析下面的代码的实现来讲解:

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("XXXX.xml"));

这句代码的意思很好理解,就是将我们配置的.xml文件进行加载,得到容器工厂,然后可以根据配置的< bean>标签id,获取到容器中的对象。该代码的具体实现可以参考下面的时序图:
在这里插入图片描述
从上面的时序图我们可以看到,我们首先会调用new ClassPathResource得到resource资源文件的实例对象,然后我们就可以根据这个对象进行XmlBeanFactory的初始化。

1.1 配置文件的封装

配置文件的封装指的就是new ClassPathResource(“XXXX.xml”)这一步的过程。这一步具体是怎么操作的,返回的Resource的对象呢?

在java中,将不同的资源抽象成URL,通过注册不同的handler来处理不同来源的资源的读取逻辑,一般的handler的类型使用不同的前缀来识别,如:file,http,jar等,但是URL没有默认定义相对的ClassPath或ServletContext等资源的handler。
所以Resource接口就是起到了这个作用,Spring对其内部使用的资源实现了自己的抽象结构:Resource接口封装底层的资源

下面的代码是Resource的源码,可以看到Resource接口抽象了所有的Spring内部使用到的底层资源:File,URL,Classpath

public interface InputStreamSource {
    InputStream getInputStream() throws IOException;
}

/**
*Resource继承了InputStreamSource类
**/
public interface Resource extends InputStreamSource {
	// 判断资源是否存在
    boolean exists();
    // 判断资源是否可读
    boolean isReadable();
    // 判断资源是否处于打开状态
    boolean isOpen();
    // 类型转换,不同的资源到URL/URI/FILE类型的转换
    URL getURL() throws IOException;
    URI getURI() throws IOException;
    File getFile() throws IOException;
    long contentLength() throws IOException;
    long lastModified() throws IOException;
    // 基于当前资源创建一个相对资源的方法
    Resource createRelative(String var1) throws IOException;
    String getFilename();
    // 用来在错误处理中打印信息
    String getDescription();
}
  • 对不同来源的资源文件都有相应的Resource实现:
    文件(FileSystemResource),Classpath资源(ClassPathResource),URL资源(UrlResource),InputStream资源(InputStreamResource),Byte数组(ByteArrayResource)等

在这里插入图片描述
我们平常在写代码的时候如果需要加载资源文件直接用Spring提供的类

Resource resource = new ClassPathResource("beanTest.xml");
InputStream inputStream = resource.getInputStream();

实现Resource接口的类其实也很简单,举几个例子,看下其getInputStream方法的实现:

  • ClassPathResource
if (this.clazz != null){
	is = this.clazz.getResourceAsStream(this.path);
}
else{
	is = this.classLoader.getResourceAsStream(this.path);
}
  • FileSystemResource
public InputStream getInputStream() throws IOException{
	return new FileInputStream(this.file);
}

我们在得到了InputStream对象后就可以通过new XmlBeanFactory(InputStream )来初始化XmlBeanFactory

这里注意一下,XmlBeanFactory现在已将是一个过期方法了

public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        // 重要,这里就是加载配置文件的入口,重新定义了XmlBeanDefinitionReader 调用loadBeanDefinitions方法
        this.reader = new XmlBeanDefinitionReader(this);
        this.reader.loadBeanDefinitions(resource);
    }
}

这里注意一下在生成reader对象之前还调用父类的构造函数。跟踪代码可以跟踪到类AbstractAutowireCapableBeanFactory中

    public AbstractAutowireCapableBeanFactory() {
        super();
        this.ignoreDependencyInterface(BeanNameAware.class);
        this.ignoreDependencyInterface(BeanFactoryAware.class);
        this.ignoreDependencyInterface(BeanClassLoaderAware.class);
    }

这里我们看下ignoreDependencyInterface这个方法。

ignoreDependencyInterface该方法的作用是忽略指定接口的自动装配功能。

自动装配功能:
若A中有属性B,A加载的时候发现B没有加载,则会先加载B。

  • 若忽略自动装配功能,则表示若类实现了BeanNameAware,BeanFactoryAware,BeanClassLoaderAware则不会对该类进行自动装配

1.2 加载Bean

在上一部分中我们已经拿到了具体的Resource实例,并且已经清楚了Resource的实现,在拿到该对象后我们也可以看看

this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);

这个代码是整个资源加载的切入点,首先先看时序图:
在这里插入图片描述
上面的时序图(就是loadBeanDefinitions(resource))的过程,我们可以总结下

  1. 封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodeResource类进行封装
  2. 获取输入流。从Resource中获取对应的InputStream并且构造InputSource
  3. 通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinition

我们来看下loadBeanDefinitions源码

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

EncodedResource的作用是对资源文件进行编码处理的

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

上面构造了一个有编码的InputStreamReader,构造完成后回到loadBeanDefinitions方法,该方法的内部才是真正的数据准备阶段。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException :

InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }

                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());

这里只取了部分的重要的代码,从传来的封装的resource参数中获取inputStream,通过SAX获取XML文件的方式准备InputSource对象,最后将准备的数据通过参数传入真正的核心处理部分doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            int validationMode = getValidationModeForResource(resource);
            Document doc = this.doLoadDocument(inputSource, resource);
            return this.registerBeanDefinitions(doc, resource);

上面的代码其实只完成了三件事情

  1. 获取对XML文件的验证
  2. 加载XML文件,并且得到对应的Document
  3. 根据返回的Document对象注册bean的信息

下面的章节会详细分析这三步的具体操作。

博客所有内容仅供自已学习和学习过程的记录,如有侵权,请联系我删除!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值