BeanFactory
概述
从接口命名,我们可以很容易看出,Spring的Ioc核心源码体系是工厂模式。
这里从类图抽象出来的接口层级关系,结合Spring源码对这些接口的注释,我们可以了解到:
BeanFactory是Spring Bean容器的顶层接口,是一个基本的容器视图,在这里只定义了容器的一些最基本的行为。而如ListableBeanFactory、ConfigurableBeanFactory等接口适用于一些比较特殊的场景。Spring的DI(依赖注入)实现是通过BeanFactory接口以及它的子接口来实现。具体的实现方式我们可以在下面了解到,这里不再赘述。
BeanFactory以及它的下级接口
BeanFactory只定义Ioc容器的基本行为。具体的实现由继承BeanFactory的实现类去实现,如XmlBeanFactory、ClasspathXmlApplicationContext等。这些实现类需要尽可能支持一个标准的Bean的生命周期(详细查看Bean生命周期章节)。
BeanFactory只是包装了Spring Ioc容器最基本的行为,但是对于不同场景下,为了能够灵活的描述Bean在实际传递和转化过程中的关系以及限制,在之下还有对应的不同的子类。
HierarchicalBeanFactory:描述Bean的继承关系。
ListableBeanFactory:描述Bean是可列表的。
AutowireCapableBeanFactory:定义Bean的自动装配规则。
由这四个接口共同定义了Bean的集合、Bean之间的关系以及Bean行为。
Ioc容器的初始化
Ioc容器的初始化包括BeanDefinition的Resource定位、载入和注册。
下面会以一个比较简单的容器实现来说明下Ioc容器的初始化。
XmlBeanFactory
public class XmlBeanFactory extends DefaultListableBeanFactory {
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);
}
}
从源码可以看到,构造方法做了以下的事情:
第一:创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition
private final XmlBeanDefinitionReader reader =
new XmlBeanDefinitionReader(this);
第二:加载Resource资源对象,该对象包含了BeanDefinition的信息,完成Bean的载入和注册。
this.reader.loadBeanDefinitions(resource);
FileSystemXmlApplicationContext
FileSystemXmlApplicationContext
源码分析
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
在构造方法里面,可以看到:
首先,调用父类的构造方法为容器设置好资源加载器;
super(parent);
跟到里面可以看到
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
创建完资源加载器之后,接下来setConfigLocations方法定位资源文件。
定位完资源文件后,开始调用refresh()函数载入Bean。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新容器
prepareRefresh();
//通过refreshBeanFactory()重刷beanFactory,如果存在,则destroy
//它,如果不存在,则创建。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为BeanFactory配置容器特性,例如类加载器,事件处理器等
prepareBeanFactory(beanFactory);
try {
// 为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
//注册BeanPostProcessor,此处将结合Bean的生命周期来看
registerBeanPostProcessors(beanFactory);
//初始化信息源
initMessageSource();
// 初始化容器事件传播器
initApplicationEventMulticaster();
// 如果子类有特殊需求,调用特殊化的初始化方法,默认为空
onRefresh();
// 为事件传播器注册事件监听器
registerListeners();
// 装载所有剩余的单态Bean
finishBeanFactoryInitialization(beanFactory);
// 发布容器的生命周期事件
finishRefresh();
}catch (BeansException ex) {
// 销毁已创建的单态Bean
beanFactory.destroySingletons();
//重置active标识,该标识在prepareRefresh()设置
cancelRefresh(ex);
throw ex;
}
}
}
//obtainFreshBeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//子类具体实现refreshBeanFactory
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refreshBeanFactory()可以参考AbstractRefreshableApplicationContext类的实现。
//AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建IoC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//委派模式
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
其他的都挺容易理解的,这里不在赘述,这里说下loadBeanDefinitions(beanFactory)
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建Bean读取器,容器使用该读取器读取Bean定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
//祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
beanDefinitionReader.setResourceLoader(this);
//为Bean读取器设置SAX xml解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
initBeanDefinitionReader(beanDefinitionReader);
//Bean读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取Bean定义资源的定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
AbstractBeanDefinitionReader reader.loadBeanDefinitions
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
counter += loadBeanDefinitions(resource);
}
return counter;
}
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取在IoC容器初始化过程中设置的资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
//加载多个指定位置的Bean定义资源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// 加载单个资源
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
从AbstractBeanDefinitionReader的loadBeanDefinitions方法分析可以知道容器主要做了以下两件事情:
(1)调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到加载的资源。
(2)加载Bean定义。这里通过委派子类的loadBeanDefinition方法来实现。
在这个过程中,有几个点,我比较想关注的:
1.Spring解析多个资源文件时,是如何做到模糊匹配资源路径加载的?
2.如何从资源文件映射程Spring容器所识别的Bean定义?
以下围绕这两点来看下。
关于第一个问题,可以透过这行代码进行分析:
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
关于ResourePatternResolver我们可以解析出他的类层次图。
可以看到,具体的实现是通过PathMatchingResourcePatternResolver
进到里面看它里面的源码