上篇文章 [重温 IOC 设计理念] 后, 我想你们对 IOC 有了一定的了解。但是了解的同时,你肯定也有带着很多疑惑,例如说,
我觉得,从 IOC 这个概念引出来的疑惑是非常多的。所以,这篇文章我决定对那篇文章进行一个“坑”的填充。所以这篇文章讲的主题是
IOC 在 Spring 的实现
或许很多人看过 Spring MVC 处理请求源码。既然你看过,相信你会看到过一个类 DispatherServlet,它就是负责处理请求的核心类,相当于一个中央调配器。而我们可以发现, DispatherServlet 有
一个构造器方法是注入一个 WebApplicationContext 的,代码如下:
public DispatcherServlet(WebApplicationContext webApplicationContext) { super(webApplicationContext); this.setDispatchOptionsRequest(true); }复制代码
这个 WebApplicationContext 可以理解为 [ Web 环境的上下文 ]。学过计算机的同学都应该知道,所谓上下文其实就是存储一些运行时必要的数据。最为经典的就是一个正在执行的线程如果被调度的话,CPU 是要将其一些上下文的数据进行保存例如说“执行到哪一行”“全局变量”等等。
那你可能会说,这个 WebApplicationContext 有什么用呢?其实,WebApplicationContext 也是 IOC 的实现。如果你不相信,可以看一下官网的定义:
定义链接: docs.spring.io/spring-fram… 我们可以看到以下定义:
The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory.
它说 BeanFactory 和 ApplicationCont
ext 皆为 Spring IOC 容器。但是,前半句 Spring Doc 讲了,BeanFactory 这个接口提供了先进的配置管理机制来管理。而 ApplicationContext 则是其子类。那么,在讲 ApplicationContext 之前我们先讲一下 BeanFactory。
BeanFactory 翻译过来就是 Bean 工厂。按照文档来说,它有以下的特点
现在我们来看看 BeanFactory 的一个基础 DefaultListableBeanFactory 的继承图,能看出它究竟如何通过继承的方式来实现以上特点。
看完了 BeanFactory,我们来看看它的 API
观察 API 可以看出,我补充说明:
若想从 BeanFactory 获取 Bean,那么首先我们需要讲 Bean 的元数据搞进 Spring IOC 容器当中。怎么弄?Spring 提供了两种方法:
像注解配置,我们经常会看到 Spring Boot 使用了 @Autowire 注解,例如说
public class UserService { @Autowireprivate UserDao userDao; }复制代码
像 Java 配置,我们也可以使用 @Configuration / @Bean / @Import / @DependsOn,例如说
@Configuration public class SecurityConfig{ @Bean public UserDao userDao() { return new UserDao(); } }复制代码
像 XML 配置就不用说了,那是 Spring / Hibernate / Structs 2 时代进行做的事情,例如说
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> </beans>复制代码
上面是我总结了一下其 Bean 元数据初始化的方法。或许你会问我究竟这几种元数据初始化方法,谁胜谁劣?目前按照我的认知来说如果单从便利性来比较是不可取的,我只能说各种方式都 not bad。各有个的应用场景,像 Spring Boot 这种约定大于配置的框架来说,更倾向于注解的方式,但是不意味着 XML 被淘汰了。因为 XML 的方式在某些特定场景还是会被用上,又或者是为了兼容老项目,Spring Boot 还特意保留着。所以,即使是多种选择,在业务架构上可能多种选择结合才是最佳方案。
看完了容器元数据的初始化,BeanFactory 作为 Spring IOC 最基础的容器,大部分时候我们不会直接从 BeanFactory 获取 Bean 的,那怎么使用它获取 Bean 呢?那接下来就要引出了其优秀的子类 - ApplicationContext 了!
上面已经说了 ApplicationContext 是 BeanFactory 的子类。现在我开始完善一下信息。ApplicationContext 是位于 org.springframework.context 包下。它属于 BeanFactory 的扩展增强。ApplicationContext 扩展都是一些面向框架的功能,例如说
从上面的选项其实我们都可以看到,Spring 框架考虑都是一些很常用的,围绕面向企业级别应用的特性。你想想,一个拥有诸多特性,而且又拥有 IOC 容器的功能类,那肯定就像是一个框架内部的控制器,方便别的组件进行调用。所以这也是为什么我们一般不直接通过 BeanFactory 进行 Bean 的获取,这也算是 “外开内聚”
的体现吧。那接下来我们看一下怎么通过 ApplicationContext 获取 Bean
假设我们在 resources/META-INF 目录下有个配置文件 servicees.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDao" class="com.jc.test01.dao.UserDao"> </bean> </beans>复制代码
然后我们使用 Java API 形式进行获取
// create and configure beans 创建和配置 BeansApplicationContext context = new ClassPathXmlApplicationContext("services.xml"); // retrieve configured instance 通过 名称+类型进行查找 UserDao userDao = context.getBean("userDao", UserDao.class); // use configured instance 调用方法 List<String> userList = userDao.getUsernameList();复制代码
上面的代码我要解释一下。ClassPathXmlApplicationContext 是 ApplicationContext 的扩展,就好像上面说的,Spring 多数情况下都是通过其丰富的子类来实现多种不同的应用风格。例如 ClassPathXmlApplicationContext 从名字看来我们可以知道它的意思是 “从 classPath 加载 XML 文件而来的上下文”[说明这是一个面向 XML 风格的实现类]。类似的还有注解风格的 AnnotationConfigApplicationContext ,还有文件系统风格的 FileSystemXmlApplicationContext 的等等。有兴趣可以通过 IDEA 来调试其继承图,你会大有收获。
在图上的粉红色圈圈,我们可以看到 ApplicationContext 的扩展特性实现的接口。总的来说,Spring 更喜欢的是通过组合继承的方法来实现特性,这样使各个类具备更高的专注度,提高封装和利用率。
到了文章末尾,我要回答这几个问题。
Spring 中关于 IOC 的特定实现是什么?
特定的实现是 BeanFactory 和 ApplicationContext。
它们是以什么方式来实现的?
其实从它们的类图就可以发现,它们其实都会通过组合和继承。如上面所说的,这样的好处在于提高各个类的职能,使其更加专注于其技能,也便于外部进行扩展。
如果 BeanFactory 和 ApplicationContext 都是容器的话,那么它们究竟谁才是底层 IOC 容器?还有它们面对真实的场景是什么?
上面说了 BeanFactory 和 ApplicationContext 皆为 IOC 的实现。但是 BeanFactory 是专注于提供最核心最基础的 IOC 功能,而 ApplicationContext 是面向企业级别应用而集成更多便于开发的特性的 IOC 实现。它们两个专注方向不同,非多余实现,互不影响。