您现在的位置是:首页 >技术交流 >Spring源码方法解析:obtainFreshBeanFactory网站首页技术交流
Spring源码方法解析:obtainFreshBeanFactory
一、前言
在之前的文章中我们对Spring有的启动流程有了一个大体的了解;学习源码的目的并非要把每一个类每一个方法都搞得十分透彻,学习源码更多的是为了学习一种思想、更好的使用框架。闲言少叙,我们直接进入今天的主题。
1、配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!--导入属性文件-->
<context:property-placeholder location="classpath:person.properties"/>
<bean id="person" class="com.example.demo.entity.Person">
<property name="name" value="${person.name}"></property>
<property name="age" value="${person.age}"></property>
</bean>
</beans>
2、Main方法
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
final Person person = context.getBean("person", Person.class);
System.out.println(person);
}
二、obtainFreshBeanFactory
该方法位于AbstractApplicationContext类中的refresh方法,从方法名中我们大致可以猜测出当前方法的作用是去获取刷新后的BeanFactory(Bean工厂),接下来我们进入方法内部。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
解析:当前方法返回了一个ConfigurableListableBeanFactory的对象,方法体内总共包含两个方法,分别是
- refreshBeanFactory:刷新Bean工厂
- getBeanFactory:获取Bean工厂
1、refreshBeanFactory
首先我们解释refreshBeanFactory方法,进入方法。
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
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);
}
}
我们先看注释对方法作用整体有个了解:此实现执行此上下文的底层的实际刷新bean工厂关闭以前的bean工厂,并且为上下文生命周期的下一阶段初始化一个新的bean工厂。<br />源码解析:
- 首先判断是否已经存在了Bean工厂,如果存在的话则销毁。
- createBeanFactory():创建BeanFactory,直接new了一个DefaultListableBeanFactory
用于创建该Bean的参数是也是一个工厂(父工厂),由于是单一的Spring环境并不存在父子工厂,所以传进去的是null
tips:Spring中是有父子容器的概念的,最明显的体现就是在SpringMVC中,由于当前是在单一的Spring环境所以不存在父子工厂。
至此我们所使用的Bean工厂已经被实例化了,并且做了一些默认配置,例如初始化一些列的Map对象用于存放后续的Bean相关东西(这部分后续会单独分析)<br />Bean工厂创建好后,按照我们一般的思维,当我们所需要的对象创建好后下一步就该给对象赋值了,Spring中也是这么做的。
2、setSerializationId
这个方法就简单多了,就是给当前Bean工厂设置一个ID值。这里不多赘述
3、customizeBeanFactory
自定义BeanFactory,从方法名中也可以知道该方法就是用来当前Bean工厂设置一些自定义属性的。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
//设置BeanDefinition是否允许被重写
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//是否允许循环依赖
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
4、loadBeanDefinitions(重要)
当属性值都设置好了,接下来就是要将我们的对象(也就是bean)装到容器中了,我们知道在Spring中我们在配置文件里写的bean会被封装成BeanDefinition并存入Spring容器中。接下来我们看看Spring是如何实现的,源码如下:
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//创建一个XML解析器用解析XML配置文件
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);
//加载BeanDefinition(重点)
loadBeanDefinitions(beanDefinitionReader);
}
其他的几个方法相对来说不是那么重要,我们直接看最后的 loadBeanDefinitions(beanDefinitionReader);
@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;
}
Spring是支持多个配置文件的,所以这里是循坏的形式去加载Bean,我们进入该方法。(删除多余方法)
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
//抛异常:省略
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
return count;
}
catch (IOException 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);
}
return count;
}
}
解析:
1、获取资源加载器,用于加载资源文件(XML配置,该资源加载器在父类中初始化,所以学习Spring源码的过程中查看父类构造器是一个很重要的点,往往很多属性都是在父类中初始化)
2、获取到了资源处理器,接下来就是要处理资源了,通过Debug可以看到最终走到了这一步
3、通过一路Debug,最终我们到了XmlBeanDefinitionReader这个类,从类名中就可以猜出该类的作用是读取XML格式的配置文件,并且解析成BeanDefinition。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//省略日志
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
//省略异常
}
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
//省略异常
}
finally {
//省略
}
}
tips:在Spring中很多实际做事儿的方法都是以do开头的,例如 doLoadBeanDefinitions
整体方法也很好理解就是加载资源,然后加载Bean。我们进入doLoadBeanDefinitions(删除无关代码)
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
}
解析:
- 根据输入流获取Document对象,就是Xml被解析后的产物。
- 注册BeanDefinition(所谓的注册,其实就是往容器里存)
继续进入registerBeanDefinitions方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
解析:
- 创建文档解析器
- 获取已经注册的BeanDefinition数量
- 注册BeanDefinition(重要,放到后续解析)
三、小结
至此我们对obtainFreshBeanFactory方法的作用有了大致的了解。一句话结论就是,该方法用于获取刷新后的Bean工厂,并且加载配置文件并解析注册成BeanDefinition。对于如果注册由于篇幅限制这里先不展开,这部分放到下一篇文章来详细解析。希望对你有所帮助