【面试】Spirng的IOC启动流程
面试回答总结
IOC启动流程主要分为:容器创建 -> 配置加载 -> Bean的解析 -> Bean的注册->工厂初始化前置工作->执行bean工厂的后置处理器->bean的后置处理器注册->国际化->事件注册->监听器注册->bean的实例化->完成刷新,以下为xml启动方式
- 基于XML的启动,是通过ClassPathXmlApplicationContext的构造器,传入xml文件的名称,然后构造器种会调用父类的构造器DefaultResourceLoader,在构造器中中创建了ResourcePatternResolver资源加载器,它能够将指定的资源位置路径解析为Resource对象。
- 然后通过setConfigLocations 保存配置地址,保存到configLocations数组中,然后调用Refresh刷新容器;
- 在 AbstractApplicationContext的refresh()中通过模板设计模式,定义了容器启动的流程,分别是:
-
- prepareRefresh():准备刷新工作 ,记录开始时间,初始化属性,校验配置文件,准备事件的存储Set
- obtainFreshBeanFactory():告诉子类,刷新Bean工厂,销毁旧beanFactory,创建新beanFactory,默认DefaultListableBeanFactory,从子容器的refreshBeanFactory方法中载入Bean的资源文件;
- prepareBeanFactory(beanFactory):准备工厂,配置工厂的下文特性, 例如上下文的 ClassLoader 和后处理器。Bean表达式解析器,BeanPostProcessor和 Aware类的自动装配等;
- postProcessBeanFactory(beanFactory):BeanFactory初始化完成的后置工作,这是一个空方法,留给三方框架或者自己配置,作用是允许对beanFoctory进行扩展处理。
- invokeBeanFactoryPostProcessors(beanFactory):调用BeanFactory的后置处理器BeanFactoryPostProcessor,在 bean定义注册之后bean实例化之前调用
- registerBeanPostProcessors(beanFactory):注册Bean的后置处理器BeanPostProcessor,在Bean初始化前,后执行
- initMessageSource:初始化信息源,国际化相关
- initApplicationEventMulticaster():初始化容器事件传播器
- onRefresh():空方法,该方法子类实现,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
- registerListeners():注册事件监听器,注册实现了ApplicationListener接口的监听器bean,这些监听器是注册到ApplicationEventMulticaster中的
- finishBeanFactoryInitialization(beanFactory):实例化所有剩余的(非延迟初始化)单例的Bean
- finishRefresh():完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent
- obtainFreshBeanFactory()方法中新建一个工厂时,通过refreshBeanFactory()方法中的loadBeanDefinitions()完成了bean的注册,也就是将BeanDefinition存放到工厂中。先是创建XmlBeanDefinitionReader 然后设置resource和sax解析器,通过doLoadBeanDefinitions()方法把resource转成inputStream,再通过doLoadDocument通过inputStream和resource去加载Document对象。
- 再通过registerBeanDefinitions方法将document和resource去注册bean,方法中直接创建一个BeanDefinitionDocumentReader 对象调用子类的doRegisterBeanDefinitions()方法,该方法中将通过parseBeanDefinitions方法将document转换成 Element对象,然后通过element拿到所有的node,就是一个个的<bean>标签,然后调用processBeanDefinition()方法,将Element和bean的解析器传入进去,通过parseBeanDefinitionElement方法获取BeanDefinitionHolder对象,然后通过BeanDefinitionReaderUtils的registerBeanDefinition方法传入BeanDefinitionHolder对象和注册的策略去注册bean,最终将BeanDefinition存入到DefaultListableBeanFactory工厂中的beanDefinitionMap中以beanName为key,BeanDefinition为value,注册bean的过程完成。
- finishBeanFactoryInitialization方法实例化bean,最终会调用到DefaultListableBeanFactory类中preInstantiateSingletons方法,方法先拿到所有的beanName 然后判断是否是抽象类 是否是单例 是否是迫切加载,然后调用getBean()方法,最终会调用doGetBean方法,方法中通过getSingleton(beanName)方法 到三级缓存中 看是否已经有这个bean了。如果不存在会调用createBean方法,方法中先将beanName存入到一个表示正在创建对象的set中,用于后续判断是否循环依赖,然后通过createBeanInstance()获取到构造器,再根据实例化策略,通过BeanUtils.instantiateClass()构造器对象。
- 对象构造完成后,将为了解决循环依赖,将bean的构造器工厂放入到三级缓存中。
- 然后调用populate方法 去赋值 如果直接就是一个值的话 就通过反射调用set方法赋值 如果是一个对象的话 那么会到缓存池(三级缓存)中获取 拿不到就创建 。
- 然后会调用BeanPostProcessor的前置处理器,对于@Autowired和@Transcational就是基于BeanPostProcessor来实现的。
- 接着会看Bean是否实现InitializingBean (初始化bean接口),如果有会触发其afterPropertiesSet方法的调用。
- 接着是调用我们自定义的bean的init-method方法,此时会调用执行。
- 然后是调用BeanPostProcessor的后置处理。
BeanFactory-框架基础设施
BeanDefinitionRegistry注册表:Spring 配置文件中每一个节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册 BeanDefinition 对象的方法。
BeanFactory 顶层接口:位于类结构树的顶端 ,它最主要的方法就是 getBean(StringbeanName),该方法从容器中返回特定名称的 Bean,BeanFactory 的功能通过其他的接口得到不断扩展:
ListableBeanFactory:该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 Bean 的个数、获取某一类型 Bean的配置名、查看容器中是否包括某一 Bean 等方法;
HierarchicalBeanFactory父子级联:父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器; 通过HierarchicalBeanFactory 接口, Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。Spring 使用父子容器实现了很多功能,比如在Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。
ConfigurableBeanFactory:是一个重要的接口,增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法;
AutowireCapableBeanFactory 自动装配:定义了将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法;
SingletonBeanRegistry运行期间注册单例Bean:定义了允许在运行期间向容器注册单实例 Bean 的方法;对于单实例( singleton)的 Bean 来说,BeanFactory 会缓存 Bean 实例,所以第二次使用 getBean() 获取 Bean 时将直接从 IoC 容器的缓存中获取 Bean 实例。Spring 在DefaultSingletonBeanRegistry 类中提供了一个用于缓存单 实例 Bean 的缓存器,它是一个用 HashMap 实现的缓存器,单实例的 Bean 以 beanName 为键 保存在这个 HashMap 中。
ApplicationContext 面向开发应用
ApplicationContext 由 BeanFactory 派生而来,提供了更多面向实际应用的功能。
ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础
上,还通过多个其他的接口扩展了 BeanFactory 的功能:
- ClassPathXmlApplicationContext:默认从类路径加载配置文件
- FileSystemXmlApplicationContext:默认从文件系统中装载配置文件
- ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事
件、关闭事件等。 - MessageSource:为应用提供 i18n 国际化消息访问的功能;
- ResourcePatternResolver : 所 有 ApplicationContext 实现类都实现了类似于
PathMatchingResourcePatternResolver 的功能,可以通过带前缀的 Ant 风格的资源文
件路径装载 Spring 的配置文件。 - LifeCycle:该接口是 Spring 2.0 加入的,该接口提供了 start()和 stop()两个方法,主要
用于控制异步处理过程。在具体使用时,该接口同时被 ApplicationContext 实现及具体
Bean 实现, ApplicationContext 会将 start/stop 的信息传递给容器中所有实现了该接
口的 Bean,以达到管理和控制 JMX、任务调度等目的。 - ConfigurableApplicationContext 扩展于 ApplicationContext,它新增加了两个主要
的方法: refresh()和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下
文的能力。在应用上下文关闭的情况下调用 refresh()即可启动应用上下文,在已经启动
的状态下,调用 refresh()则清除缓存并重新装载配置信息,而调用 close()则可关闭应用
上下文。
WebApplication 体系架构
WebApplicationContext 是专门为 Web 应用准备的,它允许从相对于 Web 根目录的
路径中装载配置文件完成初始化工作。从 WebApplicationContext 中可以获得
ServletContext 的引用,整个 Web 应用上下文对象将作为属性放置到 ServletContext
中,以便 Web 应用环境可以访问 Spring 应用上下文。
IOC启动流程
IOC启动流程分为4个阶段:容器创建 -> 配置加载 -> Bean的解析 -> Bean的注册
1.ClasspathXmlApplicationContext 容器:
spring启动入口, 通过它来加载一个配置;
//加载Spring配置文件,拿到Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("配置文件.xml")
//从容器中拿到对象实例
MyBean myBean = context.getBean(MyBean.class);
进入ClassPathXmlApplicationContext构造器可以看到,该构造器接收一个配置文件,构造器的注释是这一样描述的:创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义并自动刷新上下文。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//调用父类的构造器
super(parent);
//设置位置文件地址
setConfigLocations(configLocations);
if (refresh) {
//刷新容器【重点】
refresh();
}
}
在ClasspathXmlApplication的构造器中做了如下事情:
- 调用了父容器的构造器方法,目的是加载设置Bean的资源加载器 ResourcePatternResolver
- 然后通过setConfigLocations方法保存好配置文件地址,
- 最后调用refresh()刷新容器
1.1调用了父容器的构造器方法,加载ResourcePatternResolver 资源加载器
ResourcePatternResolver是Bean的资源加载器 ,通过父容器 AbstractApplicationContext 中的构造方法创建:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
//加载 resourcePatternResolver
this();
//
setParent(parent);
}
/**
* Create a new AbstractApplicationContext with no parent.
*/
//创建一个AbstractApplicationContext容器工厂,并构建一个ResourcePatternResolver
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
//获取 PathMatchingResourcePatternResolver
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
//资源加载器
this.resourceLoader = resourceLoader;
}
父容器AbstractApplicationContext 继承了 DefaultResourceLoader ,拥有资源加载的能力,在构造器中中创建了ResourcePatternResolver,使用的是PathMatchingResourcePatternResolver作为实现,它能够将指定的资源位置路径解析为一个或多个匹配的资源。
ResourceLoader源码
public interface ResourceLoader {
//默认从classpath中加载资源文件
/** Pseudo URL prefix for loading from the class path: "classpath:". */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
//把资源文件转换成Resource
Resource getResource(String location);
ClassLoader getClassLoader();
}
public interface ResourcePatternResolver extends ResourceLoader {
//从classpath加载资源
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
//把文件转换成Resource[] ,对ResourceLoader做了扩展
Resource[] getResources(String locationPattern) throws IOException;
}
1.2setConfigLocations 保存配置地址
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean {
//地址保存到这里
@Nullable
private String[] configLocations;
/**
* Set the config locations for this application context.
* <p>If not set, the implementation may use a default as appropriate.
*/
//可以传入多个配置
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
1.3Refresh() 刷新容器
ClasspathXmlApplication调用 AbstractApplicationContext#refresh 方法刷新容器,该方法中实现了IOC容器的整个初始化过程。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备刷新工作 ,记录开始时间,初始化属性,校验配置文件,准备事件的存储Set
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//告诉子类,刷新Bean工厂,销毁旧beanFactory,创建新beanFactory,默认DefaultListableBeanFactory
//从子容器的refreshBeanFactory方法中载入Bean的资源文件
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//准备工厂,配置工厂的下文特性, 例如上下文的 ClassLoader 和后处理器。Bean表达式解析器,
//BeanPostProcessor和 Aware类的自动装配等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//BeanFactory初始化完成的后置工作,这是一个空方法,留给三方框架或者自己配置,作用是允许对beanFoctory进行扩展处理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//调用BeanFactory的后置处理器BeanFactoryPostProcessor,在 bean定义注册之后bean实例化之前调用
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册Bean的后置处理器BeanPostProcessor,在Bean初始化前,后执行
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化信息源,国际化相关
initMessageSource();
// Initialize event multicaster for this context.
//初始化容器事件传播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//空方法,该方法子类实现,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
onRefresh();
// Check for listener beans and register them.
//注册事件监听器,注册实现了ApplicationListener接口的监听器bean,
//这些监听器是注册到ApplicationEventMulticaster中的
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//实例化所有剩余的(非延迟初始化)单例的Bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
//销毁已经创建的单例Bean。
destroyBeans();
// Reset 'active' flag.
//取消容器刷新
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...
//重置缓存
resetCommonCaches();
}
}
}