【Spring IOC】【一】Spring IOC 容器源码解析文章导读

1  前言及准备工作

Spring老生常谈,JavaWeb程序员的必备,程序员不看源码不懂源码,路走不长,所以耐心看,边看操作。

本文是 Spring IOC源码分析系列文章的导读文章,将会着重介绍 Spring 的一些使用方法和特性,为后续的源码分析文章做铺垫。

另外需要特别说明一下,本系列的源码分析文章是基于Spring 5.2.12.RELEASE版本编写的。

(1)新建一个maven工程,引入Spring依赖,写测试类进行分析常识

(2)去git上拉取Spring的相应版本的代码

统一概念:

beanName 就是bean在Spring中存储的bean的名字 

BeanFactory创建bean的工厂

2  Spring简介

Spring其中最主要的就是:IOC和AOP。

Spring的版本发展

3  Spring几个特性

3.1  lookup-method

当注入的对象,需要每次都要得到一个新的对象的时候。

第一种方式是让 类实现 ApplicationContextAware 接口(实现 BeanFactoryAware 接口也是可以的),每次调用getCat 方法时,都会获取一个新的实例,返回给调用者。

public class DemoBean implements BeanFactoryAware {

    private BeanFactory beanFactory;

    public Cat getCat() {
        return beanFactory.getBean("cat", Cat.class);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

}

第二种方式就是这里的 lookup-method 了,Spring 会在运行时对 DemoBean进行增强,使其 getCat 可以每次都返回一个新的实例。

public class DemoBean {

    @Lookup
    public Cat getCat() {
        return null;
    }

}

 

3.2  depends-on

 当一个 bean 直接依赖另一个 bean,可以使用 <ref/> 标签进行配置。不过如某个 bean 并不直接依赖于 其他 bean,但又需要其他 bean 先实例化好,这个时候就需要使用 depends-on 特性了。

<bean id="demoBean" class="com.virtuous.demo.spring.DemoBean" depends-on="cat"/>

3.3  后置处理器BeanPostProcessor

BeanPostProcessor 是 bean 实例化时的后置处理器,包含两个方法,其源码如下:

BeanPostProcessor 是 Spring 框架的一个扩展点,通过实现 BeanPostProcessor 接口,我们就可插手 bean 实例化的过程。比如大家熟悉的 AOP 就是在 bean 实例后期间将切面逻辑织入 bean 实例中的,AOP 也正是通过 BeanPostProcessor 和 IOC 容器建立起了联系。这里我来演示一下 BeanPostProcessor 的使用方式,如下:

/**
 * @author: xjx
 * @description
 * 初始化前后打印信息
 */
public class LoggerBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before " + beanName + " Initialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After " + beanName + " Initialization");
        return bean;
    }
}
//配置
<bean class="com.virtuous.demo.spring.LoggerBeanPostProcessor"/>

效果如下:

3.3  各种Aware

Spring 中定义了一些列的 Aware 接口,比如这里的 BeanFactoryAware,ApplicationContextAware以及 BeanNameAware 和 BeanClassLoaderAware等。通过实现这些 Aware 接口,我们可以在运行时获取一些配置信息或者其他一些信息。比如实现 BeanNameAware 接口,我们可以获取 bean 的配置名称(beanName)。通过实现 BeanFactoryAware 接口,我们可以在运行时获取 BeanFactory 实例。

4  SpringIOC类、接口关系

4.1 BeanFactory为基准的体系图

发现IOC涉及的类和接口挺多的,看的有点乱,我得加一篇梳理一下接口体系,方便更好的理解。

BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean,BeanFactory的功能通过其他的接口得到不断扩展。下面对上图涉及到的其他接口分别进行说明。

  • BeanFactory作为一个主接口不继承任何接口,暂且称为一级接口
  • 有3个子接口继承了它,进行功能上的增强。这3个子接口称为二级接口
  • ConfigurableBeanFactory可以被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry
  • ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的所有接口,无所不包,称为四级接口,这4级接口是BeanFactory的基本接口体系。继续,下面是继承关系的2个抽象类和2个实现类
  • AbstractBeanFactory作为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能
  • AbstractAutowireCapableBeanFactory同样是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory
  • DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了最强大的四级接口ConfigurableListableBeanFactory,并实现了一个外来接口BeanDefinitionRegistry,它并非抽象类。
  • 最后是最强大的XmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使自己更强大。

4.2 体系主要类的内容说明

4.2.1 BeanFactory接口-IOC的根

定义一个IOC容器应该有的一些基础方法:

4.2.2 二级接口ListableBeanFactory-可列表化的增强BeanFactory

对BeanFactory基础接口功能的一个列表化增强,根据某个条件返回bean的集合,方法名称都很见名知义,就不解释了。

4.2.4 二级接口HierarchicalBeanFactory-层次化

BeanFactory之间可以有父子关系,分层的思想。子容器可以访问父容器中的Bean,但父容器不能访问子容器的Bean。在容器内,Bean的id必须是唯一的,但子容器可以拥有一个和父容器id相同的Bean。父子容器层级体系增强了sping容器架构的扩展性和灵活性,因为第三方可以通过编程的方式,为一个已经存在的容器添加一个或多个特殊用途的子容器,以提供一些额外的功能。

4.2.3 二级接口AutowireCapableBeanFactory-自动装配Bean

这个就有点牛逼了,负则创建Bean、还有初始化、销毁以及Bean前后处理器、自动装配。

4.2.4 三级接口ConfigurableBeanFactory-可配置的

这个接口的方法就多的狠了,不仅继承了HierarchicalBeanFactory 和 SingletonBeanRegistry,具有父子分层还有单例注册的功能。SingletonBeanRegistry里6个方法,都是关于单例Bean的一些基础方法。它自己就几十个方法。

4.2.4 四级接口ConfigurableListableBeanFactory-继续增强ConfigurableBeanFactory

对上边的ConfigurableBeanFactory的再增强,上边的已经够多了,这个继续增加了几个方法。

忽略自动装配的的方法、判断指定的Bean是否有资格作为自动装配的候选者的方法、根据指定bean名,返回注册的Bean定义的方法等。

4.2.5 BeanDefinitionRegistry-Bean定义信息管理

它本本身继承了AliasRegistry,基础别名的方法,自身又有一些管理和操作BeanDefinition的方法。

  

4.3 另一庞大体系ApplicationContext

如果说BeanFactory是Sping的心脏,那么ApplicationContext就是完整的身躯了。ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在Beanfactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置的方式实现。

ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件,我们了解一下ApplicationContext的类继承体系:

       ApplicationContext体系结构及与BeanFactory的关系,从上图中我们可以看出ApplicationContext继承了hierarchicalBeanFactory和ListableBeanFactory接口,在此基础上,还通过多个其他的接口扩展了BeanFactory的功能,这些接口包括:

  • ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件,关闭事件等。实现了ApplicationListener事件监听接口的Bean可以接收到容器事件,并对事件进行响应处理。在ApplicationContext抽象实现类AbstractApplicationContext中,我们可以发现存在一个ApplicationEventMulticaster,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听者。
  • MessageSource:为应用提供i18n国际化消息访问的功能;
  • ResourcePatternResolver:所有ApplicationContext实现类都实现了类似于PathMatchingResourcePatternResolver的功能,可以通过带前缀的Ant风格的资源文件路径装载sping的配置文件。
  • LifeCycle:该接口是Sping2.0加入的,该接口提供了start()和stop()两个方法,主要用于控制异步处理过程。在具体使用时,ApplicationContext及具体的Bean都必须同时该接口,ApplicationContext会将start/stop的信息传递给容器中所有实现了该接口的Bean,已达到管理和控制JMX,任务调度等目的。

  在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了。ApplicationContext的初始化和BeanFactory有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例化目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因此ApplicationContext的初始化时间会比BeanFactory稍长一些,不过稍后的调用则没有“第一次懒加载”问题。

  Spring3.0支持基于类注解的配置方式,主要功能来自于sping的名为JavaConfig子项目,目前JavaConfig已经升级为spring核心框架的一部分。

  和基于XML文件配置方式的优势在于,类注解的配置方式可以很容易地让开发者控制Bean的初始化过程,比基于XML的配置更加灵活。sping为基于注解类的配置提供了专门的ApplicationContext实现类:AnnotationConfigApplicationContext。

  WebApplicationContext类体系结构与BeanFactory和ApplicationContext的关系: WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContextde引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Sping应用上下文。sping专门为此提供一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,即可以从ServletContext中获取WebApplicationContext实例。

  由于Web应用比一般的应用拥有更多的特性,因此WebApplicationContext扩展了ApplicationContext。WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext实例即以此为键放置在ServletContext的属性列表中,因此我们可以直接通过以下话语从Web容器中获取WebApplicationContext:

WebApplicationContext wac=(WebApplicationContext)servletContext.getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

这正是我们前面提到的WebApplicationContextUtils工具类getWebApplicationContext(ServletContext sc)方法的内部实现方式。这样sping的web应用上下文和web容器的上下文就可以实现互访,二者实现了融合:

 ConfigurableWebApplicationContext扩展了WebApplicationContext,它允许通过配置的方式实例化WebaApplicationContext,它定义了几个重要的方法:

    • setServletContext(ServletContext servletContext):为sping设置web应用上下文,以便两者整合;
    • setConfigLocations(String[] configLocations):设置sping配置文件地址,一般情况下,配置文件地址是相对于web根目录的地址,如/WEB-INF/baobaotao-dao.xml,/WEB-INF/baobaotao-service.xml等。但用户也可以使用带资源类型前缀的地址,如classpath:com/baobaotao/beans.xml等。
      WebApplicationContext初始化
        WebApplicationContext的初始化方式和BeanFactory,ApplicationContext有所区别,因为WebApplicationContext需要Servlet或定义Web容器监听器(ServletContextListener),借助这两者中的任何一个,我们就可以完成启动sping web应用上下文的工作。
        sping分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器:
    • org.spingframework.web.context.ContextLoaderServlet;
    • org.spingframework.web.context.ContextLoaderListener。
        两者的内部都实现了启动WebApplicationContext实例的逻辑,我们只要根据Web容器的具体情况选择两者之一,并在web.xml中完成配置就可以了。

5  小结

了解了Spring,准备工作做好没?那么让我们开始源码分析吧。

posted @ 2023-02-13 16:15  酷酷-  阅读(110)  评论(0编辑  收藏  举报