一开始想用DefaultListableBeanFactory 跟踪AOP的源代码,发现它不支持AOP,是不支持还是自己配置错了还没搞清楚,抽空要比较一下BeanFactory下的子类的区别。
ApplicationContext和我们之前的解析配置文件和创建bean的有点区别,之前都是采用的延时创建bean,就是当getBean()的时候,才会去实例化指定的bean,而ApplicationContext是解析玩配置文件马上去实例化bean,接下来,让我们去跟着源代码走一遍流程。
这次我们创建的类有如下几个:
配置文件如下:
主要就是想看一下对于AOP是如何解析配置文件和创建实例的。走起!!!
ApplicationContext beanFactory = new ClassPathXmlApplicationContext("beans.xml");
这行代码可是我们这边文章罪恶的源头啊,哈哈哈!这是我们分析代码的入口。
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//调用父类的构造方法
super(parent);
//设置配置文件的路径
setConfigLocations(configLocations);
//看是否需要重新加载配置文件
if (refresh) {
refresh();
}
}
这个构造方法每一步都很重要,我们先看一下设置配置文件的路径:
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容器是ApplicationContext可以一次加载多个配置文件。
Spring刚启动的时候refresh为true,所以需要加载配置文件,进入这个方法去看一下:
synchronized (this.startupShutdownMonitor) {
// 为重新读取配置文件准备环境
prepareRefresh();
// 声明一个内部的IOC容器
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//准备bean factory,以便在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// 允许在上下文子类中对bean工厂进行后期处理。
postProcessBeanFactory(beanFactory);
// 调用上下文中注册为bean的工厂处理器。
invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截bean创建的bean处理器。
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的消息源。
initMessageSource();
//为这个上下文初始化事件多主机。
initApplicationEventMulticaster();
//初始化特定上下文子类中的其他特殊bean。
onRefresh();
// 检查侦听器bean并注册它们
registerListeners();
// 实例化所有剩余的(非延迟-init)单例
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布响应的事件
finishRefresh();
}
catch (BeansException ex) {
...
}
finally {
//在Spring的内核中重置常见的内省缓存,因为我们可能再也不需要单例bean的元数据了
resetCommonCaches();
}
}
}
在这个方法中没有一句废话,需要做什么非常清晰,我们就来一个一个方法的进入去看看到底怎么回事:
protected void prepareRefresh() {
//本次刷新开始的时间
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
...
// 在上下文环境中初始化任何占位符属性源
initPropertySources();
// 验证所有标记为必需的属性都是可解析的
getEnvironment().validate