ClassPathXmlApplicationContext构造方法中的refresh方法初始化了Spring容器。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
prepareRefresh方法做了以下几件事情:
- 设置容器启动时间
- 设置容器active标志位为true
- 设置容器closed标志位为false
- 执行initPropertySources方法
- 获取Environment对象,校验环境变量值并添加到Environment对象中
- 准备监听器和事件的集合对象
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// 空方法,是扩展点
initPropertySources();
// 校验环境中的属性,并且添加到Environment对象中
// 遍历requiredProperties集合中的属性,如果环境中没有,则抛异常
getEnvironment().validateRequiredProperties();
// 准备监听器和事件的集合对象
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
Spring的强大之处不仅仅在于它为Java开发者提供了极大便利,而且在于它的开放式架构,使得用户可以拥有最大扩展 Spring 的能力。其中,initPropertySources() 方法就是一个例子。
可以看到 prepareRefresh() 中的 initPropertySources() 方法并没有任何参数,而如果进入initPropertySources() 方法内部,也会发现该方法是空的,没有任何逻辑。
Spring这样设计,就是为了让用户根据自己的需要,可以重写 initPropertySources() 方法,并在方法中进行个性化的属性处理以及设置。
例如:在Environment对象中添加一些属性,或者设置环境中必须存在的属性
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
public MyClassPathXmlApplicationContext(String... configLocations){
super(configLocations);
}
@Override
protected void initPropertySources() {
System.out.println("扩展initPropertySource");
//这里添加了一个name属性到Environment里面,以方便我们在后面用到
getEnvironment().getSystemProperties().put("name","bobo");
//这里要求Environment中必须包含username属性,如果不包含,则抛出异常
getEnvironment().setRequiredProperties("username");
}
}
obtainFreshBeanFactory方法创建BeanFactory对象
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 创建BeanFactory
refreshBeanFactory();
// 获取BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refreshBeanFactory方法:
- 判断环境中是否已经有BeanFactory对象,如果有,删除该对象
- 创建新的BeanFactory对象
- 创建完成后,设置序列号
- customizeBeanFactory方法是为BeanFactory对象的allowBeanDefinitionOverriding和allowCircularReferences属性赋值
- 加载BeanDefinition对象
解释一下:
- allowBeanDefinitionOverriding属性设置为true表示后发现的Bean定义信息可以覆盖前发现的Bean定义信息
- allowCircularReferences属性设置为true表示允许循环依赖
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建BeanFactory对象的步骤如下:
// 1.new一个DefaultListableBeanFactory对象
// 2.为DefaultListableBeanFactory对象的属性赋初始值
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方法解析xml文件
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建Bean定义信息解析器
// 将beanFactory作为入参传入,Bean定义信息会存到beanFactory中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 设置Environment对象
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 设置资源加载器
beanDefinitionReader.setResourceLoader(this);
// 设置实体解析器
// beanDefinitionReader解析xml文件,需要知道xml文件的内容格式
// 所以beanDefinitionReader需要先读取dtd和xsd文件
// 这两个文件定义了xml文件的内容格式
// 这是在ResourceEntityResolver构造方法中完成的
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 设置beanDefinitionReader的validationMode属性值
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
xml文件头标明了xml文件的内容格式,现在基本上都是用xsd格式
<?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>
xml文件头中xsi:schemaLocation字段值是xsd文件的网络地址,去这个地址可以获取到解析xml文件需要的xsd文件,但是如果公司不连公网,xml文件就无法解析,项目就无法跑起来吗?当然不会。原因是Spring会去读取本地的xsd文件。
本地的xsd文件路径存储在Spring项目中的spring-beans模块下的resources/META-INF文件夹中的spring.schemas文件中。
入參为XmlBeanDefinitionReader对象的loadBeanDefinitions方法
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
在main函数中,new一个ClassPathXmlApplicationContext对象,传入一个入参,这种方法会使得getConfigResources方法返回null,getConfigLocations方法返回配置文件路径
一般情况下只传一个入参,初学Spring源码,我们点击查看最后一行的loadBeanDefinitions方法
入参为String数组的loadBeanDefinitions方法
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
String[] var3 = locations;
int var4 = locations.length;
for(int var5 = 0; var5 < var4; ++var5) {
String location = var3[var5];
counter += this.loadBeanDefinitions(location);
}
return counter;
}
以上代码的主要功能是遍历locations数组中的元素,调用loadBeanDefinitions方法
入参为String的loadBeanDefinitions方法
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(location, (Set)null);
}
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = this.getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
} else {
int loadCount;
if (!(resourceLoader instanceof ResourcePatternResolver)) {
Resource resource = resourceLoader.getResource(location);
loadCount = this.loadBeanDefinitions((Resource)resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
} else {
try {
Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
loadCount = this.loadBeanDefinitions(resources);
if (actualResources != null) {
Resource[] var6 = resources;
int var7 = resources.length;
for(int var8 = 0; var8 < var7; ++var8) {
Resource resource = var6[var8];
actualResources.add(resource);
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
} catch (IOException var10) {
throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
}
}
}
}
入参为String的loadBeanDefinitions方法中,又调了loadBeanDefinitons方法,有两个入参,一个是String,另一个是Set集合,元素类型是Resource
在入参为DefaultListableBeanFactory对象的loadBeanDefinitions方法中,已经将ResourceLoader设置为DefaultListableBeanFactory对象,这个对象也是ResourcePatternResolver的子类。所以,以上代码会走到else代码块中
else代码块中,又会调一次loadBeanDefinitions方法
入参为Resource数组的loadBeanDefinitions方法
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
Resource[] var3 = resources;
int var4 = resources.length;
for(int var5 = 0; var5 < var4; ++var5) {
Resource resource = var3[var5];
counter += this.loadBeanDefinitions((Resource)resource);
}
return counter;
}
以上代码中核心功能是遍历Resource数组,调用loadBeanDefinitions方法
入参为Resource的loadBeanDefinitions方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isInfoEnabled()) {
this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
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 {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
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;
}
}
入参为Resource的loadBeanDefinitions方法中将Resource对象封装成EncodedResource,然后调用loadBeanDefinitions方法
在loadBeanDefinitions方法中,创建了字节码输入流,调用doLoadBeanDefinitions方法,读取配置文件中的内容
doLoadBeanDefinitions方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
return this.registerBeanDefinitions(doc, resource);
} catch (BeanDefinitionStoreException var4) {
throw var4;
} catch (SAXParseException var5) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
} catch (SAXException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
} catch (ParserConfigurationException var7) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
} catch (IOException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
} catch (Throwable var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
}
}
在doLoadBeanDefinitions方法中,会调用doLoadDocument方法,将输入流以及Resource对象传入,生成Document对象
Document对象中,包含了xml文件的所有内容信息,将这些信息封装成一个个Node,便于后续的解析
registerBeanDefinitions方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建Document对象解析器
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
// getBeanDefinitionCount方法是返回BeanDefinitionMap中的元素个数
// 由于现在还没有解析出BeanDefinition,所以个数为0
int countBefore = this.getRegistry().getBeanDefinitionCount();
// 解析Document对象
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
this.logger.debug("Loading bean definitions");
// Document对象中包含了xml配置文件的所有内容,并且封装成一个个Node
// getDocumentElement方法是获取到根Node,并且封装成Element,方便后续解析
Element root = doc.getDocumentElement();
this.doRegisterBeanDefinitions(root);
}
doRegisterBeanDefinitions方法
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
// 创建了一个BeanDefinitionParserDelegate对象
// 也是用来解析xml文件内容
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
// 一般这里进不来,除非在xml文件中的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.isInfoEnabled()) {
this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
}
return;
}
}
}
// 空方法,可以进行扩展
this.preProcessXml(root);
// 解析xml文件内容,生成BeanDefinition对象
this.parseBeanDefinitions(root, this.delegate);
// 空方法,可以进行扩展
this.postProcessXml(root);
this.delegate = parent;
}
if判断代码块一般进不去,除非在xml文件中的beans标签中使用profile属性指定某个配置文件
parseBeanDefinitions方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
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;
// 有两种方法可以解析节点的内容
// parseDefaultElement方法是用来解析beans标签中自带的原始标签
// 例如:bean、alias、import、description等
// parseCustomElement方法是用来解析需要额外引入其他命名空间约束,并通过前缀引用的标签
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
xml配置文件中的标签分为两种,一种是默认标签,一种是自定义标签。默认标签不用额外导入其他命名空间约束的标签,例如 <bean> 标签。自定义标签需要额外引入其他命名空间约束,并通过前缀引用的标签,例如<context:property-placeholder/>标签
parseDefaultElement方法用于解析默认标签,parseCustomElement方法用于解析自定义标签
parseDefaultElement方法
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标签,bean标签里都是对象信息
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
processBeanDefinition方法
processBeanDefinition方法是来解析bean标签,生成BeanDefinition对象,并且注册到容器中
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 生成BeanDefinition对象,并且封装成BeanDefinitionHolder对象
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));
}
}