【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,准备工作做好没?那么让我们开始源码分析吧。