本文主题
上篇文章 [重温 IOC 设计理念] 后, 我想大家对 IOC 有了必定的了解。可是了解的同时,你确定也有带着不少疑惑,例如说,
- Spring 中关于 IOC 的特定实现是什么?
- 它们是以什么方式来实现的?
- 若是 BeanFactory 和 ApplicationContext 都是容器的话,那么它们究竟谁才是底层 IOC 容器?还有它们面对真实的场景是什么?
我以为,从 IOC 这个概念引出来的疑惑是很是多的。因此,这篇文章我决定对那篇文章进行一个“坑”的填充。因此这篇文章讲的主题是
IOC 在 Spring 的实现
或许不少人看过 Spring MVC 处理请求源码。既然你看过,相信你会看到过一个类 DispatherServlet,它就是负责处理请求的核心类,至关于一个中央调配器。而咱们能够发现, DispatherServlet 有
一个构造器方法是注入一个 WebApplicationContext 的,代码以下:
public DispatcherServlet(WebApplicationContext webApplicationContext) {
super(webApplicationContext);
this.setDispatchOptionsRequest(true);
}复制代码
这个 WebApplicationContext 能够理解为 [ Web 环境的上下文 ]。学过计算机的同窗都应该知道,所谓上下文其实就是存储一些运行时必要的数据。最为经典的就是一个正在执行的线程若是被调度的话,CPU 是要将其一些上下文的数据进行保存例如说“执行到哪一行”“全局变量”等等。
那你可能会说,这个 WebApplicationContext 有什么用呢?其实,WebApplicationContext 也是 IOC 的实现。若是你不相信,能够看一下官网的定义:
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
BeanFactory 翻译过来就是 Bean 工厂。按照文档来讲,它有如下的特色
- BeanFactory 的 API 为 Spring 的 IoC 功能提供了底层基础。
- BeanFactory 遵循了“开闭原则”,对外开放扩展,对内关闭修改。它经过 BeanFactoryAware / InitializingBean / DisposableBean 来扩展其余框架。
- BeanFactory 是核心 API,这也就意味着它功能很是简洁,关注度高。BeanFactory 的实现不会对配置格式或要使用的任何组件注释作任何假设。你能够理解为它不会要求你必定要遵照什么规则或规范才能使用 Spring,由于它的抽象性,可兼容不一样的实现风格例如 XmlBeanDefinitionReader 和 AutowiredAnnotationBeanPostProcessor 等等。因此这就是Spring的容器如此灵活和可扩展的本质。[若是面试官问你你能够这么回答哦!]
如今咱们来看看 BeanFactory 的一个基础 DefaultListableBeanFactory 的继承图,能看出它究竟如何经过继承的方式来实现以上特色。
API
看完了 BeanFactory,咱们来看看它的 API
观察 API 能够看出,我补充说明:
- BeanFactory 接口仅仅提供了读功能却没有写功能?其实这些写功能会留给其子类进行实现的;
- 而后 BeanProvider 是在 Spring 中提供了延迟查找和注入的功能实现,之后有机会会讲解说明
- 咱们能看到 BeanFactory 提供了 name 或者 requireType 来获取 Bean。那是否是说明仅仅提供了两种方法进行查找呢?这个就是咱们接下来要讲的话题 - Bean 原元数据定义
Bean 原元数据定义
若想从 BeanFactory 获取 Bean,那么首先咱们须要讲 Bean 的元数据搞进 Spring IOC 容器当中。怎么弄?Spring 提供了两种方法:
- 经过注解配置
- 经过 Java API 配置
- 经过 XML 配置
像注解配置,咱们常常会看到 Spring Boot 使用了 @Autowire 注解,例如说
public class UserService {
@Autowireprivate
UserDao userDao;
}复制代码
像 Java 配置,咱们也可使用 @Configuration / @Bean / @Import / @DependsOn,例如说html
@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
上面已经说了 ApplicationContext 是 BeanFactory 的子类。如今我开始完善一下信息。ApplicationContext 是位于 org.springframework.context 包下。它属于 BeanFactory 的扩展加强。ApplicationContext 扩展都是一些面向框架的功能,例如说
- i18n 国际化
- 有强大的资源抽象类 Resource 和加载起 ResourceLoader,能够读取 URLS 和 文件
- 事件监听机制
- 能够加载多个具备层级关系的上下文
从上面的选项其实咱们均可以看到,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 实现。它们两个专一方向不一样,非多余实现,互不影响。