前面的文章Spring IOC-BeanFactory的继承体系分享了BeanFactory的所有接口,抽象类以及实现类的主要方法及层级关系。今天我们分享BeanFactory的相当于一个分支ApplicationContext。
既然是继承,那么肯定拥有BeanFactory核心功能比如一系列的getBean()功能,不同之处在于扩展了一些功能(比如加载bean定义的方式)和增加了一些功能(比如事件的支持,国际化的支持、消息的支持,应用环境的集合)。而功能的扩展肯定是反映在代码上,我们直接看代码。
首先是源头的ApplicationContext接口:
// 除了包含BeanFactory的所有功能之外,在国际化支持(MessageSource)、资源访问(如URL和文件)(ResourcePatternResolver)、事件传播(ApplicationEventPublisher)等方面进行了良好的支
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
//环境的唯一id
String getId();
//环境的名称
String getDisplayName();
//环境第一次加载的时间
long getStartupDate();
//继承自HierarchicalBeanFactory
ApplicationContext getParent();
//相当于web环境实现的bean工厂
AutowireCapableBeanFactory getAutowireCapableBeanFactory() ;
}
通过代码我们知道bean工厂的功能只是这个接口的一项子功能,特色的地方在于问绕bean工厂可以提供很多很使用的附加功能。
直接子类接口为ConfigurableApplicationContext,看下代码:
//是一个SPI服务提供者接口,提供配置功能的ApplicationContext,可以配置Context进行的环境、添加监听器、PostProcessor处理器、动态刷新、关闭等功能
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle {
void setId(String id);
//配置Context进行的环境
void setEnvironment(ConfigurableEnvironment environment);
void setParent(ApplicationContext parent);
//PostProcessor处理器
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);
//添加监听器
void addApplicationListener(ApplicationListener> listener);
//动态刷新
void refresh() throws BeansException;
void close();
}
好的,这个层级关系的接口已经说完,下面看第一级抽象类AbstractApplicationContext。这个类提供了一些方法的默认实现,其中继承自BeanFactory接口方法都委托这个环境下持有的BeanFactory对象去实现,比如
public Object getBean(String name) throws BeansException {
return getBeanFactory().getBean(name);
}
这个类只是提供ApplicationContext中增加功能的默认实现。比如:
………………
public void setId(String id) {
this.id = id;
}
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
/**
* 负责存放FrameworkServlet中设置进来的监听器,当然只能是子类,因为这是抽象的。
* 当然都可以设置监听器
*/
public void addApplicationListener(ApplicationListener> listener) {
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
else {
this.applicationListeners.add(listener);
}
}
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
//会调用XmlWebApplicationContext.loadBeanDefinitions()方法
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()
// 准备bean工厂
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasse
postProcessBeanFactory(beanFactory);
//对beanFactory对象本身调用在此环境中注册的processors
invokeBeanFactoryPostProcessors(beanFactory);
//注册所有的对bean的processors
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化国际化支持的bean
initMessageSource();
//初始化时间发布器
initApplicationEventMulticaster();
//刷新之后执行的操作
onRefresh();
//注册事件,从bean工厂得到或者这个类设置进来的
registerListeners();
//实例化所有单例的bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
………………
其实这个类的主要的作用就是实现了refresh()方法。
下面我们俩看这个类的几个抽象方法,因为这往往是子类的价值所在:
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
protected abstract void closeBeanFactory();
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
然后看子类具体的实现——AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();//先将所有的单例的bean destroy
closeBeanFactory();//this.beanFactory = null;
}
try {
//实例化一个工厂,这里是DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory(); //org.springframework.web.context.WebApplicationContext:/
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//调用.XmlWebApplicationContext 的方法
//在本类中是抽象方法,需要子类AbstractXmlApplicationContext中实现
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
}
再次找到需要子类实现的方法loadBeanDefinitions(beanFactory);直接找到子类AbstractXmlApplicationContext中实现代码:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory){
………………
//这和在BeanFactory中的加载过程一样,也是委托给XmlBeanDefinitionReader去读取配置文件,
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
………………
//加载bean定义
loadBeanDefinitions(beanDefinitionReader);
………………
}
在来看loadBeanDefinitions()方法:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader)
////getConfigResources()方法的实现再次交给子类,就是读取的配置文件的源——ClassPathXmlApplicationContext
Resource[] configResources = getConfigResources();
if (configResources != null) {
//读取配置文件都是用的这个方法,和BeanFactory是一样的
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
//照顾的是FileSystemXmlApplicationContext类
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
在来看具体子类ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,这个类比较简单,功能就是实现父类的getConfigResources()方法,就是加载读取的资源。加载的过程在构造的时候发生:
public ClassPathXmlApplicationContext(String[] paths, Class clazz, ApplicationContext parent)
throws BeansException {
super(parent);
Assert.notNull(paths, "Path array must not be null");
Assert.notNull(clazz, "Class argument must not be null");
this.configResources = new Resource[paths.length];
for (int i = 0; i < paths.length; i++) {
this.configResources[i] = new ClassPathResource(paths[i], clazz);
}
//加载完资源文件之后就刷新工厂环境
refresh();
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
//简单的将路径设置到父类AbstractRefreshableConfigApplicationContext中
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
下面在补充ClassPathXmlApplicationContext和FileSystemXmlApplicationContext的区别:
ClassPathXmlApplicationContext[只能读放在web-info/classes目录下的配置文件],classpath:前缀是不需要的,默认就是指项目的classpath路径下面;如果要使用绝对路径,需要加上file:前缀表示这是绝对路径;
对于FileSystemXmlApplicationContext,默认表示的是两种,如果要使用classpath路径,需要前缀classpath:
1.没有盘符的是项目工作路径,即项目的根目录;
2.有盘符表示的是文件绝对路径.