Spring系列博客:Spring特性(二)--Spring IOC原理以及IOC容器的实现

目录

一、什么是IOC/DI

        IOC容器:最主要是完成了完成对象的创建和依赖的管理注入等等。
        先从我们自己设计的一个视角来考虑:
        所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们可配置的文件。

对象和对象关系怎么表示?

可以用xml,properties文件等语义化配置文件表示。

描述对象关系的文件存放在哪里?

可能是classpath,filesystem,或者是URL网络资源,servletContext等。
        有了配置文件,还需要对配置文件解析。不同的配置围巾啊对对象的描述不一样,如标准的,自定义声明式的,如何统一?在内部需要有一个统一的关于对象的定义,所有外部的描述都必须转化成统一的描述定义。

如何对不同的配置文件进行解析?

需要对不同的配置文件语法,采用不同的解析器

二、Spring IOC体系结构

1、BeanFactory

        Spring Bean的创建时典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器实现供用户选择和使用,其相互关系如下:
Spring IOC体系结构
        其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。
        那为何要定义这么多层次的接口呢?查询这些接口的源码和说明发现,每个接口都有它使用的场合,它主要是为了区分在Spring内部在操作过程中这些对象的传递和转化过程中,对对象的数据访问所做的限制。
    例如,ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为。

最基本的IOC容器接口BeanFactory
public interface BeanFactory {    

//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,    
//如果需要得到工厂本身,需要转义           
String FACTORY_BEAN_PREFIX = "&"; 

//根据bean的名字,获取在IOC容器中得到bean实例    
Object getBean(String name) throws BeansException;    

//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。    
Object getBean(String name, Class requiredType) throws BeansException;    

//提供对bean的检索,看看是否在IOC容器有这个名字的bean    
boolean containsBean(String name);    

//根据bean名字得到bean实例,并同时判断这个bean是不是单例    
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    

//得到bean实例的Class类型    
Class getType(String name) throws NoSuchBeanDefinitionException;    

//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来    
String[] getAliases(String name); 

        在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是如何定义怎样加载的。正如我们只关心工厂里得到什么样的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。
        而要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现,Spring提供了许多IOC容器的实现。比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是针对最基本的ioc容器的实现,这个IOC容器可以读取XML文件定义的BeanDefinition(XML文件中对bean的描述)。

2、BeanDefinition

        SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承体系如下:
BeanDefinition体系结构
        Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:
BeanDefinition

三、IOC容器的初始化

        简单来说,IOC容器的初始化包括BeanDefinition的Resouce定位、载入和注册这三个基本的过程。我觉得重点是在载入和对BeanDefinition做解析的这个过程。可以从DefaultListableBeanFactory来入手看看IoC容器是怎样完成BeanDefinition载入的。在refresh调用完成以后,可以看到loadDefinition的调用:

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {  
    public AbstractXmlApplicationContext() {  
    }  
    public AbstractXmlApplicationContext(ApplicationContext parent) {  
        super(parent);  
    }  
    //这里是实现loadBeanDefinitions的地方  
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
        // 创建 XmlBeanDefinitionReader,并通过回调设置到 BeanFactory中去,创建BeanFactory的使用的也是 DefaultListableBeanFactory。  
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  

        // Configure the bean definition reader with this context's  
        // resource loading environment.  
        // 这里设置 XmlBeanDefinitionReader, 为XmlBeanDefinitionReader 配置ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用  
        beanDefinitionReader.setResourceLoader(this);  
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  

        // Allow a subclass to provide custom initialization of the reader,  
        // then proceed with actually loading the bean definitions.  
    // 这是启动Bean定义信息载入的过程  
        initBeanDefinitionReader(beanDefinitionReader);  
        loadBeanDefinitions(beanDefinitionReader);  
    }  

    protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {  
    }

这里使用XmlBeanDefinitionReader来载入BeanDefinition到容器中,如下代码所示:

//这里是调用的入口。  
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  
        return loadBeanDefinitions(new EncodedResource(resource));  
    }  
    //这里是载入XML形式的BeanDefinition的地方。  
    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.getResource());  
        }  

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
        if (currentResources == null) {  
            currentResources = new HashSet<EncodedResource>(4);  
            this.resourcesCurrentlyBeingLoaded.set(currentResources);  
        }  
        if (!currentResources.add(encodedResource)) {  
            throw new BeanDefinitionStoreException(  
                    "Detected recursive loading of " + encodedResource + " - check your import definitions!");  
        }  
        //这里得到XML文件,并得到IO的InputSource准备进行读取。  
        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.set(null);  
            }  
        }  
    }  
//具体的读取过程可以在doLoadBeanDefinitions方法中找到:  
    //这是从特定的XML文件中实际载入BeanDefinition的地方  
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
            throws BeanDefinitionStoreException {  
        try {  
            int validationMode = getValidationModeForResource(resource);  
            //这里取得XML文件的Document对象,这个解析过程是由 documentLoader完成的,这个documentLoader是DefaultDocumentLoader,在定义documentLoader的地方创建  
            Document doc = this.documentLoader.loadDocument(  
                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
            //这里启动的是对BeanDefinition解析的详细过程,这个解析会使用到Spring的Bean配置规则,是我们下面需要详细关注的地方。  
            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);  
        }  
    } 
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;  
    } 
public DelegatingEntityResolver(ClassLoader classLoader) {  
        this.dtdResolver = new BeansDtdResolver();  
        this.schemaResolver = new PluggableSchemaResolver(classLoader);  
    } 
public PluggableSchemaResolver(ClassLoader classLoader) {  
        this.classLoader = classLoader;  
        this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; // "META-INF/spring.schemas"  
    } 

        关于具体的Spring BeanDefinition的解析,是在BeanDefinitionParserDelegate中完成的。这个类里包含了各种Spring Bean定义规则的处理,感兴趣的同学可以仔细研究。
        我们举一个例子来分析这个处理过程,比如我们最熟悉的对Bean元素的处理是怎样完成的,也就是我们在XML定义文件中出现的这个最常见的元素信息是怎样被处理的。在这里,我们会看到那些熟悉的BeanDefinition定义的处理,比如id、name、aliase等属性元素。把这些元素的值从XML文件相应的元素的属性中读取出来以后,会被设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较为复杂的解析过程,这个过程是由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到BeanDefinition对象中并设置到BeanDefinitionHolder中去。

猜你喜欢

转载自blog.csdn.net/liuziteng0228/article/details/80303658
今日推荐