前言
在前面的文章 浅析 Spring 的 IoC 和 DI 中简述了 IoC 和 DI 的基本概念和关系, 总体上说, IoC 是一种可以帮助我们解耦各业务对象间依赖关系的对象绑定方式, 那么 Spring 提供了两种容器类型来提供支持 IoC 方式. 这两种类型是:
BeanFactory: 基础类型的 IoC 容器, 提供完整的 IoC 服务支持
ApplicationContext: ApplicationContext 是在 BeanFactory 的基础之上构建的, 是相对高级的容器实现, 除了拥有 BeanFactory 的所有支持, ApplicationContext 提供了其他高级特性.
ApplicationContext 和 BeanFactory 的继承关系如下:
可以看到 ApplicationContext 间接继承自 BeanFactory.
BeanFactory
BeanFactory 的介绍
BeanFactory 是基础类型 IoC 容器, 提供完整的 IoC 服务支持. 如果没有特殊指定, 默认采用延迟初始化策略 (lazy-load). 只有当客户端对象需要访问容器中的某个受管对象的时候, 才对该受管对象进行初始化以及依赖注入工作.
BeanFactory 的对象注册
BeanFactory, 就是生产 Java Bean 的工厂, 作为 Spring 提供的基本的 IoC 容器, BeanFactory 帮助完成 业务对象的注册和对象间依赖关系的绑定.
实际上, BeanFactory 只是一个接口, 它负责定义如何访问容器内管理的 Bean 的方法, 各个 BeanFactory 的具体实现类负责具体 Bean 的注册以及管理工作. 下面是 BeanFactory 的接口代码:
- package org.springframework.beans.factory;
- public interface BeanFactory {
- /**
- * 用来引用一个实例, 或把它和工厂产生的 Bean 区分开, 就是说, 如果一个 FactoryBean 的名字为 a, 那么,&a 会得到那个 Factory
- */
- String FACTORY_BEAN_PREFIX = "&";
- /*
- * 四个不同形式的 getBean 方法, 获取实例
- */
- Object getBean(String name) throws BeansException;
- <T> T getBean(String name, Class<T> requiredType) throws BeansException;
- <T> T getBean(Class<T> requiredType) throws BeansException;
- Object getBean(String name, Object... args) throws BeansException;
- boolean containsBean(String name); // bean 是否存在
- boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否为单实例
- boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否为原型 (多实例)
- boolean isTypeMatch(String name, Class<?> targetType)
- throws NoSuchBeanDefinitionException;// 名称, 类型是否匹配
- Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取类型
- String[] getAliases(String name);// 根据实例的名字获取实例的别名
- }
下面我们来测试下一般情况下 BeanFactory 接口的具体实现类情况:
- // 实体类
- @Component
- public class Demo {
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- //Junit 测试类
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class ApplicationTests {
- @Autowired
- private BeanFactory beanFactory;
- @Test
- public void test() {
- System.out.println("concrete factory is:" + beanFactory.getClass());
- Assert.assertTrue("Factory can't be null",beanFactory != null);
- Demo demo = (Demo) beanFactory.getBean("demo");
- System.out.println("Found the demo bean:"+demo.getClass());
- }
- }
输出结果如下:
- concrete factory is: class org.springframework.beans.factory.support.DefaultListableBeanFactory
- Found the demo bean: class com.pjmike.spring.Demo
从结果可以看出, 具体工厂是 org.springframework.beans.factory.support.DefaultListableBeanFactory 的实例. 再来看看 BeanFactory 的继承体现:
从上图可以看出, BeanFactory 有三个直接子类:
ListableBeanFactory: 通过继承该接口可以列出所有的 Bean, 也可以只列出与预期类型相对应的 bean
HierarchicalBeanFactory: 支持分层 bean 的管理, 使 BeanFactory 支持双亲 IoC 容器的管理功能
AutowireCapableBeanFactory: 可以填充不受 Spring 控制的 Bean
而三个类的子类体系就更多, 详细的参考 Spring 源码.
再来看看之前提到的 DefaultListableBeanFactory, 它也是上图中最底层的实现类:
- public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
- implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
- ...
- }
这个类其实就是 BeanFactory 的默认实现类, 一个比较通用的 BeanFactory 实现类, 它除了间接实现 BeanFactory 接口外, 还实现了 BeanDefinitionRegistry 接口, 该接口才是 BeanFactory 实现中担任 Bean 注册管理的角色, 它抽象的定义了 Bean 注册的逻辑, 当然具体的是实现还是靠 DefaultListableBeanFactory 这等实现类.
ApplicationContext
ApplicationContext 的介绍
ApplicationContext 是在 BeanFactory 的基础上构建的, 是相对比较高级的容器实现, 除了拥有 BeanFactory 的所有支持, ApplicationContext 还提供了其他高级特性, 比如:
统一资源加载策略
国际化信息支持
容器内部事件发布机制
在 ApplicationContext 容器启动之后, 默认全部初始化并绑定完成, 所以, 对于 BeanFactory 来说, ApplicationContext 往往要求更多的系统资源
ApplicationContext 的实现
Spring 中的 Context
Spring 为基本的 BeanFactory 类型容器提供了 XmlBeanFactory 实现 (继承自 DefaultListableBeanFactory), 相应的, 它也为 ApplicationContext 类型容器提供了以下几个常用的实现:
org.springframework.context.support.FileSystemXmlApplicationContext
: 在默认情况下, 从文件系统加载 bean 定义以及相关资源的 ApplicationContext 实现
org.springframework.context.support.ClassPathXmlApplicationContext
: 在默认情况下, 从 Classpath 加载 bean 定义以及相关资源的 ApplicationContext 实现
org.springframework.web.context.support.XmlWebApplicationContext
: Spring 提供的用于 Web 应用程序的 ApplicationContext 实现.
在传统的基于 xml 的 Spring 项目中, 经常会使用到上面的实现类
SpringBoot 中的 Context
在官方文档中给出对于一个 SpringBoot 应用它对应的 Context 的情况:
对于 Web 应用, context 是
AnnotationConfigServletWebServerApplicationContext
对于 响应式应用, context 是
AnnotationConfigReactiveWebServerApplicationContext
对于普通非 Web 应用, context 是
AnnotationConfigApplicationContext
以上的 context 实际上也是实现了 ApplicationContext 接口
ApplicationContext 的简单实践
我们都知道 IoC 容器一般有两种对象注入方式: 基于 xml 配置文件 与 基于注解驱动的方式. 下面就分别从这两个角度来看如何使用 ApplicationContext
基于 xml 配置文件
定义个实体类
- public class User {
- private Integer id;
- private String username;
- public User(Integer id, String username) {
- this.id = id;
- this.username = username;
- }
- public User() {
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- }
设置一个 xml 配置文件, 声明 User Bean
- <?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="user" class="com.pjmike.spring.domain.User">
- <constructor-arg name="id" value="1"/>
- <constructor-arg name="username" value="pjmike"/>
- </bean>
- </beans>
主程序
- public class XmlBootStrap {
- public static void main(String[] args) {
- // 构建一个 ApplicationContext 上下文
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
- // 设置此应用上下文的配置路径
- context.setConfigLocations("classpath:/META-INF/spring/context.xml");
- // 调用 refresh 方法, 完成配置的解析, 各种 BeanFactoryPostProcessor 和 BeanPostProcessor 的注册, 国际化配置的初始化, Web 内置容器的构造
- context.refresh();
- User user = context.getBean("user", User.class);
- System.out.print("user.getName() ="+ user.getUsername());
- }
- }
输出结果
user.getName() = pjmike
基于注解方式
声明一个配置类
- @Configuration
- public class UserConfiguration {
- @Bean(name = "user")
- public User user() {
- User user = new User();
- user.setUsername("pj");
- return user;
- }
- }
主程序
- public class AnnotationBootStrap {
- public static void main(String[] args) {
- // 构建一个 ApplicationContext 应用上下文
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- // 注册一个配置 Bean
- context.register(UserConfiguration.class);
- // 调用 refresh 启动容器
- context.refresh();
- User user = context.getBean("user", User.class);
- System.out.println("user.getName() ="+user.getUsername());
- }
- }
输出结果
user.getName() = pj
xml 与 Annotation 简单对比
从上面的两个例子可以看出基于 xml 和基于注解注入 Bean 的方式是不一样的, 基于 xml 的应用上下文 ClassPathXmlApplicationContext 需要设置配置路径, 基于注解的应用上下文 AnnotationConfigApplicationContext 需要注册一个配置 Bean, 但它们相同的一步就是必须要调用 refresh() 方法, 该方法可以看做是 IoC 容器的启动方法, 它会做很多操作, 比如完成配置的解析, 各种 BeanFactoryPostProcessor 和 BeanPostProcessor 的注册, 国际化配置的初始化, Web 内置容器的构造等等, 不调用它, 容器就无法启动. 这里只是简要说明, 更加详细的介绍会在后面的文章介绍.
现在是 springboot 盛行的阶段, 基于 xml 配置文件的方式已经逐步被基于注解的方式所取代, 如今的项目中, 更多的使用 注解的方式. 关于 xml 与注解更详细的对比可以参阅开涛大神的文章: http://jinnianshilongnian.iteye.com/blog/1879910
小结
上面的文章比较简单的总结了 BeanFactory 和 ApplicationContext, 为后续分析 Spring IoC 详细的初始化过程, Spring Bean 的加载等做一个铺垫
来源: https://juejin.im/post/5bf51d4c5188256d9832b0d3