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定位