概述
前面我们通过一个SSMdemo熟悉了一下Spring的工作环境。IOC是Spring框架的一个重要特性,实现IOC的关键是bean,而更关键的是如何bean的管理容器,也就是BeanFactory,现在我们开始从容器模块解读源码。
BeanFactory
我们先通过一个示例来看看BeanFactory如何工作的:
在前面的最后有个例子通过ClassPathXmlApplicationContext解析配置文件,并获取bean。
public static void main(String[] args) throws ClassNotFoundException {
//XmlBeanFactory已经被废弃
//XmlBeanFactory使用方式如下:
//BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
//这里使用了ClassPathXmlApplicationContext
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
SysAdminUserService s = (SysAdminUserService)context.getBean(SysAdminUserService.class);
List<SysAdminUser> list = s.selectAll(new SysAdminUser());
list.stream().forEach(System.out::println);
}
先来查看XmlBeanFactory的类图,如下:
再查看ClassPathXmlApplicationContext的类图,如下:
从中可以看到XmlBeanFactory、ClassPathXmlApplicationContext的父类实现的接口中,最顶层都是BeanFactory。
BeanFacotry,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂,在spring-beans包中,它是IOC容器的核心接口,是Spring里面最底层的接口,提供了最简单的容器的功能。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。BeanFactory在启动的时候不会去实例化Bean,有从容器中拿Bean的时候才会去实例化
ApplicationContext接口,应用上下文,在spring-context包中,也是一个IOC容器,比BeanFacotry更高级,它由BeanFactory接口派生而来。ApplicationContext包含BeanFactory的所有功能,并提供了更多其他有用的功能,通常建议使用ApplicationContext优先于BeanFactory。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化。ClassPathXmlApplicationContext类就是其中一个实现ApplicationContext接口的类,类似的类比较常用的还有AnnotationConfigApplicationContext类,前者是根据xml配置文件启动,后者则根据注解配置启动。
我们先看XmlBeanFactory的加载方式,对XmlBeanFactory的讲解不会过多深入,因为这个类已经被废弃,更多的会聚焦和ApplicationContext接口相关的类。
demo源码
//通过XmlBeanFactory来创建BeanFactory 实例
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
执行结果如下:
跟踪源码进入XmlBeanFactory类
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
//loadBeanDefinitions
this.reader.loadBeanDefinitions(resource);
}
}
可以看到在XmlBeanFactory的构造方法中,创建了XmlBeanDefinitionReader对象,并调用了loadBeanDefinitions方法,其具体源码如下
//XmlBeanDefinitionReader中
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//校验Resource参数
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
//获取Resource的InputSteam流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//通过InputSteam构造InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//执行doLoadBeanDefinitions方法
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
从上面源码中,我们继续跟踪doLoadBeanDefinitions方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//通过doLoadDocument方法,传入InputSource和Resource得到一个Document对象
//然后执行registerBeanDefinitions方法去注册各类bean
Document doc = this.doLoadDocument(inputSource, resource);
int count = this.registerBeanDefinitions(doc, resource);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} catch (BeanDefinitionStoreException var5) {
throw var5;
} catch (SAXParseException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
} catch (SAXException var7) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
} catch (ParserConfigurationException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
} catch (IOException var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
} catch (Throwable var10) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
}
}
我们主要看核心部分:registerBeanDefinitions方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
int countBefore = this.getRegistry().getBeanDefinitionCount();
//这里通过DefaultBeanDefinitionDocumentReader对象来执行registerBeanDefinitions
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
查看DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
//获取Document的root元素,即配置文件中的<beans>标签
//然后执行doRegisterBeanDefinitions方法
this.doRegisterBeanDefinitions(doc.getDocumentElement());
}
//执行doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
//委托给BeanDefinitionParserDelegate对象来处理bean的解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
//处理<beans>标签中的profile属性
String profileSpec = root.getAttribute("profile");
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
}
return;
}
}
}
//未实现的模板代码,,应该是考虑子类来实现
this.preProcessXml(root);
//主要看这步Bean的解析
this.parseBeanDefinitions(root, this.delegate);
//未实现的模板代码,应该是考虑子类来实现
this.postProcessXml(root);
this.delegate = parent;
}
从上面的代码继续追踪parseBeanDefinitions方法,此方法未解析beans标签的方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
//获取<beans>标签的子节点如<bean>、<context:component-scan>、<import>等等
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
//默认命名空间的标签
if (delegate.isDefaultNamespace(ele)) {
//解析默认标签
this.parseDefaultElement(ele, delegate);
} else {
//自定义命名空间的标签
delegate.parseCustomElement(ele);
}
}
}
} else {
//自定义命名空间的标签
delegate.parseCustomElement(root);
}
}
这里遍历root节点<beans>标签下的子节点,用命名空间来区分解析各节点元素。
默认命名空间在BeanDefinitionParserDelegate类中定义了,如下:
public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
像<bean>、<import>就是默认的命名空间,<tx:annotation-driven>就是自定义的命名空间
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:jdbc.properties"/>
</bean>
<import resource="classpath:spring-mybatis.xml" />
那我们继续追踪解析默认命名空间中标签的parseDefaultElement方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
//这里解析<bean>标签的方法
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
//processBeanDefinition 处理加工BeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//①这里委托给BeanDefinitionParserDelegate对象来解析Bean标签
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//②核心-注册BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
我们先追踪上面①部分,BeanDefinitionParserDelegate类中的parseBeanDefinitionElement方法,看它是如何解析<bean>标签的:
//BeanDefinitionParserDelegate类中
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取 ID 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取 NAME 属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
// 名称按照 , ; 进行分割
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果没有指定 id,将 name 的第一个值作为 id
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// 默认 null
if (containingBean == null) {
// 检查名字是否唯一,如果 id 重复了,将抛出错误
// 内部 usedNames 是一个 HashSet,将会存储加载过的 name 和 aliases
checkNameUniqueness(beanName, aliases, ele);
}
//前边获取了bean的id、name等属性
//这里才是核心解析方法:parseBeanDefinitionElement
// 解析 bean 标签的其它属性
// 将公共属性放入 AbstractBeanDefinition,具体实现在子类 GenericBeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
// 如果 id 和 name 都是空,那个 spring 会给它生成一个默认的名称
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
//核心解析方法
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute("class")) {
className = ele.getAttribute("class").trim();
}
String parent = null;
if (ele.hasAttribute("parent")) {
parent = ele.getAttribute("parent");
}
try {
//通过BeanDefinitionReaderUtils类的createBeanDefinition方法可以看到创建的是GenericBeanDefinition对象
AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
this.parseMetaElements(ele, bd);
this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
this.parseConstructorArgElements(ele, bd);
this.parsePropertyElements(ele, bd);
this.parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(this.extractSource(ele));
AbstractBeanDefinition var7 = bd;
return var7;
} catch (ClassNotFoundException var13) {
this.error("Bean class [" + className + "] not found", ele, var13);
} catch (NoClassDefFoundError var14) {
this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
} catch (Throwable var15) {
this.error("Unexpected failure during bean definition parsing", ele, var15);
} finally {
this.parseState.pop();
}
return null;
}
这里的步骤:
先获取<bean>标签的id、name属性。名称可能是多个使用逗号进行隔开的,相当于alias标签;
然后调用核心解析方法parseBeanDefinitionElement,获取AbstractBeanDefinition(GenericBeanDefinition)对象;
最后返回new BeanDeifinitionHolder,包含了BeanDefinition信息、bean名称、别名数组。
接下来我们追踪上边②部分,核心部分:注册BeanDefinition,调用了BeanDefinitionReaderUtils类中的registerBeanDefinition方法:
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
registry.registerAlias(beanName, alias);
}
}
}
小结一下
这里通过XmlBeanFactory来创建BeanFactory 实例的过程,就是通过XmlBeanFactory构造方法,开始解析XML配置文件,生成Docuemnt对象,解析Document中的标签,按标签的类型来进行分类解析,有些标签需要委托给BeanDefinitionParserDelegate对象解析,最后将BeanDefinition注册到DefaultListableBeanFactory的beanDefinitionMap中;或者SimpleAliasRegistry中的aliasMap。
不论是使用解析Xml还是使用ComponentScan等解析的BeanDefinition都需要进行注册,这样BeanFactory才能统一管理这些bean。
ApplicationContext
接下来我们解析一下ApplicationContext,这个接口的实现类非常多,不过不管是XmlWebApplicationContext、ClassPathXmlApplicationContext,还是AnnotationConfigApplicationContext,这些都是AbstractApplicationContext的派生类,模板方法refresh方法在该类中定义,核心逻辑也都是在 AbstractApplicationContext 抽象类中,另外还需要分析ConfigurableApplicationContext接口,它继承了ApplicationContext接口和Lifecycle接口(Spring容器的生命周期,比较简单的生命周期,定义了启动,停止、是否在运行中三个方法)。
demo中的代码如下:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
执行结果如下:
跟踪源码进入ClassPathXmlApplicationContext的构造函数:
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
//调用核心方法
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//设置一下传入的xml配置文件路径,赋值给configLocations属性
this.setConfigLocations(configLocations);
if (refresh) {
//核心方法:ApplicationContext初始化
this.refresh();
}
}
先看一下setConfigLocation方法,跟踪源码发现它在AbstractRefreshableConfigApplicationContext类中,源码如下:
public void setConfigLocation(String location) {
this.setConfigLocations(StringUtils.tokenizeToStringArray(location, ",; \t\n"));
}
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) {
//循环处理每个location字符串
this.configLocations[i] = this.resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}
//处理ocation字符串的方法
protected String resolvePath(String path) {
//Spring容器的Environment在这里通过getEnvironment方法初始化
//然后调用resolveRequiredPlaceholders方法处理字符串
return this.getEnvironment().resolveRequiredPlaceholders(path);
}
继续查看getEnvironment方法,它来自AbstractApplicationContext类:
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
//创建一个StandardEnvironment对象
this.environment = this.createEnvironment();
}
return this.environment;
}
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
这里我们获取了一个StandardEnvironment对象,StandardEnvironment类继承自AbstractEnvironment,在开发中可以获取很多环境信息,对项目执行时监控非常有帮助。
查看一下StandardEnvironment类关系图:
可以看到它的父类实现了Enviroment、ConfigurablePropertyResolver接口,它们又继承了PropertyResolver接口。查看接口里的方法可以发现Enviroment跟Spring中各类 profiles处理有关、ConfigurablePropertyResolver跟Spring中的类型转换ConversionService有关、PropertyResolver接口跟Spring中的Property处理有关。
我们项目中经常会使用不同的运行环境,dev,test 或者 prod等,这个时候加载的配置文件和属性应该有所不同,这个时候就需要使用到 Environment 来进行区分。
我们需要了解Spring 环境和属性是由四个部分组成:
- Environment : 环境
- Profile : 配置文件,可以理解为,容器里多个配置组别的属性和 bean,只有激活的 profile,它对应的组别属性和 bean 才会被加载
- PropertySource : 属性源
- PropertyResolver : 属性解析器,这个用途就是解析属性
Profile 相信大家都很熟悉了,在springboot项目里我们会使用spring.profiles.active=dev来指定profile配置文件,Spring中原始的方式是在xml配置文件里设置,如下:
<!-- 测试环境配置文件 -->
<beans profile="test">
<context:property-placeholder location="classpath:test/*.properties, classpath:common/*.properties" />
</beans>
<!-- 生产环境配置文件 -->
<beans profile="prod">
<context:property-placeholder location="classpath:production/*.properties, classpath:common/*.properties" />
</beans>
<!-- 开发环境配置文件 -->
<beans profile="dev">
<context:property-placeholder location="classpath:dev/*.properties, classpath:common/*.properties" />
</beans>
然后使用的时候可以使用以下两种方法:
①在 web.xml 中设置
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
②直接在代码中设置
//容器初始化后
context.getEnvironment().setActiveProfiles("dev");
PropertySources
在StandardEnvironment类中我们可以看到customizePropertySources方法对PropertySources的操作
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource("systemProperties", this.getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
}
MutablePropertySources 的底层是一个CopyOnWriteArrayList,容器初始化之后可以在代码中获取:
((MutablePropertySources)((StandardEnvironment)context.environment).propertySources).propertySourceList
好了,配置信息和环境的部分就讲解到这里。
回到上边,要开始refresh方法了,Bean 的解析和注册都在这里
在设置了配置信息之后,接下来就需要对ApplicationContext进行初始化操作了,也就是执行refresh方法,这个方法在AbstractApplicationContext类中实现:
//AbstractApplicationContext类中
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 1为更新准备上下文
//对系统属性或者环境变量的校验,设定一些标志
this.prepareRefresh();
// 2告诉子类去更新它们的 bean factory
//对xml配置的解析并注册bean到bean factory
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 3对BeanFactory进行各种使用前的扩展准备
this.prepareBeanFactory(beanFactory);
try {
// 模板方法,交给子类实现
//允许子类对BeanFactory进行一些后期扩展
this.postProcessBeanFactory(beanFactory);
// 4 引用执行注册过的beanFactory后处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截bean创建的bean处理器
//这里只是注册,真正调用是在getBean对时候
this.registerBeanPostProcessors(beanFactory);
//初始化消息资源
this.initMessageSource();
//初始化应用消息广播器
this.initApplicationEventMulticaster();
// 模板方法,交给子类实现,允许子类来初始化其他Bean
this.onRefresh();
// 检查并注册监听器
//在所有注册对bean中寻找Lestener bean,注册到消息广播器中
this.registerListeners();
// 5实例化非懒加载的单例
this.finishBeanFactoryInitialization(beanFactory);
//完成刷新过程
//通知生命周期处理器刷新过程,发送广播事件
//保证对应的监听器可以做进一步的逻辑处理
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
整个过程可以按代码中的注释下来,比较清晰。
- 注释1,对系统属性或者环境变量的校验,设定一些标志。这步比较简单具体看validateRequiredProperties方法,它具体在AbstractPropertyResolver这个类中实现,主要是遍历属性,空的话记录异常。
- 注释2,获取bean factory,即bean容器,并注册bean,具体看obtainFreshBeanFactory方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//BeanFactory在这步创建,并解析注册bean
//具体在AbstractRefreshableApplicationContext类中实现该方法
this.refreshBeanFactory();
//获取BeanFactory
return this.getBeanFactory();
}
跟踪代码进入AbstractRefreshableApplicationContext类的refreshBeanFactory方法
protected final void refreshBeanFactory() throws BeansException {
//如果存在bean容器了,清理掉,并关闭已有的bean容器
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
//创建一个DefaultListableBeanFactory对象,它就是新的bean容器
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
//对BeanFactory的扩展,添加了@Qualifier和@Autowired的支持
this.customizeBeanFactory(beanFactory);
//这里就是注册bean了,跟之前分析BeanFactory的时候类似
this.loadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
这一步非常重要,主要过程如下:
①先判断已有容器,如果存在,则清理掉,并关闭。
②创建一个DefaultListableBeanFactory对象,它就是新的bean容器
③设定序列号ID
④自定义BanFactory
⑤开始加载,即bean注册(核心方法)
⑥使用全局变量beanFactoryMonitor,记录BeanFactory实例
这里具体解析一下步骤⑤,loadBeanDefinitions方法在AbstractXmlApplicationContext类中实现
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建一个XmlBeanDefinitionReader 对象
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
// 最终委托给XmlBeanDefinitionReader来执行loadBeanDefinitions
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
// 最终委托给XmlBeanDefinitionReader来执行loadBeanDefinitions
reader.loadBeanDefinitions(configLocations);
}
}
通过上面的源码我们可以看到AbstractRefreshableApplicationContext类中的loadBeanDefinitions方法为模板方法,交给子类AbstractXmlApplicationContext来实现,它通过创建XmlBeanDefinitionReader对象,来执行XmlBeanDefinitionReader对象的loadBeanDefinitions方法,这里就跟之前介绍的BeanFactory加载注册bean步骤一样。
最后可以通过getBeanFactory()来获取BeanFactory。
※扩展说明:这样AbstractApplicationContext就可以全部委托getBeanFactory方法来完成bean的管理配置,如下:
//类似的方法都是通过委托getBeanFactory来实现的
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
那这个getBeanFactory方法,在AbstractApplicationContext中只是个模板方法,具体由其子类AbstractRefreshableApplicationContext实现:
//看,这里的beanFactory就是上面步骤6里面设置的BeanFactory实例
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized(this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext");
} else {
return this.beanFactory;
}
}
}
好了,obtainFreshBeanFactory方法解析完了,现在知道AbstractApplicationContext为啥具备bean管理配置的能力了吧?那继续看上面的注释3
注释3,prepareBeanFactory方法,准备类加载器的环境,对前面获取到的 beanFactory进行各类扩展设置。包括 ClassLoader类加载器, post-processors后处理等的设置,增加对SPEL语言的支持、将相关环境变量及属性注册以单例模式注册等内容。
注释4,invokeBeanFactoryPostProcessors
实例化并调用所有注册的 BeanFactoryPostProcessor,这些是后处理器,处理类型是 BeanFactory, ApplicationContext容器允许在实例化 bean 前,读取 bean 信息和修改它的属性。就是说在实例化前,给用户最后一次机会去修改 bean 信息。(Spring里还有很多这类PostProcessor后置处理器,比如BeanPostProcessor等等,还是比较重要的,这里先不展开讲这些后处理器。)注释5,finishBeanFactoryInitialization
完成 bean 容器的初始化,实例化所有剩余的(非非延迟加载)单例的bean
/**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
这里看到freezeConfiguration方法,是对bean定义的冻结,注册后的bean不可改了;preInstantiateSingletons方法就是实例化这些bean,这就是所谓的ApplicationContext容器初始化时,默认会将所有的单例bean提前进行初始化,就在这个方法。和BeanFactory不同,BeanFactory中的bean是在获取的时候才初始化。
查看PreInstantiateSingletons方法源码:
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList(this.beanDefinitionNames);
Iterator var2 = beanNames.iterator();
while(true) {
String beanName;
Object bean;
do {
while(true) {
RootBeanDefinition bd;
do {
do {
do {
if (!var2.hasNext()) {
var2 = beanNames.iterator();
while(var2.hasNext()) {
beanName = (String)var2.next();
//注释:获取单例bean的实例
Object singletonInstance = this.getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, this.getAccessControlContext());
} else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
return;
}
beanName = (String)var2.next();
bd = this.getMergedLocalBeanDefinition(beanName);
} while(bd.isAbstract());
} while(!bd.isSingleton());
} while(bd.isLazyInit());
if (this.isFactoryBean(beanName)) {
bean = this.getBean("&" + beanName);
break;
}
this.getBean(beanName);
}
} while(!(bean instanceof FactoryBean));
FactoryBean<?> factory = (FactoryBean)bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
SmartFactoryBean var10000 = (SmartFactoryBean)factory;
((SmartFactoryBean)factory).getClass();
isEagerInit = (Boolean)AccessController.doPrivileged(var10000::isEagerInit, this.getAccessControlContext());
} else {
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
}
if (isEagerInit) {
this.getBean(beanName);
}
}
}
最后,注册的最后一步是在finally代码块中的resetCommonCaches方法,用来清除缓存。
好了,以上就是ApplicationContext容器初始化的步骤。
通过BeanFactory容器和ApplicationContext容器的初始化步骤对比,可以知道两者的区别了吗?
总结
这里通过debug跟踪不同的容器初始化的步骤,这只是Spring的开始,通过对比知道两者的联系和区别。
BeanFactory是Spring IOC的基础,它是所有容器接口的父类。
ApplicationContext接口是BeanFactory的子类,对BeanFactory进行了扩展,使得ApplicationContext具有更多的功能;
ApplicationContext容器的初始化中包含了BeanFactory的初始化以及对BeanFactory的扩展,它通过getBeanFactory方法,把bean配置管理委托给BeanFactory;
ApplicationContext是初始化的时候就初始化所有的单例bean,BeanFactory中的bean是在获取的时候才初始化;
ApplicationContext增加了SPEL语言的支持(#{xx.xx}等配置)、 消息发送、响应机制(ApplicationEventPublisher)、支持了@Qualiiar和@Autowired等注解。
※细心的同学可能发现在上面的执行图中,BeanFactory里的bean定义数量有16个,而ApplicationContext立却有17个。原因就在于ApplicationContext容器对BeanFactory进行了扩展,它所注册的所有后处理器会遍历执行。MapperScannerConfigurer就是其中一个PostProcessor,而BeanFactory却没有这个功能。