本文从几个问题:Spring框架有哪几个核心组件?为什么需要这些组件?他们又是如何结合在一起构成Spring的骨骼架构?Spring的AOP特性是如何利用这些基础的骨骼架构老工作的?来探讨Spring框架的设计理念。从而让我们后面学习Spring的应用更容易一些。
在Spring框架中,核心组件有三个:Core、Context和Bean。他们构建起了整个Spring的骨骼架构,没有他们就不可能有AOP、Web等上层的特性功能。
在上面提到的三个核心组件中,Bean是核心中的核心。因为Spring就是面向Bean的编程,Bean在Spring中才是真正的主角。
Spring之所以如此流行,是因为它解决了一个问题,它可以让你把对象之间的依赖关系转而用配置文件或注解来管理,也就是它的依赖注入机制。而这个注入关系在一个叫Ioc的容器中管理。Ioc容器中就是被Bean包裹的对象。Spring正是通过把对象包裹在Bean中从而达到管理这些对象及做一系列额外操作的目的。
它的设计理念就是构建一个数据结构,然后根据这个数据结构设计它的生存环境,并让它在这个环境中按照一定的规律不停地运动,在它们的不停运动中设计一个系列与环境或者与其他个体完成信息交换。
那么在Spring的骨骼架构中,这三个组件又是怎么工作的呢?它们分别扮演什么样的角色?
举个栗子,如果把Bean比作一场演出中的演员,那么Context就是这场演出的舞台背景,而Core就是演出的道具了。它们三个放在一切就具备了演一场好戏的最基本的条件。而要想演出更加精彩,还需要它表演的节目足够精彩,这些节目就是Spring能够提供的特色功能了。
Context要做的就是发现每个Bean之间的关系,为它们建立这种关系并且维护好这种关系。所以,Context就是一个Bean关系的集合,又叫做Ioc容器,一旦建立起这个Ioc容器,Spring就可以工作了。
而Core就是发现、建立和维护每个Bean之间的关系所需要的一系列工具。相当于一个Util,为Context所用。
Bean组件在Spring的org.springframework.beans包下,这个包下的类主要解决了3个问题:Bean的定义、Bean的创建和Bean的解析。对于使用者来说,唯一需要关心的就是Bean的创建,其他两个由Spring在内部帮你完成。
Bean的定义:
Bean的定义主要由BeanDefinition描述。
Bean的定义完整的描述了在Spring的配置文件中你定义的
节点后中所有的信息,包括各种子节点。当Spring成功解析你定义的一个
节点后,在Spring内部他就被转换成BeanDifinition对象,以后所有的操作都是对这个对象进行的。
Bean的创建:
Bean的创建时典型的工厂模式。它的顶级接口是BeanFactory。
其最终的实现类是DefaultListableBeanFactory。至于为什么要定义这么多层次的接口,主要是为了区分在Spring内部对象的传递和转化过程中,对对象的数据访问所做的限制。例如ListableBeanFactory接口表示这些Bean是列表的,而HierarchicalBeanFactory表示这些Bean是由继承关系的,也就是每个Bean有可能有父Bean,AutowriteCapableBeanFactory接口定义Bean的自动装配规则。
Bean的解析:
Bean的解析过程非常复杂,主要就是对Spring配置文件的解析,这个解析过程主要通过下面几个类完成。
Context在Spring的org.springframework.context包下,其作用前面已经说了,就是个ISpring提供一个运行时的环境,用于保存各个对象的状态。
Context的顶级父类是ApplicationContext。
ApplicationContext的子类主要包含两个方面:
总的来说ApplicationContext必须完成以下几件事情:
Context作为Spring的Ioc容器,基本上整合了Spring的大部分功能。
Core包含了很多关键类,一个重要的组成部分就是定义了资源的访问方式。
下面是Resource相关的类结构图
下面是Context和Resource如何建立关系的类关系图。
Context把资源的加载、解析和描述工作委托给了ResourcePatternResolver类来完成,它相当于一个接头人,它把资源的加载、解析和资源的定义整合在一起便于其他组件使用。
Ioc容器实际上是Context组件结合其他两个组件共同构建了一个Bean关系网,构建这个关系网的入口就在AbstractApplicationContext类的refresh方法中,代码如下,这个方法就是构建整个Ioc容器过程的完整代码。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 为刷新准备新的context
prepareRefresh();
// 刷新所有BeanFactory子容器,当BeanFactory存在时就更新,不存在时就创建一个新的。BeanFactory的原始对象是DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为创建好的BeanFactory添加一些Spring本身需要的工具类
prepareBeanFactory(beanFactory);
try {
// 注册实现BeanPostProcessor接口的Bean
postProcessBeanFactory(beanFactory);
// 初始化和执行BeanFactoryPostProcessor beans
invokeBeanFactoryPostProcessors(beanFactory);
// 初始化和执行BeanPostProcessor beans
registerBeanPostProcessors(beanFactory);
// 初始化MessageSource.
initMessageSource();
// 初始化 event multicaster.
initApplicationEventMulticaster();
// 刷新由子类实现的方法.
onRefresh();
// 检查注册事件.
registerListeners();
// 初始化non-lazy-init单例Bean.
finishBeanFactoryInitialization(beanFactory);
// 执行LifecycleProcessor.onRefresh()和ContextRefreshedEvent事件.
finishRefresh();
}
catch (BeansException ex) {
// 销毁beans.
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
这三行代码对Spring的功能扩展性起了至关重要的作用。前两行主要是让你现在可以对已经构建的BeanFactory的配置做修改,后面一行就是让你可以对以后再创建Bean的实例对象是添加一些自定义的操作。所以它们都扩展了Spring的功能。
其中 invokeBeanFactoryPostProcessors方法中主要是获取实现BeanFactoryPostProcessor接口的子类,并执行它的PostProcessBeanFactory方法,方法声明如下void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
registerBeanPostProcessors方法可以获取用户定义的实现了BeanPostProcessor接口的子类,并把它们注册到BeanFactory对象中的beanPostProcessors变量中。BeanPostProcessor中声明了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,分别用于在Bean对象初始化时执行,可以执行用户自定义的操作。
后面的几行代码是初始化监听事件和对系统的其他监听者的注册,坚挺着必须是ApplicationListener的子类。
创建Bean并构建Bean的关系网
创建Bean是从finishBeanFactoryInitialization方法开始的。
protected void finishBeanFactoryInitialization(
ConfigurableListableBeanFactory beanFactory) {
// 不使用TempClassLoader.
beanFactory.setTempClassLoader(null);
// 禁止修改当前Bean的配置信息.
beanFactory.freezeConfiguration();
// 实例化non-lazy-init类型的bean.
beanFactory.preInstantiateSingletons();
}
从上面可以看出Bean的实例化是在BeanFactory中发生的。
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isInfoEnabled()) {
this.logger.info("Pre-instantiating singletons in " + this);
}
synchronized (this.beanDefinitionMap) {
for (String beanName : this.beanDefinitionNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton()
&& !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean factory =
(FactoryBean) getBean(FACTORY_BEAN_PREFIX+ beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null
&& factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
new PrivilegedAction() {
public Boolean run() {
return ((SmartFactoryBean) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = factory instanceof SmartFactoryBean
&& ((SmartFactoryBean) factory).isEagerInit();
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
}
}
注意这里的FactoryBean,这是一个非常重要的Bean,Spring有一大半的扩展功能都与这个Bean有关,这是一个特殊的Bean。它是以工厂Bean,可以产生Bean的实例的Bean。如果一个类继承FactoryBean,用户就可以自己定义生产实例对象的方法,而这一切,只需要实现它的getObject()方法即可。
如何创建Bean的实例对象及如何构建Bean实例对象之间的关联关系是Spring中的一个核心。下面是这个过程的流程图
如果是普通的Bean就通过调用getBean方法直接创建它的实例。
Ioc容器的扩展点
对Spring的Ioc容器来说,主要有BeanFactoryPostProcessor和BeanPostProcessor,他们分别在构建BeanFactory和构建Bean对象是调用。还有就是InitializingBean和DisposableBean,它们分别在Bean实例创建和销毁时被调用。还有一个是FactoryBean,它是一个特殊的Bean,这个Bean可以被用户更多的控制。
下面这个比喻可以让我们更好的理解他们之间的关系:
把 Ioc 容器比作一个箱子,这个箱子里有若干个球的模子,可以用这些模子来造很多种不同的球,还有一个造这些球模的机器,这个机器可以产生球模。那么他们的对应关系就是 BeanFactory 就是那个造球模的机器,球模就是 Bean,而球模造出来的球就是 Bean 的实例。那前面所说的几个扩展点又在什么地方呢? BeanFactoryPostProcessor 对应到当造球模被造出来时,你将有机会可以对其做出设当的修正,也就是他可以帮你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的开始和结束阶段,你可以完成一些预备和扫尾工作。BeanPostProcessor 就可以让你对球模造出来的球做出适当的修正。最后还有一个 FactoryBean,它可是一个神奇的球模。这个球模不是预先就定型了,而是由你来给他确定它的形状,既然你可以确定这个球模型的形状,当然他造出来的球肯定就是你想要的球了,这样在这个箱子里尼可以发现所有你想要的球。
我们使用Spring必须先构建Ioc容器,没有他Spring无法工作,ApplicationContext.xml就是Ioc容器的默认配置文件,Spring的所有特性都是基于Ioc容器工作的,比如后面介绍的AOP。
Ioc实际上为你构建了一个魔方,Spring为你搭好了骨骼架构,这个魔方到底能变出什么好东西,这必须要有你的参与——通过实现扩展点来改变Spring的通用行为。AOP的实现就是Spring本身实现了其扩展点达到了它想要的特性功能。
Spring的AOP是基于动态代理实现的。在JDK的java.lang.reflect包下有一个Proxy类,它正是构建代理类的入口。调用其newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
方法就可以创建代理对象。
ClassLoader,用于加载代理类的Loader类,通常这个Loader和被代理的类是同一个Loader类;Interfaces,是要被代理的那些接口;InvocationHandler用于执行除了被代理接口中方法之外的用户自定义的操作,他也是用户需要dialing的最终目的。用户调用目标方法都被代理到在InvocationHandler 类中定义的唯一方法invoke()中。
代理的目的是调用目标方法时可以转而执行InvocationHandler 类的invoke方法。
要实现代理类,在Spring的配置文件中通常是这样定义一个Bean的:
<bean id="testBeanSingleton"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>
org.springframework.aop.framework.PrototypeTargetTests$TestBean
value>
property>
<property name="target"><ref local="testBeanTarget">ref> property>
<property name="singleton"><value>truevalue>property>
<property name="interceptorNames">
<list>
<value>testInterceptorvalue>
<value>testInterceptor2value>
list>
property>
bean>
可以看到,要设置被代理的接口和接口的实现类,以及拦截器(在执行目标方法之前被调用)。
从这个代理类可以看出它继承了FactoryBean的ProxyFactoryBean。FactoryBean之所以特别就是在于它可以让你自定义对象的创建方法。当然这里的代理对象要通过Proxy类来生成。
在Spring创建了代理对象之后,当你调用目标对象上的方法时,都会被代理到InvocationHandler 类的invoke方法中执行,这在前面已经解释过了。
由于版本更新的问题,文章中某些地方可能和现在的版本不一样,但总体的框架是不会变太多的,本文的用意也是通过对Spring框架的底层实现有一个总体的把握,从而让我们对后面Spring的学习可以有一个清晰的脉络
Spring框架实现了servlet的一个监听器——ServletContextListener(Servlet容器初始化时触发,在所有的Filter和Servlet的init方法调用之前contextInitalized接口先被调用)。Spring对应的实现类是org.springframework.web.context.ContextLoaderListener(因为是监听器,所以需要在web.xml中的
标签中定义)。当Servlet容器加载时启动Spring容器。ContextLoaderListener在contextInitalized方法中初始化Spring容器,有几种方法可以加载Spring容器,通过在web.xml的
标签中哦诶之Spring的applicationContext.xml路径,文件名可以任意取,如果没有配置,将在/WEB-INF/路径下查找默认的applicationContext.xml文件。
本文为读书笔记,主要参考自许令波的《深入分析Java Web技术内幕》一书中的“Spring框架的设计理念与设计模式分析”一节。大家有兴趣的可以拜读一下。此篇文章,作者在gitHub上也有分享,地址是:Spring 框架的设计理念与设计模式分析