接着上篇,在根上下文初始化的过程中,有一步配置和启动根上下文方法:org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext,这里面包含了对bean的所有处理,下面我们慢慢来看。
先看下这个方法的实现:configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
//读取web.xm中配设置的contextConfigLocation参数值
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
//根上下文(ioc容器)的启动,bean的处理也在这里
wac.refresh();
}
然后进入refresh()方法:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备启动上下文,设置开始时间,标记活动标志,初始化配置文件中的占位符
prepareRefresh();
//将 bean 定义加载到给定的 BeanFactory 中
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//准备 BeanFactory 以便在此上下文中使用。
// 1. 设置 BeanFactory 的类加载器
// 2. 添加几个 BeanPostProcessor
// 3. 实例化几个特殊的 bean
prepareBeanFactory(beanFactory);
try {
//为空实现,留给子类做扩展,不同 ApplicationContext 实现不同
postProcessBeanFactory(beanFactory);
// Spring 的 SPI
// 先调用 BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 的实现类
// 再调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
// 例如:ConfigurationClassPostProcessor 会扫描 和 @SpringBootApplication(scanBasePackages = "") 中的Component,并且将 @Configuration 类中的 @Bean register 到 BeanFactory 中
// 扩展例如:MyBatis MapperScannerConfigurer 和 MapperScannerRegistrar,扫描Mapper register 到 BeanFactory 中
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor 的实现类,不同于刚刚的 BeanFactoryPostProcessor
// BeanPostProcessor 接口两个方法 postProcessBeforeInitialization 和 postProcessAfterInitialization 会在 Bean 初始化之前和之后调用
// 这边 Bean 还没初始化,下面的 finishBeanFactoryInitialization 才是真正的初始化方法
registerBeanPostProcessors(beanFactory);
// 初始化当前 ApplicationContext 的 MessageSource,解析消息的策略接口,用于支持消息的国际化和参数化
// Spring 两个开箱即用的实现 ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource
initMessageSource();
// 初始化当前 ApplicationContext 的事件广播器
initApplicationEventMulticaster();
// 典型模板方法
// 子类可以在实例化 bean 之前,做一些初始化工作,SpringBoot 会在这边启动 Web 服务
onRefresh();
// 向 initApplicationEventMulticaster() 初始化的 applicationEventMulticaster 注册事件监听器,就是实现 ApplicationListener 接口类
// 观察者模式,例如实现了 ApplicationEvent,通过 ApplicationEventPublisher#publishEvent(),可以通知到各个 ApplicationListener#onApplicationEvent
registerListeners();
// 初始化所有的 singletons bean(lazy-init 的除外)
// Spring bean 初始化核心方法
finishBeanFactoryInitialization(beanFactory);
// 初始化完成(ContextRefreshedEvent)事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// destroy 已经创建的 singleton 避免占用资源
destroyBeans();
// 重置'active'标志
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
resetCommonCaches();
}
}
}
进入obtainFreshBeanFactory()方法,这个方法里面调用了refreshBeanFactory()和getBeanFactory()方法,这两个方法都是在子类AbstractRefreshableApplicationContext中实现的,主要看refreshBeanFactory()方法,如下:
@Override
protected final void refreshBeanFactory() throws BeansException {
//判断当前ApplicationContext是否已经有 BeanFactory ,如果有,销毁所有 Bean,关闭 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建一个可以独立使用的容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
customizeBeanFactory(beanFactory);
//解析配置文件,并将bean的信息注册到容器
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()在子类XmlWebApplicationContext实现,代码如下:
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader对象,并将beanFactory绑定,XmlBeanDefinitionReader是BeanDefinitionReader的一个实现类,负责对xml的配置文件进行读取
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
//设置环境,这里的环境是StandardServletEnvironment,里面主要有五项属性:servletConfigInitParams、servletContextInitParams、jndiProperties、systemProperties、systemEnvironment
//这个StandardServletEnvironment创建比较早,在为根上下文设置configLocationParam的时候创建的
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
//ResourceEntityResolver包含BeansDtdResolver和PluggableSchemaResolver,用以在classpath下搜寻schema和DTD文件
//PluggableSchemaResolver有一个schemaMappingsLocation属性其值为写死的META-INF/spring.schemas
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//允许子类提供reader的自定义初始化,然后继续实际bean定义加载。这里是空实现
initBeanDefinitionReader(beanDefinitionReader);
//加载bean的定义
loadBeanDefinitions(beanDefinitionReader);
}
继续看loadBeanDefinitions()方法,如下:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
//这里获取到的配置文件,就是在web.xml里面配置的contextConfigLocation,一般是:classpath:spring-config.xml
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
//解析配置文件,加载bean的定义
reader.loadBeanDefinitions(configLocation);
}
}
}
调用XmlBeanDefinitionReader父类AbstractBeanDefinitionReader的loadBeanDefinitions(String location)方法,这个方法直接调用的loadBeanDefinitions(String location, Set
public int loadBeanDefinitions(String location, Set actualResources) throws BeanDefinitionStoreException {
//获取ResourceLoader,此处的ResourceLoader就是我们创建的XmlWebApplicationContext
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//判断XmlWebApplicationContext是否为ResourcePatternResolver,通过类继承结构可以看出,XmlWebApplicationContext是ResourcePatternResolver的子类,所以会进入第一个条件分支
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//获取resources,这里的resources里面放的就是spring的xml配置文件信息
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//调用loadBeanDefinitions(Resource... resources)解析配置文件,并返回beanDefinition的数量
int loadCount = loadBeanDefinitions(resources);
//加载过程中已经被解析过的实际的Resource的填充集合
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 {
//只能通过绝对URL加载单个资源
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
说明下为什么要判断当前resourceLoader是否是ResourcePatternResolver类型的,因为ResourceLoader只是提供了对classpath前缀的支持。而ResourcePatternResolver提供了对classpath*前缀的支持。也就是说ResourceLoader提供classpath下单资源文件的载入,而ResourcePatternResolver提供多资源文件的载入。
调用loadBeanDefinitions(Resource... resources)解析配置文件,这个方法就是循环解析所有的配置文件,具体的解析,调用子类XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)这个方法,这个方法直接调用loadBeanDefinitions(EncodedResource encodedResource)这个方法,这个方法代码如下:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//检查是否重复加载xml配置
Set currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//真正的开始配置文件解析
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
在Spring代码中,最后真正执行操作的方法一般都是doXXXX,我们在工作中写代码也可以参考这种定义。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将xml文件转成标准的Document对象,采用SAX的方式解析
Document doc = doLoadDocument(inputSource, resource);
//载入并注册
return registerBeanDefinitions(doc, resource);
}
}
继续看org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions这个方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//这里的documentReader使用的是DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录统计前的BeanDefinition数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载并注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回本次加载的BeanDefinition数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
继续看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions这个方法:
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
//真正的注册bean定义
doRegisterBeanDefinitions(root);
}
接着看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions这个方法:
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
//创建 BeanDefinitionParserDelegate 对象,这个对象的作用是将 Document 的内容转成 BeanDefinition 实例
this.delegate = createDelegate(getReaderContext(), root, parent);
//验证 XML 文件的命名空间,即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
if (this.delegate.isDefaultNamespace(root)) {
//取得 beans 标签中 profile 的属性内容,该标签主要用于环境的切换。例如开发过程中,一般存在测试环境和正式环境,两者之间可能存在不同的数据源。若想要实现环境的快速切换,就可以利用 profile 来配置。
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//前置处理(空实现,供子类实现,方便扩展)
preProcessXml(root);
//开始解析 Bean 定义
parseBeanDefinitions(root, this.delegate);
//后置处理(空实现,供子类实现,方便扩展)
postProcessXml(root);
this.delegate = parent;
}
继续看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions这个方法:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//验证 XML 文件的命名空间,即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
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 {
//非xmlns="http://www.springframework.org/schema/beans"这个命名空间的标签解析,例如:
delegate.parseCustomElement(ele);
}
}
}
}
else {
//非xmlns="http://www.springframework.org/schema/beans"这个命名空间的标签解析,例如:
delegate.parseCustomElement(root);
}
}
这里可以看到,bean的定义解析分成了两个分支,一个是bean标签的解析,一个是非bean标签及自定义标签的解析,这块内容也很多,下篇继续。