Spring源码学习-容器基本实现


可能会有不正确的地方,希望能帮忙指出

该工程的主要功能有:
访问配置文件、创建和管理bean以及IoC/DI、Aop操作相关的所有类

1.容器的基础 XmlBeanFactory

1.1访问配置文件

DefaultListableBeanFactory
XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载bean的默认实现

  • XmlBeanFactory

    • 使用了自定义的XML读取器 XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取
  • DefaultListableBeanFactory

    • 继承了AbstractAutowireCapableBeanFactory并实现了ConfigurableListableBeanFactory以及BeanDefinitionRegistry接口。

1.DefaultListableBeanFactory继承体系中的相关类
相关类的作用

  • AliasRegistry: 定义对alias的简单增删改等操作
  • SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口AliasRegistry进行实现
  • SingletonBeanRegistry:定义对单例的注册及获取
  • BeanFactory:定义获取bean及bean的各种属性
  • DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry各函数的实现
  • HierarchicalBeanFactory:继承BeanFactory,在其功能的基础上增加了对parentFactory的支持
  • BeanDefinitionRegistry:定义对BeanDefinition的各种增删改操作
  • FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上增加了对FactoryBean的特殊处理
  • ConfigurableBeanFactory:提供配置Factory的各种方法
  • ListableBeanFactory:根据各种条件获取bean的配置清单
  • AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能
  • AutowireCapableBeanFactory:提供创建bean、自动注入、初始化以及应用bean的后处理器
  • AbstractAutowireCapableBeanFactory:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现
  • ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等
  • DefaultListableBeanFactory:综合上面所有功能,主要对Bean注册后的处理。

2.XmlBeanDefinitionReader继承体系中相关类
相关类的作用

  • ResourceLoader:定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource
  • BeanDefinitionReader:主要定义资源文件读取并转换为BeanDefinition的各个功能
  • EnvironmentCapable:定义获取Environment方法
  • DocumentLoader:定义从资源文件加载到转换为Document的功能
  • AbstractBeanDefinitionReader:对EnvironmentCapable、BeanDefinitionReader类定义的功能进行实现
  • BeanDefinitionDocumentReader:定义读取Document并注册BeanDefinition功能
  • BeanDefinitionParserDelegate:定义解析Element的各种方法。

XML配置文件读取流程
1)通过继承自AbstractBeanDefinitionReader中的方法,来使用ResourceLoader将资源文件路径转换为对应的Resource文件
2)通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Document文件
3)通过实现接口 BeanDefinitionDocumentReader 的 DefaultBeanDefinitionDocumentReader 类对 Document 进行解析,并使用 BeanDefinitionParserDelegate对Element 进行解析

InputStreamSource接口 ,封装任何能返回InputStream的类。只有一个方法 : getInputStream() ,该方法返回一个新的InputStream对象

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

Resource接口继承了InputStreamSource接口,并抽象了所有Spring内部使用到的底层资源:File、URL、Classpath等。

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; // 获取文件的lastModified属性
	Resource createRelative(String relativePath) throws IOException; // 创建相对资源
	String getFilename(); 
	String getDescription(); // 获取资源详细信息
}

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

  • 文件(FileSystemResource)
  • Classpath资源(ClassPathResource)
  • URL资源(UrlResource)
  • InputStream资源(InputStreamResource)
  • Byte数组(ByteArrayResource)

例子:
BeanFactory bf = new XmlBeanFactory( new ClassPathResource(“xxx.xml”) )

ClassPathResource的实现方式是通过class或者classLoader提供的底层方法进行调用

ClassPathResource:

 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(this.getDescription() + " cannot be opened because it does not exist");
        } else {
            return is;
        }
    }

XmlBeanFactory:

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

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    // 调用父类的构造方法,用于忽略 BeanNameAware、BeanFactoryAware、BeanClassLoaderAware 这三个类的初始化 
    // 在父类 AbstractAutowireCapableBeanFactory类的构造方法中的
    // ignoreDependencyInterface(BeanNameAware.class);
    // ignoreDependencyInterface(BeanFactoryAware.class);
    // ignoreDependencyInterface(BeanClassLoaderAware.class);
        super(parentBeanFactory); 
        this.reader = new XmlBeanDefinitionReader(this);
        this.reader.loadBeanDefinitions(resource); // 资源加载真正实现的地方
    }

1.2加载Bean

this.reader.loadBeanDefinitions(resource)时序图
在这里插入图片描述
大致流程:
1)封装资源文件。 对参数Resource使用EncodeResource类进行封装
2)获取输入流。从Resource中获取对应的InputStream并构造InputSource
3)通过构造的InputSource实例和Resource实例,继续调用函数doLoadBeanDefinitions。

相关代码
XmlBeanDefinitionReader

 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Loading XML bean definitions from " + encodedResource);
        }
		// 通过属性来记录已经加载的资源
        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if (!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
            // 从encodedResource中获取已经封装的Resource对象,并再次从Resource中获取其中的inputStream
                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());
                } finally {
                    inputStream.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if (((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }

2.获取XML的验证模式

XML文件的验证模式,是为了保证XML文件的正确性,比较常用的验证模式有两种:DTD跟XSD

2.1DTD

DTD(Document Type Definition) 文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。能保证XML文档格式正确有的有效方法。它是通过比较XML文档和DYD文件来看文档是否符合规范,元素和标签使用是否正确。
DTD文档包含: 元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。
使用方法:在XML文件的头部声明即可。

2.2XSD

XSD(XML Schemas definition) 描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,检查该文档是否符合要求。XML Schema本身是一个XML文档,它符合XML语法结构。
使用方法:

  • 1.声明名称空间
  • 2.指定控件所对应的XML Schema文档的存储位置 (一部分是名称空间的URI,另一部分是该名称控件所标识的位置或者URL地址)。

3.获取Document

明面上是XmlBeanFactoryReader类去读取文档,但其实是委托给了DocumentLoader去执行。
DocumentLoader的实现类是DefaultDocumentLoader。
解析步骤是:
1.XmlBeanFactory的构造方法 创建了XmlBeanDefinitionReader,并调用Reader的loadBeanDefinitions()方法加载Resource资源
2.XmlBeanDefinitionReader将Resource用EncodedResource封装一遍,进行一系列判断后,调用
doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法
3.通过doLoadDocument()方法调用真正使用的DefaultDocumentLoader的loadDocument()方法,其代码如下

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

        DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
    }

这是使用SAX解析XML文档 ,步骤是
1.创建DocumentBuilderFactory
2.通过DocumentBuilderFactory创建DocumentBuilder,解析inputSource
创建DocumentBuilder时,有个参数entityResolver需要特别说明一下

3.1EntityResolver

何为EntityResolver?
官网解释: 如果SAX应用程序需要实现自定义处理外部实体,则必须实现次接口并使用setEntityResolver方法向SAX驱动器注册一个实例。
对于解析一个XML,SAX首先读取该XML文档上的声明,根据声明去寻找对应的DTD定义,以便对文档进行一个验证。
EntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程。 比如将DTD文件放到项目中,在实现时,直接将此文档读取并返回给SAX即可。避免了通过网络来寻找。
EntityResolver

public interface EntityResolver {
    InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException;
}

1)如果解析验证模式为XSD的配置文件。

<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
   		http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
  • publicId: null
  • systemId: http://www.Springframework.org/schema/beans/Spring-beans.xsd

2)如果解析验证模式为DTD的配置文件

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" " http://www.springframework.org/dtd/spring-beans.dtd ">  
  • publicId: -//SPRING//DTD BEAN//EN
  • systemId: http://www.springframework.org/dtd/spring-beans.dtd

EntityResolver的实现类是DelegatingEntityResolver,所以可以看该实现类的resolveEntity方法

public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException {
        if (systemId != null) {
            if (systemId.endsWith(".dtd")) {
                return this.dtdResolver.resolveEntity(publicId, systemId);
            }
            if (systemId.endsWith(".xsd")) {
                return this.schemaResolver.resolveEntity(publicId, systemId);
            }
        }
        return null;
    }

可以看到,对于不同的验证模式,Spring用了不同的解析器
dtd类型的。是直接截取systemId最后的xx.dtd 然后在当前路径下寻找
xsd类型的,是默认到META-INF/Spring.schemas文件总找到systemId对应的XSD文件并加载

4.解析及注册BeanDefinitions

回到XmlBeanDefinitionReader的doLoadBeanDefinitions()方法,当获取到Document对象后,接下来就是将document对象 传入registerBeanDefinitions()这个方法中了。

 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		 // 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        // 在实例化BeanDefinitionReader时候,会将BeanDefinitionRegistry传入,默认使用继承自DefaultListableBeanFactory的子类
        // 记录统计前BeanDefinition的加载个数
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        // 加载及注册bean
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        // 记录本次加载的BeanDefinition个数
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }

这段代码重要的就是实例化后调用的registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 这个方法 — 提取root,再次将root作为参数继续BeanDefinition的注册

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.doRegisterBeanDefinitions(doc.getDocumentElement()); // 参数就是root
    }

此时代码中的doRegisterBeanDefinitions(Element root) 方法,就是真正进行解析Xml的方法

protected void doRegisterBeanDefinitions(Element root) {
		// 专门处理解析
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
        	// 处理profile属性 (使用哪个环境的配置文件)
            String profileSpec = root.getAttribute("profile");
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
		//  解析前处理,留给子类实现
        this.preProcessXml(root);
        this.parseBeanDefinitions(root, this.delegate);
        // 解析后处理,留给子类实现
        this.postProcessXml(root);
        this.delegate = parent;
    }

该方法中,使用了模板方法模式, 主要是为了后期如果需要在Bean解析前后做处理的话,重写这两个方法即可

4.1 profile属性的使用

这个属性,主要是用来 同时在配置文件中,部署多套配置,来使用于不同的环境,这样可以方便的进行切换、部署环境 , 最常用的就是更换不同的数据库。

知道这点后,在看上面的代码,首先程序会获取beans节点是否定义了prodile属性。如果定义了,则会需要到环境变量中去寻找。

4.2 解析并注册BeanDefinition

根据代码进入parseBeanDefinition(Element root, BeanDefinitionParserDelegate delegate)方法

 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)) {
                    	// 解析默认配置
                        this.parseDefaultElement(ele, delegate);
                    } else {
                    	// 解析自定义配置
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }
    }

XML配置的两大类Bean声明
默认的: <bean id="test" class="test.TestBean" />
自定义:<tx:annotation-driven/>

如果采用Spring默认的配置,则采用parseDefaultElement()方法进行解析
如果采用自定义方式的配置,则采用delegate.parseCustomElement()方法进行解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值