springioc作为spring核心的两个基础功能之一,对于学习spring的重要性是不言而喻的。
从我平常使用spring的感觉来分析,ioc的初始化主要完成的就是对于我们所配置的bean进行注册并存放到spring容器中来供依赖注入时使用。
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
ClassPathResource resource = new ClassPathResource("test.xml");
// System.out.println(resource.getDescription());
// System.out.println(resource.getPath());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
String[] beanDefinitionNames = factory.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++) {
System.out.println(beanDefinitionNames[i]);
}
testbean test1 = (testbean) factory.getBean("test1");
System.out.println(test1.toString());
BeanDefinition test11 = factory.getBeanDefinition("test1");
System.out.println(test11.isLazyInit());
上面着串代码就是spring容器加载并注册bean的基本流程。
- 创建一个beanfactory,也就是创建一个我们的spring容器,简单的看这个容器可以说就是一个<string,beandefinition>形式的map。
- 创建一个resource,这个resource是spring的一个描述并定位到配置文件的类,他根据配置文件的区别有很多实现类,最常见的就是在classpath下配置xml的classpathresource和filesystemresource
- 加载beandefinition,这一步就是根据上面的resource来定位到这个配置文件并获取他的流根据信息解析配置文件并对所有的bean封装为spring的一个叫做beandefinition的数据结构。
- 注册beandefinition,在这一步会先是将beandefinition压入到一个map中并保存他们的key到一个list里之后在对单例模式的beandefinition进行一次遍历并根据反射创建他们的对象。
ClassPathXmlApplicationContext我们在初学spring时一定会用到的一个基于XML配置的beanfactory,虽然他并没有命名为xxxbeanfactory但是他也是beanfactory接口的实现类,对于beanfactory实现类可以分为两种,一种就是实现了基础ioc功能的xxxbeanfactory,第二种就是在基础功能之上spring还为我们提供了一系列高级方法的xxxcontext他们在实现了beanfactory接口之外还实现了其他接口,来提供一些好用的功能。
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
AbstractRefreshableConfigApplicationContext内的方法
/**
* 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;
}
}
这个是他的构造方法,可以看到它都是调用的一些父类的方法,首先是设置了双亲容器,之后设置容器的一些基本配置,如果没有具体的要求的话会设置默认配置。在后面就是ioc初始化的核心方法了。
AbstractApplicationContext内的方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 执行refresh方法之前的准备,大概就是将一些配置信息进行初始化例如active设置为true,closed设置为false等等.
prepareRefresh();
// 重置内部的beanfactory容器.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置容器的各种配置信息.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
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.
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();
}
}
}
在这个refresh方法中spring完成了创建容器并对于beandefinition的加载和注册。
这里还是建议大家自己去跟着流程跑一遍源码,自己看的话会有一些自己的理解。