上次我解释了关于BeanFactory的一些功能。那么以BeanFactory的一个实现类为例,我们来看一下BeanFactory的工作原理。
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
这是关于XmlBeanFactory的构造方法,从这里我们可以看到,我们需要自己定义Resource资源来创建这样一个IOC容器。总体而言这样的构造方法我们可以用编程的方式来声明出来
ClassPathResource res = new ClassPathResource("beam.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
在使用IOC容器之前我们需要以下几个步骤
1.创建IOC容器配置文件的抽象资源,这个抽象资源包括了BeanDefinition的定义信息
2.创建一个BeanFactory,这里使用的是DefaultListableBeanFactory
3.创建一个BeanDefinition的读取器,并设置入参factory(对应的DefaultListableBeanFactory)表示读取到的bean都要最终注册到这个factory中
4..这个时候使用读取器来读取我们的resource资源并最终将resource注册到factory
接下来来说一下关于ApplicationContext的特点
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver
我们可以看到实际上ApplicationContext也是继承了BeanFactory的一些实现类,因此我们可以认为ApplicationContext也是一个BeanFactory,但是它有一些自己的特点。
1.支持不同的信息源。
2.访问资源,体现在对ResourceLoader和Resource的支持上,这样我们可以从不同地方法得到Bean资源。
3.支持应用事件。继承了接口ApplicationEventPublisher,这样在上下文中引入了事件机制。
4.在ApplicationContext中提供的附加服务。这些服务使得基本IOC容器的功能更丰富。
接下来我们来看一下关于IOC容器的初始化
IOC容器的初始化包括BeanDefinition的Resource定位,载入和注册三个基本过程。现在我主要来讲一下关于BeanDefinition的Resource定位过程。
我们以org.springframework.context.support.FileSystemXmlApplicationContext为例
首先来看这个类的继承关系图
首先我们来看这个类的构造方法
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
这个方法如果有父类容器,我们设置加载父类的容器,调用父类的有参构造方法。
之后setConfigLocations(configLocations);设置我们的xml文件的地址,这个方法是在一个配置加载类中
org.springframework.context.support.AbstractRefreshableConfigApplicationContext.setConfigLocations(String...)。调用的是父类的方法。之后它要进行容器的更新调用的方法是
org.springframework.context.support.AbstractApplicationContext.refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
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();
}
}
}
这个方法实现了BeanDefinition的Resource定位,载入和注册。首先来看关于Resource的定位,在
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
代码中的obtainFreshFactory()方法中,我们可以看到refreshBeanFactory()方法,但是这个方法是一个抽象方法,需要它的子类来实现,这个方法实现是在
org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()中进行实现
在这个方法中,有这样的一行代码,将BeanDefinition加载到factory中
loadBeanDefinitions(beanFactory);
由于这个方法是一个抽象方法,我们来看这个AbstractRefreshableApplicationContext的子类
org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory)对方法的具体的实现
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
我们可以看到这个方法里面有两行代码和我们前面的编程式初始化IOC容器很相近
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
loadBeanDefinitions(beanDefinitionReader);
这个地方是使用读取器来读取beanDefinition,但是BeanDefinition的读取方式有两种,一种是根据configLocations读取,另一种是根据resources进行读取,对于FileSystemXmlApplicationContext类来说,很显然由于构造方法里面的
setConfigLocations(configLocations);
这样一条语句,所以是根据configLocations,也就是路径获取xml文件来构造beanfinition。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
之后我们再回到最开始的配置加载类中查找我们之前配置好的configLocations
org.springframework.context.support.AbstractRefreshableConfigApplicationContext.getConfigLocations()
protected String[] getConfigLocations() {
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}
这就是关于BeanDefinition的Resource资源定位,关于载入和注册我将会在之后的阅读学习中继续谈到。