1、通过XML配置文件去初始化容器
我们以xml的方式去初始化去初始化bean,来看看在整个初始化流程中究竟做了什么?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
<bean id="bean1" class="com.zp.example02.TestBeanLoad.Bean1"/>
<!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
<bean id="bean2" class="com.zp.example02.TestBeanLoad.Bean2">
<!-- 依赖注入, 建立与 bean1 的依赖关系 -->
<property name="bean1" ref="bean1"/>
</bean>
</beans>
public class TestBeanLoad {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("a02.xml");
//
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
static class Bean1 {
}
static class Bean2 {
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
}
容器的初始化过程主要通过refresh()
进行初始化的,同样refresh()
方法也是整个初始化流程的算法骨架模板方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//1.为更新准备上下文,设定一些标志
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//2.通知子类刷新内部bean工厂,初始化BeanFactory并进行XML文件的解析、读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//3.准备在此上下文中使用的Bean工厂
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//4.允许在上下文子类中对bean工厂进行后处理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//5.调用beanFactory的后处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//6.注册bean后处理器,在调用getBean的时候回调这些bean后处理器方法
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//7.为上下文初始化Message源
initMessageSource();
// Initialize event multicaster for this context.
//8.初始化事件多播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//9.留给子类初始化其他的bean
onRefresh();
// Check for listener beans and register them.
//10.注册事件监听
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//11.初始化所有的单例bean(非懒惰加载)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//12.发布事件通知
finishRefresh();
}
2、prepareRefresh()
该方法主要做一些容器初始化前的准备工作,为刷新准备这个上下文,设置它的启动日期和活动标志,以及执行任何属性源的初始化。具体的代码逻辑如下:
protected void prepareRefresh() {
// Switch to active.(容器启动时间)
this.startupDate = System.currentTimeMillis();
//设置容器的关闭标志位
this.closed.set(false);
//设置容器开启的标志位
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.(子类扩展点用于存在一些环境参数)
initPropertySources();
// Validate that all properties marked as required are resolvable:& (校验需要的环境参数)
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...(添加刷新前的应用监听器集合)
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...(收集早期的事件监听)
this.earlyApplicationEvents = new LinkedHashSet<>();
}
3、obtainFreshBeanFactory()
该方法是整个启动流程中十分重要的方法,通知子类刷新内部bean工厂,初始化BeanFactory并进行XML文件的解析、读取。BeanFactory的创建和Bean定义信息的注册、解析都是通过此步骤来完成的。通过一张时序图来看下次方法运行的整体流程,再逐一进行刨析。
- AbstractRefreshableApplicationContext中的refreshBeanFactory
/**该实现执行此上下文的底层bean工厂的实际刷新,关闭以前的bean工厂(如果有的话)并为上下文生命周期的下一个阶段初始化新的bean工厂**/
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果存在BeanFactory则销毁之前的BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//子类自由配置工厂,例如:是否允许覆盖同名称的不同定义对象、循环依赖、手动添加一些Bean定义信息等
customizeBeanFactory(beanFactory);
//初始化docmentReader,并进行XMl文件读取及解析,默认命名空间的解析,自定义标签的解析
//这一步实际上就从XML配置文件里的bean信息给读取到了Factory里了
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
先在这里看下loadBeanDefinitions()
执行结束之后的结果:
从图中可以知道,loadBeanDefinitions()方法运行完后,在beanFactory变量里面存放着一个ConcurrentHashMap变量,用于存放着这两个Bean的KV键值对,说明这两个Bean的定义信息以及属性信息已经被加载到BeanFactory当中了。
- AbstractXmlApplicationContext中的loadBeanDefinitions()
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//为给定的BeanFactory创建一个新的XmlBeanDefinitionReader
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);
//加载bean的定义信息
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//这里就是我们在ClassPathXmlApplicationContext中传入的a02.xml
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
首先在refreshBeanFactory()方法中已经初始化了DefaultListableBeanFactory,对于读取XML配置文件,还需要使用XmlBeanDefinitionReader。所以在上述loadBeanDefinitions()中就需要初始化XmlBeanDefinitionReader。在DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以进行配置文件的读取了。要注意的地方时,在XmlBeanDefinitionReader初始化时就已经把DefaultListableBeanFactory给注册进去了,所以在XmlBeanDefinitionReader读取的BeanDefinition都会注册到DefaultListableBeanFactory中,也就是经过上述的loadingBeanDefinitions(),类型DefaultListableBeanFactory的变量beanFactory就已经包含了所有解析好的配置了。
- AbstractBeanDefinitionReader中的loadBeanDefinitions()
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
for (String location : locations) {
count += loadBeanDefinitions(location);
}
return count;
}
/**
* 从指定的资源位置加载bean定义,位置也可以是位置模式,前提是这个bean定义读取器的ResourceLoader是一个ResourcePatternResolver(资源路径解析器)
*.....
*/
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
//位置模式
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//资源定位
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//加载bean定义信息的
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
//只能通过绝对路径来加载单个资源
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
- PathMatchingResourcePatternResolver中的getResource()方法
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
//如果是类路径下的资源
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
//类路径资源(可能有同名的多个资源),通配资源
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
//具有给定名称的所有类路径资源
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Generally only look for a pattern after a prefix here,
// and on Tomcat only after the "*/" separator for its "war:" protocol.
//通常只在前缀后面查找一个模式,并且在Tomcat之后只有“* /”分隔符之后的“war:”协议
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
locationPattern.indexOf(':') + 1);
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
//给定具有名称的单个资源
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
此步骤主要对应AbstractBeanDefinitionReader中定位资源的实现,根据我们传入的规则定位到要加载的配置文件位置。
- XmlBeanDefinitionReader中的loadBeanDefinitions()方法
/**
* 从指定的XML文件加载bean定义信息
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//获取到读取xml配置文件的InputStream流后,进行BeanDefinitions的加载
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
....
}
/*
真正从xml配置文件中加载Bean定义信息
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//获取xml的配置信息并封装为Document对象
Document doc = doLoadDocument(inputSource, resource);
return this.registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
...
}
}
在XmlBeanDefinitionReader中loadBeanDefinitions()方法和doLoadBeanDefinitions()方法主要对我们对应的配置文件路径转化为读取并转化为Docment对象。
- XmlBeanDefinitionReader中的registerBeanDefinitions()方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//从工厂中拿到bean定义信息的个数
int countBefore = getRegistry().getBeanDefinitionCount();
//通过documentReader来解析注册bean定义信息
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//本次加载ben定义信息的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
此步骤主要通过创建BeanDefinitionDocumentReader让BeanDefinitionDocumentReader来解析注册bean定义信息。
- DefaultBeanDefinitionDocumentReader中的doRegisterBeanDefinitions()方法
/**
* Register each bean definition within the given root {@code <beans/>} element.
* 任何嵌套的<beans>元素都将导致此方法的递归。为了正确传播和保留beans的default属性,需要跟踪当前(父)委托,该委托可以为null。创建新的(子)委托时,需要引用父项以进行回退,然后最终将this.delegate重置为其原始(父)引用。
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
//首先会拿到根节点对象 <Beans>标签
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
//拿到一个解析对象
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 解析 bean 定义之前处理 xml 对象,默认实现是空
preProcessXml(root);
//解析bean定义信息
parseBeanDefinitions(root, this.delegate);
// 解析 bean 定义之后处理 xml 对象默认实现是空
postProcessXml(root);
this.delegate = parent;
}
该步骤主要作为根节点解析的入口。
- DefaultBeanDefinitionDocumentReader中的parseBeanDefinitions()方法
/**
* 解析文档中根级别的元素
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
/**
*
* 若 element 的 namespace 是 default namespace,将进入 parse default element 流程;
* 比如当前 element 是普通的 <bean/>
* 若 element 的 namespace 不是 default namespace,将进入 parse custom element 流程;
* 比如当前 element 是 <context:annotation-config/> 或者是 <context:component-scan/>
*
* */
if (delegate.isDefaultNamespace(root)) {
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)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
首先,判断 root 元素的 namespace 对应的是不是 default namespace,若不是,将进入parse custom element,这里我们关注常规流程,既是当 root 元素的 namespace 是 default namespace 的流程。
- DefaultBeanDefinitionDocumentReader中的parseDefaultElement()方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//处理import元素
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//处理别名元素
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//处理bean标签
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//如果此根标签是bean的话
processBeanDefinition(ele, delegate);
}
//处理Beans
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
我们主要进行分析处理bean标签。
- DefaultBeanDefinitionDocumentReader中的processBeanDefinition()方法
/**
* 解析bean的定义信息
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//生成BeanDefinitionHolder也就是这一步进行元素属性 id、name 、class等的识别以及生成Bean定义信息
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//对 bean definition 进行相关的修饰操作
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//向工厂中注册最终修饰后的B
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
通过processBeanDefinition()方法,就将我们xml配置的标签解析成BeanDefinition并且在最终将BeanDefinition注册到工厂中。到此为止完成了容器的创建和BeanDefinition的解析与注册。