Spring的架构
最核心的Spring Core Container(即Spring容器),其他都是可选的。
在Spring框架重的核心组件其实只有三个:Bean,Core和Context。没有他们就没有诸如AOP、WEB等上层特性功能。
最核心组件:Bean
如果非要在这三个核心组件中选一个最核心的,那非Bean莫属了,其实Spring就是面向Bean编程。我们用Spring的原因是什么?其实Spring把我们和对象的依赖关系转而用配置文件来管理(就是它的依赖注入机制),这个注入关系在一个叫IOC的容器中进行集中管理,这个IOC容器又是什么?就是被Bean包裹的对象。也就是说,Bean来管理对象了,作为程序员的你只需要在适当的地方注入这些对象,并利用这些对象执行你的业务代码。
协同作战:Core和Context
假如这是一场战争,而Bean就是战场上的将军,那么Core就可以看作将军手中的剑、盾牌等利器,Context就可以看作这个战场,它为战争提供了环境。
为什么能这么比喻?我们都知道Bean包装的是对象,而对象里面肯定有数据,如何给这些数据提供生存环境就是 Context 要解决的问题,对 Context 来说就是
- 1,发现每个 Bean 之间的关系;
- 2,为它们建立这种关系;
- 3,要维护好这种关系。
所以 Context 就是一个 Bean 关系的集合,这个关系集合又叫 Ioc 容器,一旦建立起这个 Ioc 容器后 Spring 就可以为你工作了。
那 Core 组件又有什么用武之地呢?其实 Core 就是发现、建立和维护每个 Bean 之间的关系所需要的一系列的工具。
Spring注解
bean注入与装配的的方式有很多种:
- 1,通过xml;
- 2,通过get/set方式;
- 3,通过构造函数;
- 4,通过注解。
最简单易用的方式就是使用Spring的注解了,Spring提供了大量的注解方式,让项目阅读和开发起来更加方便。这里我总结项目开发中常用的注解(里面涉及一些SpringMVC的概念,我下篇博客详细介绍):
- @Controller:
- 用于注解控制层组件;
- 当标注在一个类上,该类就是控制层组件;
- DispatchServlet(分发控制器)会扫描使用了该注解的类方法,并检测该方法是否使用了@ResquestMapping注解;
- 可以把Request请求header部分的值绑定到方法参数上。
- @RestController:
- 相当于@Controller 和 @ResponseBody 的结合
- @Service:
- 用于注解业务层组件。
- @Repository:
- 用于注解DAO(数据访问层)的组件。
- @Component:
- 当组件不好归类,用它注解。
- 当组件不好归类,用它注解。
- @ResponseBody:
- 异步请求;
- 将Controller返回的请求,转换为指定格式,写入Response对象的Body数据区;
- @ResquestMapping:
- 处理地址请求映射的注解;
- 可以用于类上或者方法上;
- 用于类上时,表示类中所有响应请求的方法都是以该地址为父路径。
- @PathVariable:
- 用于将请求URL中的模板变量映射到功能处理方法的参数上。
- @RequestParam:
- 用于SpringMVC后台控制层获取参数。
- @RequestHeader:
- 将Request请求的Header部分的值绑定到方法参数上。
- @Autowired:
- 对类成员变量、方法、构造函数进行标注,完成自动装配的工作。
- @SessionAttribute:
- 写在class上,将值放到session作用域中。
- @Valid:
- 实体数据校验。
- @CookieValue:
- 用来获取Cookie中的值。
Spring的控制反转(IOC)
IOC的概念:
控制反转(IOC)还有个解释叫依赖注入(DI),这两个名字很难从字面理解,但是当你理解它的原理后就会发现它们的描述是何等准确。IOC技术的本质就是构建对象的技术,换句话说就是将一个类实例化成对象的技术。
通过控制反转或者依赖注入,对象的依赖关系将由负责协调系统中各个对象的第三方组件在创建对象时设定。对象无需自行创建或管理它们的依赖关系,依赖关系将被自动注入到需要它们的对象中去。
Spring提供一个容器,我们在xml文件里定义各个对象的依赖关系,由容器完成对象的构建,当我们Java代码里需要使用某个实例的时候就可以从容器里获取,那么对象的构建操作就被Spring容器接管。因此,控制反转就是本来属于java程序里构建对象的功能交由容器接管,依赖注入就是当程序要使用某个对象时候,容器会把它注入到程序中。读到这里大家应该能反应出来“控制反转”为什么要这么命名了吧。
IOC容器如何工作
很多人都知道,Spring的IOC功能其实就是依赖于Java的反射机制。直白点说,当你在xml文件中配置好了bean(bean需要提供全类名)之后,Spring通过自己的类对xml文件进行解析,然后利用反射机制将对象创建出来,然后放到自己的数据结构中,比如Map。然后键就是bean中的id属性的值,值就是创建的对象。当然这只是最粗略的了解,Spring具体是怎么样做的呢?
创建 BeanFactory 工厂
Ioc 容器实际上就是 Context 组件结合其他两个组件共同构建了一个 Bean 关系网,构建构建这个关系网的入口就在 AbstractApplicationContext 类的 refresh() 方法中。这个方法的代码如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1.1 初始化的刷新
prepareRefresh();
//1.2 告诉所有 bean factory 子容器,刷新自己(销毁内部的factory,创建新的factory)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//2 初始化 beanFactory的基本信息,添加一些 Spring 本身需要的一些工具类
prepareBeanFactory(beanFactory);
try {
//3.1 注册实现了BeanPostProcessor接口的beans
postProcessBeanFactory(beanFactory);
//3.2 初始化和执行BeanFactoryPostProcessor beans,
// 主要是获取实现 BeanFactoryPostProcessor 接口的子类
invokeBeanFactoryPostProcessors(beanFactory);
//3.3 初始化和执行BeanPostProcessor beans
// BeanPostProcessor是什么?是为了在bean加载过程中修改bean
registerBeanPostProcessors(beanFactory);
//4.1 为context 初始化 message source .
initMessageSource();
//4.2 为context 初始化 event multicaster
initApplicationEventMulticaster();
//4.3 初始化特殊beans
onRefresh();
//4.4 检测注册是事件
registerListeners();
//5 初始化所有单例beans
finishBeanFactoryInitialization(beanFactory);
//6 最后一步:初始化Lifecycle的bean并启动
finishRefresh();
}
catch (BeansException ex) {
// 销毁beans
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
这个方法就是Spring构建Ioc容器的完整代码,这段代码主要包含这样几个步骤:
- 1,构建 BeanFactory,以便于产生所需的类。
- 2,注册可能感兴趣的事件。
- 3,创建 Bean 实例对象。
- 4,触发被监听的事件。
我们一步一步来分析。注意我代码中注释用到的序号,下面直接用序号代表指定的哪一行代码。
(1)代码1.1和1.2主要是创建和配置 BeanFactory。下面是更新 BeanFactory 的方法代码:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) { //已存在就销毁旧的
destroyBeans();
closeBeanFactory();
}
try {
//BeanFactory 的原始对象是 DefaultListableBeanFactory
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);
}
}
这个方法实现了 AbstractApplicationContext 的抽象方法 refreshBeanFactory,这段代码清楚的说明了 BeanFactory 的创建过程。下图是创建BeanFactory 的流程图:
下图是Bean 的解析和登记流程时序图:
流程图讲得很明白,我这里就不再重复了。
(2)创建好 BeanFactory 后,接下去添加一些 Spring 本身需要的一些工具类,这个操作在AbstractApplicationContext 的代码2中完成。(就是prepareBeanFactory(beanFactory);
)
(3)接下来,代码3.1, 3.2,3.3对Spring 的功能扩展性起了至关重要的作用。前两行主要是让你现在可以对已经构建的 BeanFactory 的配置做修改,后面一行就是让你可以对以后再创建 Bean 的实例对象时添加一些自定义的操作,所以他们都是扩展了 Spring 的功能。
invokeBeanFactoryPostProcessors 方法中主要是获取实现 BeanFactoryPostProcessor 接口的子类。并执行它的 postProcessBeanFactory 方法,这个方法的声明如下:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;
它的参数是 beanFactory,说明可以对 beanFactory 做修改.
registerBeanPostProcessors 方法也是可以获取用户定义的实现了 BeanPostProcessor 接口的子类,并执行把它们注册到 BeanFactory 对象中的 beanPostProcessors 变量中。BeanPostProcessor 中声明了两个方法:postProcessBeforeInitialization、postProcessAfterInitialization 分别用于在 Bean 对象初始化时执行。可以执行用户自定义的操作。
(4)代码 4.1 - 4.4 是初始化监听事件和对系统的其他监听者的注册,监听者必须是 ApplicationListener 的子类。
(5)接下来是代码5,非常重要。单独列出来大篇幅说:
创建 Bean 实例:
(5-1)Bean 的实例化代码,是从 finishBeanFactoryInitialization 方法开始的,下面的finishBeanFactoryInitialization 的代码:
protected void finishBeanFactoryInitialization(
ConfigurableListableBeanFactory beanFactory) {
// 停止使用临时ClassLoader进行类型匹配。
beanFactory.setTempClassLoader(null);
// 允许缓存所有bean定义元数据,而不需要进一步的更改。
beanFactory.freezeConfiguration();
// 初始化Beans
beanFactory.preInstantiateSingletons();
}
从上面代码中可以发现 Bean 的实例化是在 BeanFactory 中发生的。
(5-2)接着再来看上述代码清单中的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);
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);
}
}
}
}
}
- FactoryBean:
- 这里出现了一个非常重要的 Bean —— FactoryBean,可以说 Spring 一大半的扩展的功能都与这个 Bean 有关,这个特殊的 Bean是个工厂 Bean,可以产生 Bean 的 Bean,这里的产生 Bean 是指 Bean 的实例。
- 如果一个类继承 了FactoryBean ,用户可以自己定义产生实例对象的方法,只要实现他的 getObject 方法。然而在 Spring 内部这个 Bean 的实例对象是 FactoryBean,通过调用这个对象的 getObject 方法就能获取用户自定义产生的对象,从而为 Spring 提供了很好的扩展性。
- Spring 获取 FactoryBean 本身的对象是在前面加上 & 来完成的。
下面的流程图很好的展示了preInstantiateSingletons 创建Bean实例的流程:
如何扩展IOC
如何让这些 Bean 对象有一定的扩展性(就是可以加入用户的一些操作)?有哪些扩展点?其实通过上面的介绍,我们已经能猜出一些了。
- BeanFactoryPostProcessor, BeanPostProcessor。他们分别是在构建 BeanFactory 和构建 Bean 对象时调用。
- InitializingBean 和 DisposableBean 他们分别是在 Bean 实例创建和销毁时被调用。用户可以实现这些接口中定义的方法,Spring 就会在适当的时候调用他们。
- FactoryBean 他是个特殊的 Bean,这个 Bean 可以被用户更多的控制。上面已经介绍过了。
参考文献:《深入分析JavaWeb技术内幕》、《Spring源码解析》等