zoukankan      html  css  js  c++  java
  • 啃啃老菜:Spring IOC核心源码学习(一)

    啃啃老菜:Spring IOC核心源码学习(一)

    本文主要以spring ioc容器基本代码骨架为切入点,理解ioc容器的基本代码组件结构,各代码组件细节剖析将放在后面的学习文章里。

    关于IOC容器

    IoC容器:最主要是完成了完成对象的创建和依赖的管理注入等等。

    先从我们自己设计这样一个视角来考虑:

    所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们可配置的文件。

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

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

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

    可能是classpath,filesystem,或者是URL网络资源,servletContext等。

    回到正题,有了配置文件,还需要对配置文件解析。

    不同的配置文件对对象的描述不一样,如标准的,自定义声明式的,如何统一? 在内部需要有一个统一的关于对象的定义,所有外部的描述都必须转化成统一的描述定义。

    如何对不同的配置文件进行解析?需要对不同的配置文件语法,采用不同的解析器。

    基于以上问题,对应过来,刚好是 spring ioc 容器抽象的的几个主要接口:

    Resource

    BeanDefinition

    BeanDefinitionReader

    BeanFactory

    ApplicationContext

    以上五个都是接口,都有各式各样的实现,正是这5个接口定义了spring ioc容器的基本代码组件结构。而其组件各种实现的组合关系组成了一个运行时的具体容器。

    各代码组件详解

    1.Resource

    是对资源的抽象,每一个接口实现类都代表了一种资源类型,如ClasspathResource、URLResource,FileSystemResource等。每一个资源类型都封装了对某一种特定资源的访问策略。它是spring资源访问策略的一个基础实现,应用在很多场景。

     

    具体可以参考文章:

    Spring 资源访问剖析和策略模式应用

    http://www.ibm.com/developerworks/cn/java/j-lo-spring-resource/index.html

    2.BeanDefinition

    用来抽象和描述一个具体bean对象。是描述一个bean对象的基本数据结构。

    3.BeanDefinitionReader

    BeanDefinitionReader将外部资源对象描述的bean定义统一转化为统一的内部数据结构BeanDefinition。对应不同的描述需要有不同的Reader。如XmlBeanDefinitionReader用来读取xml描述配置的bean对象。



     

    4.BeanFactory

    用来定义一个很纯粹的bean容器。它是一个bean容器的必备结构。同时和外部应用环境等隔离。BeanDefinition是它的基本数据结构。它维护一个BeanDefinitions Map,并可根据BeanDefinition的描述进行bean的创建和管理。



     

    5.ApplicationContext

    从名字来看叫应用上下文,是和应用环境息息相关的。没错这个就是我们平时开发中经常直接使用打交道的一个类,应用上下文,或者也叫做spring容器。其实它的基本实现是会持有一个BeanFactory对象,并基于此提供一些包装和功能扩展。为什么要这么做呢?因为BeanFactory实现了一个容器基本结构和功能,但是与外部环境隔离。那么读取配置文件,并将配置文件解析成BeanDefinition,然后注册到BeanFactory的这一个过程的封装自然就需要ApplicationContext。ApplicationContext和应用环境细细相关,常见实现有ClasspathXmlApplicationContext,FileSystemXmlApplicationContext,WebApplicationContext等。Classpath、xml、FileSystem、Web等词都代表了应用和环境相关的一些意思,从字面上不难理解各自代表的含义。

    当然ApplicationContext和BeanFactory的区别远不止于此,有:

    1.  资源访问功能:在Resource和ResourceLoader的基础上可以灵活的访问不同的资源。

    2.  支持不同的信息源。

    3.  支持应用事件:继承了接口ApplicationEventPublisher,这样在上下文中为bean之间提供了事件机制。

    ……



     

    以上5个组件基本代表了ioc容器的一个最基本组成,而组件的组合是放在ApplicationContext的实现这一层来完成。

    以ClasspathXmlApplicationContext 容器实现为例,其组合关系如下:


    ClassPathXmlApplicationContext的refresh() 方法负责完成了整个容器的初始化。

    为什么叫refresh?也就是说其实是刷新的意思,该IOC容器里面维护了一个单例的BeanFactory,如果bean的配置有修改,也可以直接调用refresh方法,它将销毁之前的BeanFactory,重新创建一个BeanFactory。所以叫refresh也是能理解的。

    以下是Refresh的基本步骤:
    1.把配置xml文件转换成resource。resource的转换是先通过ResourcePatternResolver来解析可识别格式的配置文件的路径
    (如"classpath*:"等),如果没有指定格式,默认会按照类路径的资源来处理。 
    2.利用XmlBeanDefinitionReader完成对xml的解析,将xml Resource里定义的bean对象转换成统一的BeanDefinition。
    3.将BeanDefinition注册到BeanFactory,完成对BeanFactory的初始化。BeanFactory里将会维护一个BeanDefinition的Map。

    当getBean的时候就会根据调用BeanFactory,根据bean的BeanDifinition来实例化一个bean。当然根据bean的lazy-init、protetype等属性设置不同以上过程略有差别。

    refresh()代码如下:

    Java代码 
    1. public void refresh() throws BeansException, IllegalStateException {  
    2.     synchronized (this.startupShutdownMonitor) {  
    3.         // Prepare this context for refreshing.  
    4.         prepareRefresh();  
    5.   
    6.         // Tell the subclass to refresh the internal bean factory.  
    7.         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
    8.   
    9.         // Prepare the bean factory for use in this context.  
    10.         prepareBeanFactory(beanFactory);  
    11.   
    12.         try {  
    13.             // Allows post-processing of the bean factory in context subclasses.  
    14.             postProcessBeanFactory(beanFactory);  
    15.   
    16.             // Invoke factory processors registered as beans in the context.  
    17.             invokeBeanFactoryPostProcessors(beanFactory);  
    18.   
    19.             // Register bean processors that intercept bean creation.  
    20.             registerBeanPostProcessors(beanFactory);  
    21.   
    22.             // Initialize message source for this context.  
    23.             initMessageSource();  
    24.   
    25.             // Initialize event multicaster for this context.  
    26.             initApplicationEventMulticaster();  
    27.   
    28.             // Initialize other special beans in specific context subclasses.  
    29.             onRefresh();  
    30.   
    31.             // Check for listener beans and register them.  
    32.             registerListeners();  
    33.   
    34.             // Instantiate all remaining (non-lazy-init) singletons.  
    35.             finishBeanFactoryInitialization(beanFactory);  
    36.   
    37.             // Last step: publish corresponding event.  
    38.             finishRefresh();  
    39.         }  
    40.   
    41.         catch (BeansException ex) {  
    42.             // Destroy already created singletons to avoid dangling resources.  
    43.             beanFactory.destroySingletons();  
    44.   
    45.             // Reset 'active' flag.  
    46.             cancelRefresh(ex);  
    47.   
    48.             // Propagate exception to caller.  
    49.             throw ex;  
    50.         }  
    51.     }  
    52. }  

     以上的obtainFreshBeanFactory是很关键的一个方法,里面会调用loadBeanDefinition方法,如下:

    Java代码 
    1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  
    2.     // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
    3.     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
    4.   
    5.     // Configure the bean definition reader with this context's  
    6.     // resource loading environment.  
    7.     beanDefinitionReader.setResourceLoader(this);  
    8.     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
    9.   
    10.     // Allow a subclass to provide custom initialization of the reader,  
    11.     // then proceed with actually loading the bean definitions.  
    12.     initBeanDefinitionReader(beanDefinitionReader);  
    13.     loadBeanDefinitions(beanDefinitionReader);  
    14. }  

     LoadBeanDifinition方法很关键,这里特定于整个IOC容器,实例化了一个XmlBeanDefinitionReader来解析Resource文件。关于Resource文件如何初始化和xml文件如何解析都在

    Java代码 
    1. loadBeanDefinitions(beanDefinitionReader);  

     里面的层层调用完成,这里不在累述。

    小结 

    Spring的扩展性是毋庸置疑的,学习spring的设计是一个很好的实践理论结合。主要个人觉得有几点:

    1.  框架顶层的设计有着很好的抽象,遵循面向接口编程的规范。Resource、BeanFactory、ApplicationContext都是非常好的接口抽象,非常明确的定义了该组件的一些功能。

    2.  利用组合模式。

    3.  个组件的实现里大量使用了模板方法模式,提升了同一组件代码的复用性。

    4.  各种设计保留了扩展的接口,很多基于spring的框架都可以很容易的介入实现了自己的一些扩展。

    5.  框架里采用里很多经典的设计模式,如代理、装饰、策略等等。

  • 相关阅读:
    前端开发规范:命名规范、html 规范、css 规范、js 规范
    node.js入门系列(一)--Node.js简介
    浅析前端开发中的 MVC/MVP/MVVM 模式
    网页加载进度条的实现
    JavaScript深入系列(一)--原型和原型链详解
    vue2.0项目实战(5)vuex快速入门
    既然写CSS很容易,那为什么大家还是把CSS写的那么烂呢?
    vue2.0项目实战(4)生命周期和钩子函数详解
    vue2.0项目实战(3)使用axios发送请求
    【css技能提升】完美的 Sticky Footer 布局
  • 原文地址:https://www.cnblogs.com/daichangya/p/12958807.html
Copyright © 2011-2022 走看看