1. 准备
编程式Ioc启动
ClassPathResource res = new ClassPathResource( "beans.xml" ); //定位资源
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); //创造个ioc容器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); //创造个读取xml的BeanDefinition读取器
reader.loadBeanDefinitions(res); //用这个读取器去读取resource
|
这是手动启动ioc的流程,这是简单ioc容器,而ApplicationContext为我们提供里一系列加载不同Resources读取器的实现。但是换句话来说,ApplicationContext也是需要上述的流程,只不过它帮我们实现了,其实启动流程就在这里,和这个一样,不过是一个手动,一个自动的区别
2. 开始分析
以FileSystemXmlApplicationContext为例子
启动代码
public class FileSystemXmlApplicationContextStartProcess {
public static void main(String[] args){
FileSystemXmlApplicationContext cxt = new FileSystemXmlApplicationContext( "bean.xml" );
}
}
|
在FileSystemXmlApplicationContext中无论那个构造方法,都均调用这个构造构造方法
FileSystemXmlApplicationContext构造方法
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
//设置双亲容器
super (parent);
//存放入参
this .setConfigLocations(configLocations);
if (refresh) {
//ioc容器启动
this .refresh();
}
}
|
AbstractApplicationContext.refresh方法
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this .startupShutdownMonitor;
synchronized ( this .startupShutdownMonitor) {
this .prepareRefresh();
//定位资源流程主要在这里,得到一个beanFactory,也就是Ioc容器,这也上述手动编程式开启ioc一样需要个ioc容器
ConfigurableListableBeanFactory beanFactory = this .obtainFreshBeanFactory();
this .prepareBeanFactory(beanFactory);
try {
this .postProcessBeanFactory(beanFactory);
this .invokeBeanFactoryPostProcessors(beanFactory);
this .registerBeanPostProcessors(beanFactory);
this .initMessageSource();
this .initApplicationEventMulticaster();
this .onRefresh();
this .registerListeners();
this .finishBeanFactoryInitialization(beanFactory);
this .finishRefresh();
} catch (BeansException var5) {
this .destroyBeans();
this .cancelRefresh(var5);
throw var5;
}
}
}
|
AbstractApplicationContext.obtainFreshBeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//进入这里,这里是构造BeanFactory的地方
this .refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = this .getBeanFactory();
if ( this .logger.isDebugEnabled()) {
this .logger.debug( "Bean factory for " + this .getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
|
AbstractRefreshableApplicationContext.refreshBeanFactory
protected final void refreshBeanFactory() throws BeansException {
//如果存在就先销毁这个BeanFactory
if ( this .hasBeanFactory()) {
this .destroyBeans();
this .closeBeanFactory();
}
try {
//这里是真实创建BeanFactory的地方,创建的是DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = this .createBeanFactory();
beanFactory.setSerializationId( this .getId());
this .customizeBeanFactory(beanFactory);
//这里加载BeanDefinition
this .loadBeanDefinitions(beanFactory);
Object var2 = this .beanFactoryMonitor;
synchronized ( this .beanFactoryMonitor) {
this .beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException( "I/O error parsing bean definition source for " + this .getDisplayName(), var5);
}
}
|
AbstractXmlApplicationContext.loadBeanDefinitions
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//构造一个xml的BeanDfinition读取器 这里也和上述编程式开启ioc一样,需要个读取器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment( this .getEnvironment());
beanDefinitionReader.setResourceLoader( this );
beanDefinitionReader.setEntityResolver( new ResourceEntityResolver( this ));
this .initBeanDefinitionReader(beanDefinitionReader);
//加载BeanDefinition
this .loadBeanDefinitions(beanDefinitionReader);
}
|
AbstractXmlApplicationContext.loadBeanDefinitions
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//如果资源已经定位,那么就走这里
Resource[] configResources = this .getConfigResources();
if (configResources != null ) {
reader.loadBeanDefinitions(configResources);
}
//资源还没定位,那么就还是字符串形式的 上面例子就是bean.xml
String[] configLocations = this .getConfigLocations();
if (configLocations != null ) {
reader.loadBeanDefinitions(configLocations);
}
}
|
AbstractBeanDefinitionReader.loadBeanDefinitions
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null" );
int counter = 0 ;
String[] var3 = locations;
int var4 = locations.length;
//如果是资源文件很多,那么一个一个的加载
for ( int var5 = 0 ; var5 < var4; ++var5) {
String location = var3[var5];
//加载BeanDefinition
counter += this .loadBeanDefinitions(location);
}
return counter;
}
|
AbstractBeanDefinitionReader.loadBeanDefinitions
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//这个获取的是FileSystemXmlApplicationContext本身,因为这个ApplicationContext是扩展了ResourceLoader这个接口的
ResourceLoader resourceLoader = this .getResourceLoader();
if (resourceLoader == null ) {
throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available" );
} else {
int loadCount;
if (!(resourceLoader instanceof ResourcePatternResolver)) {
//调用getResource完成资源定位
Resource resource = resourceLoader.getResource(location);
loadCount = this .loadBeanDefinitions((Resource)resource);
if (actualResources != null ) {
actualResources.add(resource);
}
if ( this .logger.isDebugEnabled()) {
this .logger.debug( "Loaded " + loadCount + " bean definitions from location [" + location + "]" );
}
return loadCount;
} else {
try {
//调用getResource完成资源定位,这里实际上调用的是PathMatchingResourcePatternResolver这个类 这里是ioc初始化的第一部分
Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
//定位好Resource就开始拿到Resources开始loadBeanDefinition 这是ioc初始化的第二部分 和上述编程式开启ioc一样,找到资源后用读取器读取资源
loadCount = this .loadBeanDefinitions(resources);
if (actualResources != null ) {
Resource[] var6 = resources;
int var7 = resources.length;
for ( int var8 = 0 ; var8 < var7; ++var8) {
Resource resource = var6[var8];
actualResources.add(resource);
}
}
if ( this .logger.isDebugEnabled()) {
this .logger.debug( "Loaded " + loadCount + " bean definitions from location pattern [" + location + "]" );
}
return loadCount;
} catch (IOException var10) {
throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]" , var10);
}
}
}
}
|
PathMatchingResourcePatternResolver.getResources
//这个方法主要是就是把资源的String类型表示,变成Resource
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null" );
if (locationPattern.startsWith( "classpath*:" )) {
return this .getPathMatcher().isPattern(locationPattern.substring( "classpath*:" .length())) ? this .findPathMatchingResources(locationPattern) : this .findAllClassPathResources(locationPattern.substring( "classpath*:" .length()));
} else {
//因为是文件资源bean.xml 所以最后执行到这步
int prefixEnd = locationPattern.indexOf( ":" ) + 1 ;
return this .getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this .findPathMatchingResources(locationPattern) : new Resource[]{ this .getResourceLoader().getResource(locationPattern)};
}
}
|
DefaultResourceLoader.getResource
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null" );
if (location.startsWith( "/" )) {
return this .getResourceByPath(location);
} else if (location.startsWith( "classpath:" )) {
return new ClassPathResource(location.substring( "classpath:" .length()), this .getClassLoader());
} else {
try {
URL url = new URL(location);
return new UrlResource(url);
} catch (MalformedURLException var3) {
//因为bean.xml不符合上面任何一个,最后到这里,而这个的实现缺是在FileSystemXmlApplicationContext中实现
return this .getResourceByPath(location);
}
}
}
|
其实在这里可以看出,这可以返回不同的资源文件类型,有classResource,UrlResource
FileSystemXmlApplicationContext.getResourceByPath
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith( "/" )) {
path = path.substring( 1 );
}
//返回文件形式的资源对象
return new FileSystemResource(path);
}
|
至此,Resource定位就到此结束,Ioc初始化的三部分,第一部分已经完成
3. 流程图

4. 总结
将spring的配置文件,也就是bean的xml文件找到的过程,是个寻找资源定位的过程,有可能在classpath,可能在文件系统,也可能是url定位