简单的spring ioc源码脉络分析
本文主要是记录自己在学习spring源码时的理解
基于xml的ioc时序图
简单分析
在ioc的的时序图中分为三个阶段,分别为定位,加载和注册。定位主要是用来找到配置的xml文件在哪。加载就是把找到的配置文件解析成BeanDefnition对象。注册则是把BeanDefinition放到ioc容器中
运行案例
IocDao 模拟持久层对象
public class IocDao {
@Override
public String toString() {
return "返回iocDao对象";
}
}
IocService 模拟service层对象
public class IocService {
private List iocList;
private IocDao iocDao;
public List getIocList() {
return iocList;
}
public void setIocList(List iocList) {
this.iocList = iocList;
}
public IocDao getIocDao() {
return iocDao;
}
public void setIocDao(IocDao iocDao) {
this.iocDao = iocDao;
}
}
xml配置文件applicationContext.xml中部分主要内容
<bean id="iocDao" class="IocDao"/>
<bean id="iocService" class="IocService">
<property name="iocDao" ref="iocDao"/>
<property name="iocList">
<list>
<value>ioc11111111111</value>
<value>ioc22222222222</value>
<value>ioc33333333333</value>
</list>
</property>
</bean>
Main 主类
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IocService iocService = context.getBean("IocService", IocService.class);
List iocList = iocService.getIocList();
for (int i = 0; i < iocList.size(); i++) {
System.out.println("list属性值"+iocList.get(i));
}
System.out.println();
System.out.println(iocService.getIocDao().toString());
}
}
ioc创建的三个基本过程
定位:找到xml配置文件在哪里,即获取到Resource
一、寻找入口
以我们使用最多的CalssPathXmlApplicationContext为例,通过main()方法启动
在此处打断点,准备进入spring源码,首先进入方法,查看构造方法的调用
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
进入构造器传进来的参数就是我们写的xml文件名,并且又调用了其他构造器。
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);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
}
}
参数列表
属性名 | 属性值 |
---|---|
configLocations | applicationContext.xml |
refresh | true |
parent | null |
configLocation实际为String数组,为方便展示直接写的数组唯一的属性值
这个构造器有三个步骤
设置容器的资源加载器
对应的这句代码,对应第二步
super(parent);
作用为设置容器的资源加载器,对应第二步
设置Bean配置信息的定位路径
将在第二步中进行分析
this.setConfigLocations(configLocations);
启动整个ioc容器对Bean定义的载入过程(重要)
对应第三步
this.refresh();
二、获得配置路径
我们跟随上面构造器的super()方法一直向上追踪,直到找到AbstractApplicationContext这个类
这里截取了三个方法的代码,虽然代码看起来很多很复杂,但是仔细梳理一下核心过程,上面的构造方法做了以下两项重要工作
为容器设置好Bean资源加载器
下面这个方法是我们追踪到AbstractApplicationContext的入口,首先先调用了自身的无参构造
//debug追踪进来的入口,从此处开始分析
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
this.setParent(parent);
}
无参构造方法,对一些属性赋值,我们在此阶段只需要关注最后一个方法即可
public AbstractApplicationContext() {
this.logger = LogFactory.getLog(this.getClass());
this.id = ObjectUtils.identityToString(this);
this.displayName = ObjectUtils.identityToString(this);
this.beanFactoryPostProcessors = new ArrayList();
this.active = new AtomicBoolean();
this.closed = new AtomicBoolean();
this.startupShutdownMonitor = new Object();
this.applicationStartup = ApplicationStartup.DEFAULT;
this.applicationListeners = new LinkedHashSet();
//只关注这个方法即可
this.resourcePatternResolver = this.getResourcePatternResolver();
}
上面的方法又调用了下面这个getResourcePatternResolver方法
这个方法用来获取一个Spring Source的加载器用于读入Spring Bean定义资源文件
protected ResourcePatternResolver getResourcePatternResolver() {
//AbstractApplicationContext继承了DefaultResourceLoader,因此也是一个资源加载器
//返回的对象是Spring资源加载器,其getResource(String location)方法用于载入资源
return new PathMatchingResourcePatternResolver(this);
}
设置Bean配置信息的定位路径
追踪上面提到过的setConfigLocations(configLocations)方法,进入父类AbstractRefreshableConfigApplicationContext中
可以找到方法的代码为
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) {
//resolvePath为本类中将字符串解析为路径的方法
this.configLocations[i] = this.resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}
三、开始启动
SpringIOC 容器对 Bean 配置资源的载入是从 refresh()函数开始的,refresh()是一个模板方法,规定了IOC容器的启动流程,有些逻辑要交给其子类去实现。它对Bean配置资源进行载入。
追踪第一步中的refresh方法,进入父类AbstractRefreshableConfigApplicationContext的refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
//准备刷新
this.prepareRefresh();
//获取BeanFactory
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//准备BeanFactory
this.prepareBeanFactory(beanFactory);
try {
//BeanFactory后置处理
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
//执行BeanFactory后置事件处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册Bean后置事件处理器
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
//初始化MessageSource消息源
this.initMessageSource();
//初始化事件传播器
this.initApplicationEventMulticaster();
//调用子类的某些特殊Bean初始化方法
this.onRefresh();
//注册事件监听器
this.registerListeners();
//初始化所有剩余的单例Bean
this.finishBeanFactoryInitialization(beanFactory);
//发布容器事件,结束Refresh过程
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
//销毁Bean
this.destroyBeans();
//取消Refresh
this.cancelRefresh(var10);
throw var10;
} finally {
//重设公共缓存
this.resetCommonCaches();
contextRefresh.end();
}
}
}
代码同样很长,下面详细分析refresh的逻辑,篇幅原因不进入每个方法,可以自行进入方法查看
准备刷新
prepareRefresh();
调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识
获取BeanFactory
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
获取到应用上下文维护的beanFactory,默认是DefaultListableBeanFactory。并设置刷新标志refreshed为true
准备BeanFactory
this.prepareBeanFactory(beanFactory);
对beanFactory做了一些基础设置的配置,比如BeanClassLoader、BeanExpressionResolver、ApplicationContextAwareProcessor、ApplicationListenerDetector监听器检测器以及默认的环境信息bean。并设置了哪些不需要自动注入以及哪些已经解析过可以直接使用。
对应第四步
BeanFactory的后置处理
this.postProcessBeanFactory(beanFactory);
为容器的某些子类指定特殊的BeanPost事件处理器
执行BeanFactory后置事件处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
注册并调用BeanFactoryPostProcessor,扫描获取BeanDefinition;
注册Bean后置事件处理器
this.registerBeanPostProcessors(beanFactory);
注册BeanPostProcessor到BeanFactory;
初始化MessageSource消息源;
this.initMessageSource();
国际化相关
初始化事件传播器
this.initApplicationEventMulticaster();
调用子类的某些特殊Bean初始化方法
this.onRefresh();
为事件传播器注册事件监听器
this.registerListeners();
检查监听bean并将这些bean向容器中注册
初始化所有剩余的单例Bean
this.finishBeanFactoryInitialization(beanFactory);
初始化所有non-lazy-init bean,比如我们的controller、service、mapper等
发布容器事件,结束Refresh过程
this.finishRefresh();
初始化容器的生命周期事件处理器,并发布容器的生命周期事件
重设公共缓存
this.resetCommonCaches();
重置Spring核心中的常见内省缓存,因为我们可能不再需要单例bean的元数据
下面两个是catch中的方法
销毁已经创建的bean
this.destroyBeans();
为了防止Bean资源占用,如果出现异常则销毁已经在前面过程中生成的Bean
取消刷新
this.cancelRefresh(var10)
参数为捕获到的异常,充值容器的同步标识
总结
refresh()方法主要为IOC容器Bean的生命周期管理提供条件,Spring IOC容器载入Bean配置信息从其子类容器的refreshBeanFactory()方法启动, 所以整个refresh()中“ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,我们前面说的载入就是从这句代码开始启动。
refresh()方法的主要作用是:在创建IOC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IOC容器。它类似于对IOC容器的重启,在新建立好的容器中对容器进行初始化,对Bean配置资源进行载入。
四、创建容器
AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法, 容器真正调用的是其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory()方法
我们进入上一步的obtainFreshBeanFactory()方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//创建BeanFactory
this.refreshBeanFactory();
//返回BeanFactory
return this.getBeanFactory();
}
继续进入refreshBeanFactory()方法
protected final void refreshBeanFactory() throws BeansException {
//如果已经有容器,则销毁容器中的bean并关闭容器
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
//创建ioc容器
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
//对ioc容器进行定制化,如设置启动参数,开启注解的自动装配等
this.customizeBeanFactory(beanFactory);
//调用Bean定义的方法,使用了委派模式
//对应第五步
this.loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
} catch (IOException var2) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
}
}
能看出来,这里的逻辑还是很清晰的,如果有容器就销毁,然后进行容器的创建
五、载入配置路径
AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,我们继续从上一步的loadBeanDefinitions(beanFactory)方法进入
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader,即创建Bean读取器
//并通过回调设置到容器中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//为Bean读取器设置spring属性
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
//设置SAX xml解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//当读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
this.initBeanDefinitionReader(beanDefinitionReader);
//Bean读取去真正实现的加载方法(由此进入)
this.loadBeanDefinitions(beanDefinitionReader);
}
接着进入loadBeanDefinitions(beanDefinitionReader)方法、
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取Bean定义资源的定位
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
//调用父类的方法获取Bean的定义资源
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
//调用父类的方法获取Bean的定义资源
//对应第六步
reader.loadBeanDefinitions(configLocations);
}
}
六、分配路径处理策略
进入loadBeanDefinitions(configLocations方法
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
String[] var3 = locations;
int var4 = locations.length;
//对每一个路径进行处理
for(int var5 = 0; var5 < var4; ++var5) {
String location = var3[var5];
//从这里进入
count += this.loadBeanDefinitions(location);
}
return count;
}
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(location, (Set)null);
}
进入到真正处理的方法
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取在ioc容器初始化过程中设置的资源加载器
ResourceLoader resourceLoader = this.getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
} else {
int count;
if (resourceLoader instanceof ResourcePatternResolver) {
//主要理解这里即可
try {
//加载指定位置的Bean资源定义文件,对应第七步
Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
//委派调用子类的方法,实现加载功能,对应第八步
count = this.loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
} catch (IOException var6) {
throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var6);
}
} else {
Resource resource = resourceLoader.getResource(location);
count = this.loadBeanDefinitions((Resource)resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
}
七、解析配置文件路径
从getResources(location)进入找到具体实现方法
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
//判断是否为类路径下
if (locationPattern.startsWith("classpath*:")) {
return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
} else {
//如果不在类路径下则拼接前缀
int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(58) + 1;
return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
}
}
在这一步已经成功获取了资源加载器
八、开始读取配置内容
继续回到第七步XmlBeanDefinitionReader类中的loadBeanDefinitions方法,并进入loadBeanDefinitions(resources)方法
一直进入直到找到真正读取的方法doLoadBeanDefinitions
//从特定XMl文件中实际载入Bean定义资源
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//将xml文档转换为dom对象,解析过程由documentLoader实现
//对应第九步
Document doc = this.doLoadDocument(inputSource, resource);
//启动对Bean定义解析的详细过程,该解析过程会用到SpringBean配置规则
//对应第十步
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);
}
}
九、准备文档对象
进入doLoadDocument(inputSource, resource)
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//创建文件解析工厂
//由此进入
DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//创建文档解析器
DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
//解析Spring的Bean定义资源
//将Resource解析成DOM文件
return builder.parse(inputSource);
}
创建文件解析工厂的方法
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException {
//创建文档解析工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
//设置解析XML的校验
if (validationMode != 0) {
factory.setValidating(true);
if (validationMode == 3) {
factory.setNamespaceAware(true);
try {
factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
} catch (IllegalArgumentException var6) {
ParserConfigurationException pcex = new ParserConfigurationException("Unable to validate using XSD: Your JAXP provider [" + factory + "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(var6);
throw pcex;
}
}
}
return factory;
}
至此,Spring IOC 容器根据定位的 Bean 配置信息, 将其加载读入并转换成为 Document 对象过程完成。
加载:解析xml文件,保存成BeanDefinition对象
下面注释中写的Bean定义就是BeanDefinition
** 在这里我们要继续分析 Spring IOC 容器将载入的 Bean 配置信息转换为 Document 对象之后, 是如何将其解析为 Spring IOC 管理的 Bean 对象并将其注册到容器中的。**
十、分配解析策略
回到第八步,进入registerBeanDefinitions(doc, resource)方法
//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//得到BeanDefinitionDoucmentReader来对xml格式的BeanDefinition解析
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
//获得容器中注册的bean数量
int countBefore = this.getRegistry().getBeanDefinitionCount();
//解析过程入口,这里使用了委派模式
//具体的解释实现过程有实现类DefaultBeanDefinitionDoucmentReader实现
//对应第十一部
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
//统计解析的Bean数量
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
按照 Spring Bean的定义规则对 Document对象进行解析,其解析过程是在接 口BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader 中实现。
十一、将配置载入内存
进入registerBeanDefinitions(doc, this.createReaderContext(resource))方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//获取xml描述符
this.readerContext = readerContext;
//调用实现具体逻辑的方法
//doc.getDocumentElement()获取根标签
this.doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
//调用后面的方法
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
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;
}
}
}
//在解析Bean定义之前,进行自定义的解析,增强计息过程的可扩展性
this.preProcessXml(root);
//从document根元素开始进行Bean定义的Doucment对象
//调用后面的方法
this.parseBeanDefinitions(root, this.delegate);
//在解析Bean定义之后,进行自定义的解析,增强计息过程的可扩展性
this.postProcessXml(root);
this.delegate = parent;
}
//创建BeanDefinitionParserDelegate,用于完成真正的解析
protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//BeanDefinitionParserDelegate初始化Document根标签
delegate.initDefaults(root, parentDelegate);
return delegate;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//Bean定义的Document对象使用了Spring默认的XMl命名空间
if (delegate.isDefaultNamespace(root)) {
//获取根元素的所有子节点
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
//判断节点是否为xml元素节点
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
//调用后面的方法
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
//如果没有使用spring默认的命名空间,则使用用户自定义的解析规则进行解析
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//如果是import,则进行导入解析
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
//如果是alias,则进行别名解析
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
//如果既不是导入也不是别名,即最普通的bean元素
} else if (delegate.nodeNameEquals(ele, "bean")) {
//调用下面的方法
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
//解析bean元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析beanDefinition的子元素
//对应第十二步
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//向ioc容器注册 解析得到的Bean定义,这是Bean定义向ioc容器注册的入口
//对应第十六步
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
//在完成向ioc容器注册Bean的定义后,发送注册事件
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
十二、载入bean元素
Bean配置信息中的和元素解析在DefaultBeanDefinitionDocumentReader中已经完成
进入到parseBeanDefinitionElement方法
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//获取bean标签的id
String id = ele.getAttribute("id");
//获取name
String nameAttr = ele.getAttribute("name");
//获取alias属性值
List<String> aliases = new ArrayList();
//将所有name属性存放到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果没有配置id属性时,将别名中的第一个赋值给beanName
if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
beanName = (String)aliases.remove(0);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
//检查id或name的唯一性,containingBean标识bena标签中是否还有子bean标签
if (containingBean == null) {
//检查id,name和别名是否重复
this.checkNameUniqueness(beanName, aliases, ele);
}
//下关系对bean标签中配置的bean定义进行解析
//调用下面的方法
AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果没有配置id,name,别名,且不包含子元素
//生成一个唯一的beanName并注册
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
//如果没有id,name,别名,并且包含子元素
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
}
} catch (Exception var9) {
//解析出错时,返回空
this.error(var9.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} else {
return null;
}
}
//详细对备案标签配置的其他属性进行解析
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
//记录解析的bean
this.parseState.push(new BeanEntry(beanName));
//只读取备案标签配置的class名字(类名),保存到BeanDefinition中
//目前制作记录,不做实例化,实例化在后面依赖注入时完成
String className = null;
if (ele.hasAttribute("class")) {
className = ele.getAttribute("class").trim();
}
//如果配置了parent属性,则获取parent属性值
String parent = null;
if (ele.hasAttribute("parent")) {
parent = ele.getAttribute("parent");
}
try {
//根据类名和parent属性值创建BeanDefition
//为载入bean定义信息做准备
AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
//对当前bean元素中配置的一些属性值进行解析和设置,如配置单态(singleton)等
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//设置Description西欧毛线哦
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
//对元信息进行解析
this.parseMetaElements(ele, bd);
//对lookup-method属性解析
this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//对repalce-method属性解析
this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析构造方法属性
this.parseConstructorArgElements(ele, bd);
//解析<property>
//对应第十三步
this.parsePropertyElements(ele, bd);
//解析Qualifier
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;
}
只要使用过 Spring,对 Spring 配置文件比较熟悉的人,通过对上述源码的分析,就会明白我们在 Spring配置文件中元素的中配置的属性就是通过该方法解析和设置到 Bean 中去的。
注意: 在解析元素过程中没有创建和实例化 Bean 对象,只是创建了 Bean 对象的定义类BeanDefinition,将元素中的配置信息设置到 BeanDefinition 中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的 Bean 对象。
上面方法中一些对一些配置如元信息(meta)、qualifier 等的解析,我们在 Spring 中配置时使用的也不多,我们在使用 Spring 的元素时,配置最多的是属性,因此我们下面继续分析源码,了解 Bean 的属性在解析时是如何设置的。
十三、载入property元素
从上面parsePropertyElements(ele, bd);进入
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
//获取bean标签下的所有子标签
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
//如果是property标签,则调用解析property标签的元素进行解析
if (this.isCandidateElement(node) && this.nodeNameEquals(node, "property")) {
//调用后面的方法
this.parsePropertyElement((Element)node, bd);
}
}
}
//解析property标签
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取name属性值
String propertyName = ele.getAttribute("name");
if (!StringUtils.hasLength(propertyName)) {
this.error("Tag 'property' must have a 'name' attribute", ele);
} else {
this.parseState.push(new PropertyEntry(propertyName));
try {
//如果有同名的property存在,不进行解析,直接返回
//即如果配置了同名的property,只有第一个生效
if (bd.getPropertyValues().contains(propertyName)) {
this.error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析获取property的值
//调用后面的方法
Object val = this.parsePropertyValue(ele, bd, propertyName);
//根据property名字和值创建property实例
PropertyValue pv = new PropertyValue(propertyName, val);
//解析属性
this.parseMetaElements(ele, pv);
pv.setSource(this.extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
}
}
}
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = propertyName != null ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element";
//获取所有子标签,只能是其中一种类型:ref,value,list,etc等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
//子元素不是description和mate属性
if (node instanceof Element && !this.nodeNameEquals(node, "description") && !this.nodeNameEquals(node, "description")) {
if (subElement != null) {
this.error(elementName + " must not contain more than one sub-element", ele);
} else {
//当前标签还有子标签,如list内嵌套value标签
subElement = (Element)node;
}
}
}
//判断时ref还是value
boolean hasRefAttribute = ele.hasAttribute("ref");
boolean hasValueAttribute = ele.hasAttribute("value");
//不允许既不是ref,也不是value
if (hasRefAttribute && hasValueAttribute || (hasRefAttribute || hasValueAttribute) && subElement != null) {
this.error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
//如果是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref信息
if (hasRefAttribute) {
String refName = ele.getAttribute("ref");
if (!StringUtils.hasText(refName)) {
this.error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(this.extractSource(ele));
return ref;
//如果是value,创建一个value的数据对象TypedStringValue ,这个对象封装了value信息
} else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute("value"));
valueHolder.setSource(this.extractSource(ele));
return valueHolder;
//如果还有子元素,如list
} else if (subElement != null) {
//解析
//对应第十四步,
return this.parsePropertySubElement(subElement, bd);
} else {
this.error(elementName + " must specify a ref or value", ele);
return null;
}
}
小结
1)ref 被封装为指向依赖对象一个引用。
2)value 配置都会封装成一个字符串类型的对象。
3)ref 和 value 都通过“解析的数据类型属性值.setSource(extractSource(ele));”方法将属性值/引用与所引用的属性关联起来。
十四、载入property子元素
在上面方法的最后对于元素的子元素通过parsePropertySubElement()方法解析,我们继续分析该方法的源码,了解其解析过程。
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
//如果没有使用spring默认的命名矿建,则使用自定义的规则解析
if (!this.isDefaultNamespace((Node)ele)) {
return this.parseNestedCustomElement(ele, bd);
//如果是bean则用bean的解析器
} else if (this.nodeNameEquals(ele, "bean")) {
BeanDefinitionHolder nestedBd = this.parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = this.decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
//如果是ref
} else if (this.nodeNameEquals(ele, "ref")) {
String refName = ele.getAttribute("bean");
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//获取parent属性值,引用父级容器的bean
refName = ele.getAttribute("parent");
toParent = true;
if (!StringUtils.hasLength(refName)) {
this.error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
this.error("<ref> element contains empty target attribute", ele);
return null;
} else {
//创建ref类型数据,指向被引用的对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
//设置引用类型是被当前子元素所引用
ref.setSource(this.extractSource(ele));
return ref;
}
//按类别选择解析方法
} else if (this.nodeNameEquals(ele, "idref")) {
return this.parseIdRefElement(ele);
} else if (this.nodeNameEquals(ele, "value")) {
return this.parseValueElement(ele, defaultValueType);
} else if (this.nodeNameEquals(ele, "null")) {
TypedStringValue nullHolder = new TypedStringValue((String)null);
nullHolder.setSource(this.extractSource(ele));
return nullHolder;
} else if (this.nodeNameEquals(ele, "array")) {
return this.parseArrayElement(ele, bd);
} else if (this.nodeNameEquals(ele, "list")) {
//对应第十五步,解析list的子元素
return this.parseListElement(ele, bd);
} else if (this.nodeNameEquals(ele, "set")) {
return this.parseSetElement(ele, bd);
} else if (this.nodeNameEquals(ele, "map")) {
return this.parseMapElement(ele, bd);
} else if (this.nodeNameEquals(ele, "props")) {
return this.parsePropsElement(ele);
} else {
this.error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
十五、解析list的子元素
下面,我们对集合元素的解析方法进行源码分析,了解其实现过程。
进入parseListElement(ele, bd)方法
public List<Object> parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {
//获取valueType属性,即获取数据类型
String defaultElementType = collectionEle.getAttribute("value-type");
//获取所有子节点
NodeList nl = collectionEle.getChildNodes();
//封装为ManagedList
ManagedList<Object> target = new ManagedList(nl.getLength());
target.setSource(this.extractSource(collectionEle));
//设置集合目标数据类型
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(this.parseMergeAttribute(collectionEle));
//具体的元素解析
//调用后面的方法
this.parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
protected void parseCollectionElements(NodeList elementNodes, Collection<Object> target, @Nullable BeanDefinition bd, String defaultElementType) {
//遍历集合所有节点
for(int i = 0; i < elementNodes.getLength(); ++i) {
Node node = elementNodes.item(i);
if (node instanceof Element && !this.nodeNameEquals(node, "description")) {
//将元素加入集合,递归遍历下一个节点
target.add(this.parsePropertySubElement((Element)node, bd, defaultElementType));
}
}
}
注册:将BeanDefinition对象放入BeanFactory中
十六、分配注册策略
//将解析的BeanDefinitionHold注册到容器中
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
//获得解析的Beandefinition名称
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);
}
}
}
十七、向容器注册
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition)beanDefinition).validate();
} catch (BeanDefinitionValidationException var8) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);
}
}
BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!this.isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (!beanDefinition.equals(existingDefinition)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
if (this.hasBeanCreationStarted()) {
synchronized(this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
this.removeManualSingletonName(beanName);
}
} else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition == null && !this.containsSingleton(beanName)) {
if (this.isConfigurationFrozen()) {
this.clearByTypeCache();
}
} else {
this.resetBeanDefinition(beanName);
}
}