Spring 的核心组件详解

Spring 的核心组件详解

Spring 总共有十几个组件,但是真正核心的组件只有三个:Core、Context 和 Bean。它们构建起了整个 Spring的骨骼架构,没有它们就不可能有 AOP、Web 等上层的特性功能。

一、Spring的设计理念

Bean组件是 Spring核心中的重点,Spring 就是面向Bean编程的(Bean Oriented Programming:BOP)就像Object 对OOP的意义一样,没有对象的概念就像没有面向对象的编程,在Spring中没有Bean也就没有Spring存在的意义。我们使用 Spring的主要一个原因就是 Spring会把对象之间的依赖关系转而用配置文件来管理。也就是依赖注入机制。而这个注入关系在一个叫 IOC的容器中管理,而IOC容器就是被Bean包裹的对象。Spring正是通过把对象包装在Bean中从而达到管理这些对象及做一系列额外操作的目的。

前面如果把 Bean比作一场演出中的演员,Context就是这场演出的舞台背景,而 Core应该就是演出的道具了。Bean包装的是Object,而 Object必然有数据,如何给这些数据提供生存环境就是Context要解决的问题,对Context来说它就是要发现每个Bean之间的关系并维护好这种关系。所以Context就是一个Bean关系的集合,这种关系集合又叫 IOC容器。Core组件用来发现、建立和维护每个 Bean之间所需的一系列工具,把 Core组件也可以看成是Util。 ​

二、Bean组件

Bean组件在Spring的 org.springframework.beans 包下,在这个包下的所有类主要解决了3件事:Bean的定义、Bean的创建及对Bean的解析。对Spring使用者来说唯一需要关心的就是Bean的创建,其他两个由Spring在内部完成。BeanDefination:Bean的定义完整的描述了在 Spring配置文件中定义的节点中所有的信息,包括各种子节点。当 Spring成功解析 节点后,在Spring内部它就被转化成 BeanDefinition对象,以后所有操作都是对这个对象操作。

Bean的解析过程非常复杂,功能被分的很细。因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。

三、Context组件

Context 在 Spring的 org.springframework.context 包下,给 Spring提供一个运行时的环境,用于保存各个对象的状态。ApplicationContext 是 Context 的父类,它除了能标识一个应用环境的基本信息外,还集成了5个接口来扩展 Context 的功能。例如:通过继承 BeanFactory 表明容器中运行的主体对象是 Bean,另外继承了 ResourceLoader接口,使得 ApplicationContext可以访问外部资源(在Core中说明);

【1】ApplicationContext 的子类主要包含两个方面: ● ConfigurableApplicationContext 表示该 Context 是可修改的,也就是在构建 Context 中,用户可以动态添加或修改已有的配置信息,其中最经常使用的是可更新的 Context,即 AbstractRefreshableApplicationContex类; ● WebApplicationContext 为Web装备的Context,可直接访问ServletContext;

【2】再往下分别就是构建 Context的文件类型,接着就是访问 Context的方式,这样一级一级构成了完成的 Context等级层次。总体来说 ApplicationContext 必须要完成一下几件事情: ✔ 标识一个应用环境; ✔ 利用 BeanFactory创建Bean对象; ✔ 保存对象关系表; ✔ 能够捕捉各种事件;Context 作为 Spring的IOC容器,基本上整合了 Spring的大部分功能,或者说是大部分功能的基础。

四、Core组件

Core 组件作为 Spring的核心组件,其中包含了很多关键类,例如:定义了资源的访问方式。这种将所有资源都抽象成一个接口的方式很值得以后的设计中拿来学习。

Context 与 Core之间的关系:比如 Context一般会把资源的加载、解析和描述工作委托给 ResourcePatternResolver类来完成,它相当于一个接头人,把资源的加载、解析和资源的定义整合在一起便于其他组件使用,在Core组件中还有很多类似的方式。

五、IOC容器如何工作

IOC容器实际上是 Context组件结合其他两个组件共同构建了一个 Bean关系网,如何构建这个关系网,构建的入口就在AbstractApplicationContext 类的 refresh方法中,这个方法代码如下:https://www.cnblogs.com/GooPolaris/p/8184429.html

1 @Override

2 public void refresh() throws BeansException, IllegalStateException {

3 synchronized (this.startupShutdownMonitor) {

4 // 为刷新做准备的 Context

5 prepareRefresh();

6 // 刷新所有BeanFactory子容器

7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

8 // 创建BeanFactory

9 prepareBeanFactory(beanFactory);

10

11 try {

12 // 注册实现了BeanPostProcess接口的Bean

13 postProcessBeanFactory(beanFactory);

14 // 初始化和执行BeanFactoryPostProcess beans

15 invokeBeanFactoryPostProcessors(beanFactory);

16 // 初始化和执行BeanPostProcess beans

17 registerBeanPostProcessors(beanFactory);

18 // 初始化 message source

19 initMessageSource();

20 // 初始化 event multicaster

21 initApplicationEventMulticaster();

22 // 刷新子类实现的方法

23 onRefresh();

24 // 检查注册事件

25 registerListeners();

26 // 初始化 (non-lazy-init) 单例Bean

27 finishBeanFactoryInitialization(beanFactory);

28 // 执行LifecycleProcessor.onRefresh()和ContextRefreshedEvent事件

29 finishRefresh();

30 }

31

32 catch (BeansException ex) {

33 destroyBeans();//如果刷新失败那么就会将已经创建好的单例Bean销毁掉

34 cancelRefresh(ex);//重置context的活动状态

35 throw ex;//抛出异常

36 }

37 }

38 }

这个方法就是构建整个 IOC容器过程的完整代码,了解里面的每一行代码,基本上就了解了大部分 Spring的原理和功能。这段主要包含一下步骤: 1)、构建BeanFactory,以便于产生所需的“演员”; 2)、注册可能感兴趣的事件; 3)、创建Bean实例对象; 4)、触发被监听的事件;首先创建和配置BeanFactory《容器》,这里是refresh,也就是刷新配置。前面介绍了Context有可更新的子类,这里正是实现这个功能,当BeanFactory存在时就更新,如果不存在就新创建。更新BeanFactory的方法如下:

1 @Override

2 protected final void refreshBeanFactory() throws BeansException {

3 if (hasBeanFactory()) {//如果已经存在一个bean工厂那么就将其销毁,关闭。

4 destroyBeans();

5 closeBeanFactory();

6 }

7 try {

8 DefaultListableBeanFactory beanFactory = createBeanFactory();//新建一个Bean工厂。

9 beanFactory.setSerializationId(getId());

10 customizeBeanFactory(beanFactory);//自定义bean工厂。

11 loadBeanDefinitions(beanFactory);//加载BeanDefinition

12 synchronized (this.beanFactoryMonitor) {

13 this.beanFactory = beanFactory;//将创建好的bean工厂的引用交给的context来管理。

14 }

15 }

16 catch (IOException ex) {//加载bean定义资源的时候可能会抛出异常。

17 throw new ApplicationContextException("I/O error parsing bean definition source for "

18 + getDisplayName(), ex);

19 }

20 }

这个方法实现了 AbstractApplictionContext 的抽象方法 refreshBeanFactory,这段代码清楚的说明了 BeanFactory的创建过程。注意 BeanFactory对象的类型变化,BeanFactory有很多子类,在什么情况下使用非常关键。BeanFactory的原始对象是DefaultListableBeanFactory,这个非常关键,因为它涉及后面对这个对象的多种操作,下面看一下这个类的继承关系图:​

从上图可以发现除了 BeanFactory相关类外,还发现与 Bean 的 register 相关的类。这在 refreshBeanFactory方法的loadBeanDefinitions(beanFactory) 一行将找到答案,这个方法将加载、解析Bean的定义,也就是把用户定义的数据结构转化为IOC容器中的特定数据结构。

创建好 BeanFactory后,添加一些 Spring本身需要的工具类,这个操作在 AbstractApplictionContext 的 prepareBeanFactory的prepareBeanFactory方法中完成。

在 AbstractApplicationContext中接下来的3行代码 Spring的功能扩展性能起了至关重要的作用。前两行主要是让你现在可以对已经构建的 BeanFactory的配置做修改,后面一行就是让你可以对以后再创建 Bean的实例对象时添加一些自定义的操作。所以它们都扩展了 Spring的功能,要学习 Spring必须搞清楚这一部分。

其中在 invokeBeanFactoryPostProcessors方法中主要是获取实现 BeanFactoryPostProcess接口的子类,并执行它的postProcessBeanFactory方法,这个方法的声明如下:

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException

它的参数是 beanFactory,说明可以对 beanFactory做修改,beanFactory是 ConfigurableListableBeanFactory类型的,这也印证了前面介绍的不同BeanFactory所使用的场合不同,这里只能是可配置的 BeanFactory,防止一些数据被用户随意修改。

registerBeanPostProcess方法也可以获取用户定义的实现了BeanPostProcessor接口的子类,并把他们注册到 BeanFacotry对象中的 beanPostProcessor变量中。在 BeanPostProcessor中声明了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,分别用于 Bean对象初始化时执行,可以执行用户自定义的操作。后面几行代码是初始化监听事件和对系统的其他监听者的注册,监听者必须是 ApplicationListener的子类。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {

//不使用TempClassLoader

beanFactory.setTempClassLoader(null);

//禁止修改当前Bean的配置信息

beanFactory.freezeConfiguration();

//实例化non-lazy-init类型的bean

beanFactory.preInstantiateSingletons();

}

从上面的代码中可以发现 Bean的实例化是在 BeanFactory中发生的。PreInstantiateSingletons方法的代码如下:

1 @Override

2 public void preInstantiateSingletons() throws BeansException {

3 if (this.logger.isDebugEnabled()) { //日志(这是一种好的日志处理方式,当没有开启debug级别时,就会省下一次字符串拼接),spring源码里到处都是这样的处理,可以学习。

4 this.logger.debug("Pre-instantiating singletons in " + this);

5 }

6

7 // Iterate over a copy to allow for init methods which in turn register new bean definitions.

8 // While this may not be part of the regular factory bootstrap, it does otherwise work fine.

9 //拷贝一个新的list ,this.beanDefinitionNames 干嘛的,上文大概已有说明,可以结合《Spring 源码阅读 BeanFactory(二) 之registerBeanDefinition方法》一块看

10 List beanNames = new ArrayList(this.beanDefinitionNames);

11

12 // Trigger initialization of all non-lazy singleton beans...

13 for (String beanName : beanNames) { //遍历beanName

14 //这里先解释一下getMergedLocalBeanDefinition方法的含义,因为这个方法会常常看到。Bean定义公共的抽象类是AbstractBeanDefinition,普通的Bean在Spring加载Bean定义的时候,实例化出来的是GenericBeanDefinition,而Spring上下文包括实例化所有Bean用的AbstractBeanDefinition是RootBeanDefinition,这时候就使用getMergedLocalBeanDefinition方法做了一次转化,将非RootBeanDefinition转换为RootBeanDefinition以供后续操作。

15 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

16 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {//非抽象的,单例的,非懒加载的

17 if (isFactoryBean(beanName)) { //Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean, 工厂Bean 再起炉灶

18 final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);

19 boolean isEagerInit;

20 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {

21 isEagerInit = AccessController.doPrivileged(new PrivilegedAction() {

22 @Override

23 public Boolean run() {

24 return ((SmartFactoryBean) factory).isEagerInit();

25 }

26 }, getAccessControlContext());

27 }

28 else {

29 isEagerInit = (factory instanceof SmartFactoryBean &&

30 ((SmartFactoryBean) factory).isEagerInit());

31 }

32 if (isEagerInit) {

33 getBean(beanName);

34 }

35 }

36 else { //非工厂Bean 调用该方法

37 getBean(beanName);

38 }

39 }

40 }

41 }

这里出现了一个 Bean(Factory Bean),可以说 Spring有一大半的扩展功能与这个 Bean有关,它是一个工厂Bean,用于产生Bean实例,如果一个类继承 FactoryBean,用户可以自定义产生对象的方法,只需要实现它的 getObject方法即可。然而在Spring内部,这个Bean的实例对象是 FactoryBean通过调用这个对象的 getObject方法就能获取用户自定义产生的对象,从而为Spring 提供了很好的扩展性。Spring 获取 FactoryBean本身的对象是通过在前面加上&来完成的。

如何创建 Bean的实例对象及如何构建Bean实例对象之间的关联关系是 Spring中的一个核心,还有就是建立 Bean对象实例之间的关系,也是 Spring框架的核心竞争力。 Bean实例创建流程图如下: ​

六、IOC容器的扩展点

对 Spring的 IOC容器来说,主要有 BeanFactoryPostProcessor和 BeanPostProcessor,他们分别在构建 BeanFactory和构建Bean对象时调用。还有就是 InItializingBean 和 DisposableBean,他们分别在Bean创建和销毁时调用。用户可以实现在这些接口中定义的方法,Spring 会在适当的时候调用它们。还有一个是FactoryBean,它是个特殊的Bean,可以被用户更多的控制。

这些扩展点通常也是我们使用 Spring来完成特殊任务的地方,如何精通Spring就看是否掌握好Spring有哪些扩展点,以及如何使用它们。要知道如何使用它们就必须了解它们内在的机制。

ApplicationContext.xml 就是 IOC容器的默认配置文件,Spring 的所有特性功能都是基于 IOC容器工作的。我们可以通过实现Spring 的扩展点来改变 Spring的通用行为。至于如何实现扩展点来得到我们想要的结果,在Spring中有很多例子,其中AOP的实现就是通过扩展点达到想要的特性功能,可以拿来参考。

相关推荐

《太极崛起》前期升级分享
365提款一直在审核

《太极崛起》前期升级分享

📅 10-28 👁️ 2933
长期吸烟的人,突然把烟戒了,是好事还是坏事?建议了解一下
Android之来电秀实现(一)
365体育投注提款

Android之来电秀实现(一)

📅 01-19 👁️ 9821
联想V系列笔记本:中端市场的实力担当
365体育投注提款

联想V系列笔记本:中端市场的实力担当

📅 10-11 👁️ 7023
椒盐虾姑(不用炸)的做法与步骤
365提款一直在审核

椒盐虾姑(不用炸)的做法与步骤

📅 12-21 👁️ 8062
Dota 2 自 2021 年以来首次显示在线人数创纪录低点
365体育投注提款

Dota 2 自 2021 年以来首次显示在线人数创纪录低点

📅 07-31 👁️ 6848
军训晒黑了多久能白回来
365提款一直在审核

军训晒黑了多久能白回来

📅 01-09 👁️ 4775
杭州地铁武林门站附近有哪些景点公园
365体育投注提款

杭州地铁武林门站附近有哪些景点公园

📅 10-14 👁️ 3088
怎样把甩棍的威力发挥到极致?
365bet平台网址

怎样把甩棍的威力发挥到极致?

📅 02-02 👁️ 6236