Spring框架的设计理念
它这种设计策略完全类似于Java实现OOP的设计理念,当然Java本身的设计要比Spring复杂太多太多,但是它们都是构建一个数据结构,然后根据这个数据结构设计它的生存环境,并让它在这个环境中按照一定的规律不停地运动,在它们的不停运动中设计一系列与环境或者与其他个体完成信息交换。这样想来我们用到的其他框架都是大概类似的设计理念。
Spring总共有十几个组件,但是真正核心的组件只有几个。从图中可以看出,Spring框架中的核心组件只有三个:Core、Context和Bean。它们构建起了整个Spring的骨骼架构,没有它们就不可能有AOP、Web等上层的特性功能。如果要在它们三个中选出核心,那就非Bean组件莫属了。其实Spring就是面向Bean的编程(BOP,Bean Oriented Programming),Bean在Spring中才是真正的主角。
核心组件介绍
可以把Bean比做一场演出中的演员,Context就是这场演出的舞台背景,而Core应该就是演出的道具了。只有它们在一起才能具备能演出一场好戏的最基本的条件。当然有最基本的条件还不能使这场演出脱颖而出,还要它表演的节目足够精彩,这些节目就是Spring能提供的特色功能了。
Spring解决了一个非常关键的问题,它可以让你把对象之间的依赖关系转而用配置文件来管理,也就是它的依赖注入机制。而这个注入关系在一个叫Ioc的容器中管理,Bean包装的是Object,而Object必然有数据,如何给这些数据提供生存环境就是Context要解决的问题,对Context来说它就是要发现每个Bean之间的关系,为它们建立这种关系并且维护好这种关系。所以Context就是一个Bean关系的集合,这个关系集合就叫Ioc容器。一旦建立起这个Ioc容器Spring就可以为你工作了。其实Core就是发现、建立和维护每个Bean之间的关系所需要的一系列工具,从这个角度来看,Core组件叫Util更能让你理解。
Bean组件
Bean组件在Spring的org.springframework.beans包下。这个包下的所有类主要解决了三件事:Bean的定义、Bean的创建及对Bean的解析。对Spring的使用者来说唯一需要关心的就是Bean的创建,其他两个由Spring在内部帮你完成了,对你来说是透明的。
Spring Bean的创建是典型的工厂模式,它的顶级接口是BeanFactory。
BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是从图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。查阅这些接口的源码和说明可以发现每个接口都有它使用的场合,它主要是为了区分在Spring内部对象的传递和转化过程中,对对象的数据访问所做的限制。例如,ListableBeanFactory接口表示这些Bean是可列表的,而HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean,AutowireCapableBeanFactory接口定义Bean的自动装配规则。这四个接口共同定义了Bean的集合、Bean之间的关系和Bean的行为。
Bean的定义主要由BeanDefinition描述。
Bean的定义完整地描述了在Spring的配置文件中你定义的节点中所有的信息,包括各种子节点。当Spring成功解析你定义的一个节点后,在Spring的内部它就被转化成BeanDefinition对象,以后所有的操作都是对这个对象进行的。Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。
Context组件
Context在Spring的org.springframework.context包下,它实际上就是给Spring提供一个运行时的环境,用以保存各个对象的状态。ApplicationContext是Context的顶级父类,它除了能标识一个应用环境的基本信息外,还继承了5个接口,这5个接口主要是扩展了Context的功能。可以看出 ApplicationContext 继承了 BeanFactory,这也说明了 Spring 容器中运行的主体对象是 Bean,另外 ApplicationContext 继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源。
ApplicationContext 的子类主要包含两个方面:
- ConfigurableApplicationContext 表示该 Context 是可修改的,也就是在构建 Context 中用户可以动态添加或修改已有的配置信息,它下面又有多个子类,其中最经常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 类。
- WebApplicationContext 顾名思义,就是为 web 准备的 Context 他可以直接访问到 ServletContext,通常情况下,这个接口使用的少。
再往下分就是按照构建 Context 的文件类型,接着就是访问 Context 的方式。这样一级一级构成了完整的 Context 等级层次。
总体来说 ApplicationContext 必须要完成以下几件事:
- 标识一个应用环境
- 利用 BeanFactory 创建 Bean 对象
- 保存对象关系表
- 能够捕获各种事件
Context 作为 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者说是大部分功能的基础。
Core 组件
Core组件作为Spring的核心组件,其中包含了很多关键类,一个重要的组成部分就是定义了资源的访问方式。
可以看出Resource接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。对资源的提供者来说,如何把资源包装起来交给其他人用这也是一个问题,我们看到Resource接口继承了InputStreamSource接口,这个接口中有个getInputStream方法,返回的是InputStream类。这样所有的资源都可以通过InputStream类来获取,所以也屏蔽了资源的提供者。另外还有一个问题就是加载资源的问题,也就是资源的加载者要统一,从上图中可以看出这个任务是由ResourceLoader接口完成的,它屏蔽了所有的资源加载者的差异,只需要实现这个接口就可以加载所有的资源,它的默认实现是DefaultResourceLoader。
Context和Resource是如何建立关系的?Context把资源的加载、解析和描述工作委托给了ResourcePatternResolver类来完成,它相当于一个接头人,它把资源的加载、解析和资源的定义整合在一起便于其他组件使用。Core组件中还有很多类似的方式。
Ioc容器如何工作
IOC作为一个容器,它里面放得都是bean,bean与bean之间的对应关系,而bean之间的对应关系我们开始都是通过xml配置文件来体现的。
那么这里就反馈了如下几个问题:
1、对应与对象之间的关系是通过xml配置文件来描述的(当然也可以是properties等文件)。
2、描述的文件存放位置在那里,一般来说我们都是放在classpath目录下的,但是也可以是URL、fileSystem。
3、文件的解析。
4、Bean在容器中的表现形式,也就是它的数据结构。
对于Spring而言,它用Resource、BeanDefinition、BeanDefinitionReader、BeanFactory、ApplicationContext五个组件来实现以上问题,而同时这5个接口定义了 spring ioc 容器的基本代码组件结构。
下面我们简单了解这五个结构
Resource
Resource,对资源的抽象,它的每一个实现类都代表了一种资源的访问策略,如ClasspathResource 、 URLResource ,FileSystemResource 等。
BeanDefinition
用来描述和抽象一个具体的Bean对象,它是描述Bean对象的基本数据结构。配置文件中的每一个bean对应一个BeanDefinition实例。
BeanDefinitionReader
外部资源所表达的语义需要转化为内部数据结构BeanDefinition,BeanDefinitionReader起到解析的作用。对应不同的描述需要有不同的Reader 。如 XmlBeanDefinitionReader 用来读取xml 描述配置的 bean 对象。
BeanFactory
BeanFactory是一个纯粹的bean容器,它是IOC必备的数据结构,其中BeanDefinition是它的基本结构,它内部维护着一个BeanDefinition map,并可根据BeanDefinition 的描述进行 bean 的创建和管理。
ApplicationContext
这个就是大名鼎鼎的Spring容器,它叫做应用上下文,与我们应用息息相关,它继承BeanFactory,是BeanFactory的扩展升级版。由于ApplicationContext的结构就决定了它与BeanFactory的不同,其主要区别有:
- 继承MessageSource,提供国际化的标准访问策略。
- 继承ApplicationEventPublisher,提供强大的事件机制。
- 扩展ResourceLoader,可以用来加载多个Resource,可以灵活访问不同的资源。
- 对Web应用的支持。
如何创建BeanFactory工厂
Ioc容器实际上是Context组件结合其他两个组件共同构建了一个Bean关系网,如何构建这个关系网?构建的入口就在AbstractApplicationContext类的refresh方法中,这个方法的代码如下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //获取实现BeanFactoryPostProcessor接口的子类,并执行它的postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //获取用户定义的实现了BeanPostProcessor接口的子类,并把它们注册到BeanFactory对象中的beanPostProcessors变量中
registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //构建bean的关系网
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
这个方法就是构建整个Ioc容器过程的完整的代码,了解了里面的每一行代码基本上就了解大部分Spring的原理和功能。
这段代码主要包含这样几个步骤:
- 构建BeanFactory,以便于产生所需的“演员”。
- 注册可能感兴趣的事件。
- 创建Bean实例对象。
- 触发被监听的事件。
第二三句就是在创建和配置 BeanFactory。这里是 refresh 也就是刷新配置,前面介绍了 Context 有可更新的子类,这里正是实现这个功能,当 BeanFactory 已存在是就更新,如果没有就新创建。下面是更新 BeanFactory 的方法代码,这个方法实现了AbstractApplicationContext的抽象方法refreshBeanFactory,这段代码清楚地说明了BeanFactory的创建过程:
AbstractRefreshableApplicationContext.refreshBeanFactory protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory);
//加载、解析Bean的定义,也就是把用户定义的数据结构转化为Ioc容器中的特定数据结构 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException( "I/O error parsing bean definition source for " + getDisplayName(), ex); } }
注意BeanFactory对象的类型的变化,前面介绍了它有很多子类,在什么情况下使用子类非常关键。BeanFactory的原始对象是DefaultListableBeanFactory,这非常关键,因为它涉及后面对这个对象的多种操作,下面看一下这个类的继承关系图。
创建 BeanFactory 时序图
Bean 的解析和登记流程时序图如下:
创建好BeanFactory后,添加一些Spring本身需要的一些工具类,这个操作在AbstractApplicationContext的prepareBeanFactory方法中完成。
在invokeBeanFactoryPostProcessors方法中主要是获取实现BeanFactoryPostProcessor接口的子类,并执行它的postProcessBeanFactory方法,这个方法的声明如下:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
它的参数是beanFactory,说明可以对beanFactory做修改,这里注意,beanFactory是ConfigurableListableBeanFactory类型的,这也印证了前面介绍的不同BeanFactory所使用的场合不同,这里只能是可配置的BeanFactory,防止一些数据被用户随意修改。
在registerBeanPostProcessors方法中可以获取用户定义的实现了BeanPostProcessor接口的子类,并把它们注册到BeanFactory对象中的beanPostProcessors变量中。BeanPostProcessor中声明了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,分别用于在Bean对象初始化时执行,可以执行用户自定义的操作。
后面的几行代码是初始化监听事件和对系统的其他监听者的注册,监听者必须是ApplicationListener的子类。
如何创建Bean实例并构建Bean的关系网
下面就是Bean的实例化代码,是从finishBeanFactoryInitialization方法开始的。
AbstractApplicationContext.finishBeanFactoryInitialization protected void finishBeanFactoryInitialization( ConfigurableListableBeanFactory beanFactory) { // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
从上面的代码中可以发现Bean的实例化是在BeanFactory中发生的。PreInstantiateSingletons方法的代码如下:
DefaultListableBeanFactory.preInstantiateSingletons 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); //FACTORY_BEAN_PREFIX=& boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( new PrivilegedAction<Boolean>() { public Boolean run() { return ((SmartFactoryBean) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit(); } if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } } } }
这里出现了一个非常重要的Bean——FactoryBean,可以说Spring一大半的扩展功能都与这个Bean有关,这是个特殊的Bean。它是个工厂Bean,可以产生Bean的Bean,这里的产生Bean是指Bean的实例,如果一个类继承FactoryBean用户可以自己定义产生实例对象的方法,只需实现它的getObject方法。然而在Spring内部,这个Bean的实例对象是FactoryBean,通过调用这个对象的getObject方法就能获取用户自定义产生的对象,从而为Spring提供了很好的扩展性。Spring获取FactoryBean本身的对象是通过在前面加上&来完成的。
如何创建Bean的实例对象及如何构建Bean实例对象之间的关联关系是Spring中的一个核心,这个过程的流程图如下:
如果是普通的 Bean 就直接创建他的实例,是通过调用 getBean 方法。下面是创建 Bean 实例的时序图:
还有一个非常重要的部分就是建立 Bean 对象实例之间的关系,这也是 Spring 框架的核心竞争力,何时、如何建立他们之间的关系请看下面的时序图:
Ioc 容器的扩展点
如何让这些 Bean 对象有一定的扩展性,就是可以加入用户的一些操作。对 Spring 的 Ioc 容器来说,主要有以下几个。
- BeanFactoryPostProcessor, BeanPostProcessor。他们分别是在构建 BeanFactory 和构建 Bean 对象时调用。
- InitializingBean 和 DisposableBean 分别是在 Bean 实例创建和销毁时被调用。用户可以实现这些接口中定义的方法,Spring会在适当的时候调用。
- FactoryBean 是个特殊的 Bean,如果一个类继承FactoryBean,用户可以自己定义产生实例对象的方法,只需实现它的getObject方法。
这些扩展点通常也是我们使用 Spring 来完成我们特定任务的地方,如何精通 Spring 就看你有没有掌握好 Spring 有哪些扩展点,并且如何使用他们,要知道如何使用他们就必须了解他们内在的机理。可以用下面一个比喻来解释。
我们把 Ioc 容器比作一个箱子,这个箱子里有若干个球的模子,可以用这些模子来造很多种不同的球,还有一个造这些球模的机器,这个机器可以产生球模。那么他们的对应关系就是 BeanFactory 就是那个造球模的机器,球模就是 Bean,而球模造出来的球就是 Bean 的实例。
所说的几个扩展点又在什么地方呢?BeanFactoryPostProcessor 对应到当造球模被造出来时,你将有机会可以对其做出设当的修正,也就是他可以帮你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的开始和结束阶段,你可以完成一些预备和扫尾工作。BeanPostProcessor 就可以让你对球模造出来的球后做出适当的修正。最后还有一个 FactoryBean,它可是一个神奇的球模。这个球模不是预先就定型了,而是由你来给他确定它的形状,既然你可以确定这个球模型的形状,当然他造出来的球肯定就是你想要的球了,这样在这个箱子里尼可以发现所有你想要的球。
Ioc 容器如何为我所用
我们使用 Spring 必须要首先构建 Ioc 容器,没有它 Spring 无法工作,ApplicatonContext.xml 就是 Ioc 容器的默认配置文件,Spring 的所有特性功能都是基于这个 Ioc 容器工作的,比如AOP,JDBC。
Ioc 它实际上就是为你构建了一个魔方,Spring 为你搭好了骨骼架构,这个魔方到底能变出什么好的东西出来,这必须要有你的参与。那我们怎么参与?这就是前面说的要了解 Spring 中那有些扩展点,我们通过实现那些扩展点来改变 Spring 的通用行为。至于如何实现扩展点来得到我们想要的个性结果,Spring 中有很多例子,其中 AOP 的实现就是 Spring 本身实现了其扩展点来达到了它想要的特性功能,可以拿来参考。