SpringBean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用,其相互关系如下:
其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是最终的默认实现类是DefaultListableBeanFactory,他实现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如ListableBeanFactory接口表示这些Bean是可列表的,而HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。AutowireCapableBeanFactory接口定义Bean的自动装配规则。这三个接口共同定义了Bean的集合、Bean之间的关系、以及Bean的行为。
BeanFactory源码如下:
public interface BeanFactory {
//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";
//根据bean的名字,获取在IOC容器中得到bean实例
Object getBean(String name) throws BeansException;
//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name. Object...args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean corrtainsBean(String name);
//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String nane) throws NoSuchBeanDefinitionException;
boolean isPrototype(String nane) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String nane, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String nane, @Nullable Class<?> typeToMatch) throws NoSuc hBeanDefinit ionExcept ion;
//得到bean实例的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String nane);
在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的Bean是如何定义怎样加载的。正如我们只关心从工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口并不关心。 而要知道工厂是如何产生对象的,需要看具体的IOC容器实现,Spring提供了许多IOC容器的实现。比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是针对最基本的IOC容器的实现,这个IOC容器可以读取XML文件定义的BeanDefinition(XML文件中对bean的描述),如果说XmlBeanFactory是容器中的屌丝,ApplicationContext应该算容器中的高帅富。ApplicationContext是Spring提供的一个高级的IOC容器,它除了能够提供IOC容器的基本功能外,还为用户提供了以下的附加服务:
SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承体系如下:
Bean的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过下图中的类完成:
IOC容器的初始化包括BeanDefinition的Resource定位、载入和注册这三个基本的过程。以ApplicationContext为例,ApplicationContext系列容器是最常用的,因为Web项目中使用的XmlWebApplicationContext就属于这个继承体系,还有ClasspathXmlApplicationContext等,其继承体系如下图所示:
ApplicationContext允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于Bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的Bean定义环境。
以FileSystemXmlApplicationContext为例,看一下初始化流程:
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
//实际都是在调用这个构造函数
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
通过分析FileSystemXmlApplicationContext的源代码可以知道,在创建FileSystemXmlApplicationContext容器时,构造方法做以下两项重要工作:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
//静态初始化块,在整个容器创建过程中只执行一次
static {
// 为了避免应用程序在Weblogic8.1关闭时出现类加载异常加载问题,加载IOC容
// 器关闭事件(ContextClosedEvent)类
ContextClosedEvent.class.getName();
}
//super方法一直传递到这里
public AbstractApplicationContext(ApplicationContext parent) {
this();
setParent(parent);
}
// 第一步调用空构造,进一步调用getResourcePatternResolver方法
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
//获取一个Spring Source的加载器用于读入Spring Bean定义资源文件
protected ResourcePatternResolver getResourcePatternResolver() {
//AbstractApplicationContext 继承 DefaultResourceLoader,因此也是一个资源加载器
//Spring资源加载器,其getResource(String location)方法用于载入资源
return new PathMatchingResourcePatternResolver(this);
}
/** 省略很多代码*/
}
AbstractApplicationContext构造方法中调用PathMatchingResourcePatternResolver的构造方法创建Spring资源加载器:
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
//设置Spring的资源加载器
this.resourceLoader = resourceLoader;
}
在设置完容器的资源加载器之后,接下来FileSystemXmlApplicationContext执行setConfigLocations方法,通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定位,该方法的源码如下:
//处理单个资源文件路径为一个字符串的情况
public void setConfigLocation(String location) {
//String CONFIG_LOCATION_DELIMITERS = ", ; \t\n”;
//即多个资源文件路径之间用", ; \t\n”分隔,解析成数组形式
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}
//解析Bean定义资源文件的路径,处理多个资源文件字符串数组
public void setConfigLocations(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++) {
// resolvePath 同一个类中将字符串解析为路径的方法
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
也就是说既可以使用一个字符串来配置多个SpringBean定义资源文件,也可以使用字符串数组,即下面两种方式都是可以的: ClasspathResource res=new ClasspathResource(“a.xml,b.xml,……”);多个资源文件路径之间可以是用”,;\t\n”等分隔。 ClasspathResource res=new ClasspathResource(newString[]{“a.xml”,”b.xml”,……}); 至此,SpringIOC容器在初始化时将配置的Bean定义资源文件定位为Spring封装的Resource。
接下来,FileSystemXmlApplicationContext执行refresh函数,SpringIOC容器对Bean定义资源的载入是从refresh()函数开始的,refresh()是一个模板方法,refresh()方法的作用是:在创建IOC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IOC容器。refresh的作用类似于对IOC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。
FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IOC容器对Bean定义的载入过程:
public void refresh throws BeansExceptlon, IllegalStateException (
synchronized (this.startupShutdownMonitor) (
//调用容器准备刷新的方法,获取容器的当时时间.同时给容器设置同步标识
prepareRefresh();
//告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
//子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtalnFreshBeanFactory();
//为BeanFactory配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
//为容器的某些于类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
//调用所有注册的 BeanFactoryPostProcessor 的 Bean
InvokeBeanFactoryPostProcessors(beanFactory);
//为BeanFactory注朋BeanPost事件处理悬.
//BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
//初始化信息源,和国际化相关.
lnitMessageSource();
//初始化容器事件传播器.
InitAppllcationEventHulticaster();
//调用子类的某些特殊Bean初始化方法
onRefresh();
//为事件传播器注明事件监听器.
registerListeners();
//初始化所有剩余的单例Bean
flnishBeanFactorylnitialization(beanFactory);
//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isMamEnabled() {
logger.wam("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex);
}
//x销毁已创建的Bean
destroySeans();
// 取消refresh 操作,重置容器的同步标识.
cancelR«fresh(ex);
throw ex;
}
finally {
resetComonCaches ();
}
}
refresh()方法主要为IOC容器Bean的生命周期管理提供条件,SpringIOC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中“ConfigurableListableBeanFactory beanFactory=obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3raoq7cjs82so