ApplicationContext、BeanFactory简介与启动流程
1.容器简介
什么是容器?
Java应用中,对象与对象之间往往存在各种复杂的依赖,对象的构建也会变得越来越复杂,如果这些对象的生命周期全部由开发人员编写的话,那么工作量将会变得无比巨大,对象的管理也会非常复杂。所以才会诞生容器的概念,容器的核心左右只有一个:Bean的管理,当我们需要使用某个 Bean 时,容器会自动帮我们创建,并在适当时销毁。外界有一个标准的名词,前者称呼为 IOC,也就是控制反转,后者称呼为 DI,也就是依赖注入。
IOC/DI
-
IOC (Inversion of Control) 控制反转:
所谓控制反转,就是当我们需要某个 Bean 时,将 Bean 的名称告知容器,由容器去创建该 Bean,而不是我们手动 new 一个,这里 Bean 创建管理的控制权都交给了容器,所以这是一种控制权的反转。其通俗点讲就是需要什么东西让别人送过来,而不是自己去拿。
-
DI (Dependency Injection) 依赖注入:
就是指当 A Bean 里面需创建 B Bean 时,会在创建 A Bean 的时候,自动将依赖的 B Bean 注入进去,其 B Bean 是被动接受注入而不是自己主动去找。换句话说就是指 A Bean 不是从容器中查找它依赖的 B Bean,而是在容器创建 A Bean 候主动将它依赖的 B Bean 注入给它。
2.容器的结构
容器本质上可以也可以看作是 Bean 工厂,该工厂管理 Bean 的生命周期,以及 Bean 之间的依赖关系。外界也将 Spring 容器称为 IOC 容器。当然,这里容器仅仅是 Spring 的抽象概念,代码中将其具象化为 BeanFactory 或 ApplicationContext,容器功能也由具象化的类进行处理。
2.1 BeanFactory
BeanFactory 是容器最基础的类,它定义了容器的基本功能规范:
public interface BeanFactory {
// 对 FactoryBean 的转义定义,因为如果使用 bean 的名字检索 FactoryBean 得到的对象是工厂生成的对象,
String FACTORY_BEAN_PREFIX = "&";
// 根据 bean 的名字,获取在容器中 bean 实例
Object getBean(String name) throws BeansException;
//根据 bean 的名字和 Class 类型来得到 bean 实例,增加了类型安全验证机制。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 提供对 bean 的检索,看看是否在容器有这个名字的 bean
boolean containsBean(String name);
// 根据 bean 名字,判断这个 bean 是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 根据 bean 名字,判断这个 bean 是不是原型
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
// 根据 bean 名字,判断是否与指定的类型匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
// 得到 bean 实例的 Class 类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// 得到bean 的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}
在 BeanFactory 里只对容器的基本行为作了定义,其根本不关心你的 Bean 是如何定义怎样加载的。至于工厂是怎么生产这些对象的,这个基本的接口不关心。而要知道工厂是如何产生对象的,我们就需要看具体的容器了,也就是 BeanFactory 的子类。
BeanFactory 大致的继承关系如下:
BeanFactory 体系中常用的实现类有:
- ListableBeanFactory:提供容器中 bean 迭代的功能。如返回所有 Bean 的名字、容器中 Bean 的数量等。
- HierarchicalBeanFactory:提供父容器的访问功能,可通过 ConfigurableBeanFactory 的
setParentBeanFactory 方法设置父容器。 - AutowireCapableBeanFactory:为 Spring 容器之外的 Bean ,也就是未交由 Spring 管理的
Bean ,提供依赖注入的功能。
以上三个是 BeanFactory 的直系亲属,这个三个直系亲属下面又派生了两个复杂的容器:
- ConfigurableBeanFactory:其继承了 HierarchicalBeanFactory 和
SingletonBeanRegistry 这两个接口,其提供了很多方法,如:定义类加载器、类型转化、属性编辑器、注册依赖 Bean
、销毁 bean 等,且该接口被大多数的容器继承、实现。 - ConfigurableListableBeanFactory:这个接口继承了 ListableBeanFactory、
AutowireCapableBeanFactory、ConfigurableBeanFactory,自身主要提供用于分析和修改 bean
定义以及预先实例化单例 Bean 的方法。 最后是核心容器:
DefaultListableBeanFactory:它实现了以上所有的接口,在 BeanFactory 体系中可以作为一个独立的容器使用。
2.2 ApplicationContext
上面说过 ApplicationContext 是 BeanFactory 子类,它不仅包含 BeanFactory 所有功能,还对其进行了扩展,而我们喜欢将 ApplicationContext 称为应用上下文,因为容器只是它的基本功能。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
// 返回此应用程序上下文的唯一ID
@Nullable
String getId();
// 返回此上下文所属的应用程序名称
String getApplicationName();
// 返回应用上下文具像化的类名
String getDisplayName();
// 返回第一次加载此上下文时的时间戳
long getStartupDate();
// 获取父级应用上下文
@Nullable
ApplicationContext getParent();
// 将 AutowireCapableBeanFactory 接口暴露给外部使用
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
ApplicationContext 自身提供的方法非常简单,但它继承了六个接口,来扩展自身功能:
- EnvironmentCapable:获取 Environment。
- ListableBeanFactory、HierarchicalBeanFactory:这是 BeanFactory 体系接口,分别提供
Bean 迭代和访问父容器的功能。 - MessageSource:支持国际化功能。
- ApplicationEventPublisher:应用事件发布器,封装事件发布功能的接口。
- ResourcePatternResolver:该接口继承至 ResourceLoader ,作用是加载多个 Resource。
- ApplicationContext 同样提供了非常多的实现类,其又可细分为两大类,
ConfigurableApplicationContext 和 WebApplicationContext。
2.2.1 ConfigurableApplicationContext
该接口是比较重要的一个接口,几乎所有的应用上下文都实现了该接口。该接口在ApplicationContext的基础上提供了配置应用上下文的能力,此外提供了生命周期的控制能力。
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
// 应用上下文配置时,这些符号用于分割多个配置路径
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
// BeanFactory中,ConversionService类所对应的bean的名字。如果没有此类的实例的话吗,则使用默认的转换规则
String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
//LoadTimeWaver类所对应的Bean在容器中的名字。如果提供了该实例,上下文会使用临时的 ClassLoader ,这样,LoadTimeWaver就可以使用bean确切的类型了
String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
// Environment 类在容器中实例的名字
String ENVIRONMENT_BEAN_NAME = "environment";
// System 系统变量在容器中对应的Bean的名字
String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
// System 环境变量在容器中对应的Bean的名字
String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
// 设置容器的唯一ID
void setId(String id);
// 设置此容器的父容器
void setParent(@Nullable ApplicationContext parent);
// 设置容器的 Environment 变量
void setEnvironment(ConfigurableEnvironment environment);
// 以 ConfigurableEnvironment 的形式返回此容器的环境变量。以使用户更好的进行配置
@Override
ConfigurableEnvironment getEnvironment();
// 此方法一般在读取应用上下文配置的时候调用,用以向此容器中增加BeanFactoryPostProcessor。增加的Processor会在容器refresh的时候使用。
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
// 向容器增加一个 ApplicationListener,增加的 Listener 用于发布上下文事件,如 refresh 和 shutdown 等
void addApplicationListener(ApplicationListener<?> listener);
// 向容器中注入给定的 Protocol resolver
void addProtocolResolver(ProtocolResolver resolver);
// 这是初始化方法,因此如果调用此方法失败的情况下,要将其已经创建的 Bean 销毁。
// 换句话说,调用此方法以后,要么所有的Bean都实例化好了,要么就一个都没有实例化
void refresh() throws BeansException, IllegalStateException;
// 向JVM注册一个回调函数,用以在JVM关闭时,销毁此应用上下文
void registerShutdownHook();
// 关闭此应用上下文,释放其所占有的所有资源和锁。并销毁其所有创建好的 singleton Beans
@Override
void close();
// 检测此 FactoryBean 是否被启动过
boolean isActive();
// 返回此应用上下文的容器。
// 千万不要使用此方法来对 BeanFactory 生成的 Bean 做后置处理,因为单例 Bean 在此之前已经生成。
// 这种情况下应该使用 BeanFactoryPostProcessor 来在 Bean 生成之前对其进行处理
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
该接口下又有几个重要的实现类:
- AbstractApplicationContext:这是个抽象类,仅实现了公共的上下文特性。这个抽象类使用了模板方法设计模式,需要具体的实现类去实现这些抽象的方法。
- GenericApplicationContext:该类继承自 AbstractApplicationContext,是为通用目的设计的,它能加载各种配置文件,例如 xml,properties 等等。它的内部持有一个 DefaultListableBeanFactory 的实例,实现了
- BeanDefinitionRegistry 接口,以便允许向其应用任何 bean 的定义的读取器。
- AnnotationConfigApplicationContext:该类继承自 GenericApplicationContext,提供了注解配置(例如:@Configuration、@Component等)和类路径扫描(scan方法)的支持。
2.2.2 WebApplicationContext
该接口是专门为 Web 应用准备的,其允许从相对于 Web 根目录的路径中装载配置文件完成初始化。
public interface WebApplicationContext extends ApplicationContext {
// 整个 Web 应用上下文是作为属性放置在 ServletContext 中的,该常量就是应用上下文在 ServletContext 属性列表中的 key
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
// 定义了三个作用域的名称
String SCOPE_REQUEST = "request";
String SCOPE_SESSION = "session";
String SCOPE_APPLICATION = "application";
// 在工厂中的 bean 名称
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
// ServletContext 初始化参数名称
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
// 在工厂中 ServletContext 属性值环境bean的名称
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
// 用来获取 ServletContext 对象
@Nullable
ServletContext getServletContext();
}
该接口的核心实现类有:
- ConfigurableWebApplicationContext:该接口同时继承了 WebApplicationContext 和
ConfigurableApplicationContext,提供了 Web 应用上下文的可配置的能力。 - GenericWebApplicationContext:该类继承自 GenericApplicationContext,实现了
ConfigurableWebApplicationContext。 - XmlWebApplicationContext:该上下文是使用 Xml 配置文件的方式,不过是在 Web 环境中使用的。
- AnnotationConfigServletWebServerApplicationContext:该类是被 SpringBoot
扩展而来的,SpringBoot 使用的就是该上下文。
3、ApplicationContext 启动流程
- 1、从启动类开始
@Xiao7Application(appName = "example", port = "8081", env = AppConstant.ENV_DEV)
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringbootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}
- 2、SpringApplication的run方法
public ConfigurableApplicationContext run(String... args) {
...
ConfigurableApplicationContext context = null;
...
try {
...
// 通过 createApplicationContext 方法创建上下文,根据 Web 环境不同创建的上下文也不同
context = createApplicationContext();
...
// 该方法用于启动上下文
refreshContext(context);
...
}
catch (Throwable ex) {
...
}
context = createApplicationContext();
...
}
- 3、AbstractApplicationContext的refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 初始化 refresh 的上下文环境,就是记录下容器的启动时间、标记已启动状态、处理配置文件中的占位符
prepareRefresh();
// 2. 初始化 BeanFactory,加载并解析配置
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
/* ---至此,已经完成了简单容器的所有功能,下面开始对简单容器进行增强--- */
// 3. 对 BeanFactory 进行功能增强,如设置BeanFactory的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory);
try {
// 4. 后置处理 beanFactory,交由子类实现
postProcessBeanFactory(beanFactory);
// 5. 调用已注册的 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册 BeanPostProcessor,仅仅是注册,调用在getBean的时候
registerBeanPostProcessors(beanFactory);
// 7. 初始化国际化资源
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 留给子类实现的模板方法
onRefresh();
// 10. 注册事件监听器
registerListeners();
// 11. 实例化所有非延迟加载的单例
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新过程,发布应用事件
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
}
// 13.销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
this.destroyBeans();
// Reset 'active' flag.
this.cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
this.resetCommonCaches();
}
}
}
整个流程如此,具体流程以后在具体分析。