我们知道,Spring框架基于IOC容器能够实现依赖注入,使代码之间不在具有高度的耦合关系.解耦给问我们带来很多的好处,不仅会使我们的代码更加容易扩展维护,同时也更加方便测试。
在Spring中,我们常使用ClassPathXmlApplicationContext(类路径下读取配置),XmlWebApplicationContext(web环境下加载配置),AnnotationConfigApplicationContext(基于注解的配置读取),FileSystemXmlApplicationContext(基于文件系统中的配置读取)。而本文,将以ClassPathXmlApplicationContext为切入点来进行分析。整个入口也很简单,只有一行代码,如下:
ClassPathXmlApplicationContext bb = new ClassPathXmlApplicationContext("beanConf.xml");
读取beanConf.xml配置文件,从而持有IOC容器,我们来看看Spring底层做了一些什么。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
//调用父类的构造器 获得资源加载器
super(parent);
//对资源文件的定位
setConfigLocations(configLocations);
if (refresh) {
//容器的刷新 ,核心所在
refresh();
}
}
在super(parent)中,实际调用的为AbstractApplicationContext的构造方法。
public AbstractApplicationContext() {
//获取资源加载器
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
//生成一个加载器
return new PathMatchingResourcePatternResolver(this);
}
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
而setConfigLocations(configLocations)中,设置配置资源。
public void setConfigLocation(String location) {
多个配置文件可以以,;以及\t\n分割开。
setConfigLocations(StringUtils.tokenizeToSringArray(location, CONFIG_LOCATION_DELIMITERS));
}
public void setConfigLocations(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;
}
}
在此方法中 ,我们可以看到多个配置文件可以用,;以及\t\n分割,同时配置文件也可以以数组的方式传入:new String[] {configLocation}。
下面再来看看refresh容器的刷新做了什么。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//一:为容器的刷新做准备,
//1:设置容器的启动时间,同步标志。
//2:initPropertySources :初始化属性设置,子类来自定义实现。
//3:validateRequiredProperties:属性的校验
//4:this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); 早期事件的初始化。
prepareRefresh();
//二:根据配置获取刷新的BeanFactory。此方法主要涉及到对配置文件的 定位,解析以及注册。
//1:定位包括从类路径,文件系统,url完成资源的定位。
//2: 解析主要为对XML文件的解析,由配置文件解析为BeanDefinitions
//3: 注册即把对应bean的信息存在在map中。
// 这一步也BeanFactory初始化的过程,此方法完成后则对应的bean的信息都保存在BeanDefinitions中,bean实例化的时候就是基于解析出的bean的定义信息来完成的.此步骤也是很关键的,将在下一篇中详细进行描述。
[Spring源码分析之BeanFactory初始化](https://blog.csdn.net/qq_28051453/article/details/84946397)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//三:BeanFactory的配置工作。
//1:设置类加载器,表达式解析器。
//2:添加部分beanPostProcessor等等
prepareBeanFactory(beanFactory);
try {
//四:自定义后置BeanFactory的配置。子类重写的此方法可以自定义一些配置的装载,个性化的设置。
postProcessBeanFactory(beanFactory);
//五:执行BeanFactoryPostProcessors(BeanFactory的后置处理器)
//1:执行BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的方法。这里应该注意的是BeanDefinitionRegistryPostProcessor继承于BeanFactoryPostProcessor。执行的顺序为先执行BeanDefinitionRegistryPostProcessor中的方法,后执行BeanFactoryPostProcessor。
//2:具体执行的顺序为:先执行PriorityOrdered类型的,再执行Ordered类型的,最后执行其他的。
//3:自定义bean实现BeanFactoryPostProcessor或者BeanDefinitionRegistryPostProcessor接口,实现其方法,则可以对bean工厂进行操作。
invokeBeanFactoryPostProcessors(beanFactory);
//六:注册Bean的后置处理器 监听容器所触发的相关事件
registerBeanPostProcessors(beanFactory);
//七:初始化MessageSource相关的组件(国际化,消息的绑定与解析的功能)
//1:获取beanFactory,判断容器中是否有MessageSource组件,没有的话则创建一个并注册到容器中。
initMessageSource();
//八:初始化事件的派发器。
//获取beanFactory,得到applicationEventMulticaster,将创建好的注册到容器中。
initApplicationEventMulticaster();
//九:容器的刷新,留给子类自定义的实现。
onRefresh();
//十:注册事件的相关监听器
//1:拿到容器中所有的ApplicationListeners,为每个事件添加监听器。
registerListeners();
//十一:初始化所有非懒加载的单例bean,核心。这个方法即是bean的实例化过程。包括读取bean的定义信息,获取bean的依赖关系。实例化bean以及设置。这个方法也将在下篇中详细的描述。
//1:根据bean的定义信息(BeanDefinitions)实例化bean.
//2:populateBean方法给bean的属性赋值。
//3:初始化bean(initializeBean)
//3.1:执行Aware接口的相关方法
//3.2:执行后置处理器初始化前的方法:applyBeanPostProcessorsBeforeInitialization
//3.3:执行初始化方法:invokeInitMethods
//3.4:执行后置处理器初始化后的方法:applyBeanPostProcessorsAfterInitialization
//4:注册bean的销毁方法,注意,这里只是注册。
//5:单例的bean添加至缓存中,下次再次获取则直接从缓存中获取,从而保证是同一个对象。
finishBeanFactoryInitialization(beanFactory);
//十一:完成beanFactory的创建工作,IOC容器创建完成。
//1:初始化容器生命周期相关的后置处理器,发布容器初始化完成的时间。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
//十二:销毁单实例bean
destroyBeans();
//十三:重置容器同步的标识
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
至此,整个IOC容器创建完成。通过以上分析,总结可知,IOC容器其实就相当于一个一个的Map,Map中保存了相关的bean的定义信息以及实例化信息。对于单实例的bean而言,首次获取的时候总是从缓存中获取,没有则再创建,并加载到缓存中。默认的单实例在容器启动的时候创建,在容器关闭的时候销毁。
以下是相关其他Spring源码分析文章
Spring源码分析之BeanFactory初始化