对spring web启动时IOC源码研究(二)
发现这样debug到哪说到哪好像有点回不来了~让我重新理下思路,主要步骤先上图,有什么不同意见欢迎批评教育~
(一)spring IOC的主要步骤都在refresh()这个方法中,我给出了自己的理解注释
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 解析前准备工作,主要为打log,设置active标志
prepareRefresh(); // 对xml文件进行解析工作,并把解析后的BeanDefintiton定义信息保存入容器
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 在上下文中用容器之前的准备工作,主要为对DefaultListableBeanFactory beanFactory的参数设置
prepareBeanFactory(beanFactory); try {
// beanFactory添加后处理器
postProcessBeanFactory(beanFactory); // 调用beanFactoryPostProcessor接口实现类,并放入容器
invokeBeanFactoryPostProcessors(beanFactory); // 实例化实现beanPostProcessor的类,并放入容器
registerBeanPostProcessors(beanFactory); // 实例化MessageSource接口实现类,没有会实现默认实现类,用来实现国际化
initMessageSource(); // 实例化ApplicationEventMulticaster事件广播器,没有会创建默认类,应该是用来给监听的类发送广播吧
initApplicationEventMulticaster(); // 实例化一些特殊的类
onRefresh(); // 检查监听器并注册他们
registerListeners(); // 实例化所有非懒加载的单例对象,处理实现了Aware相关接口的类
finishBeanFactoryInitialization(beanFactory); // 结束容器的更新,初始化lifecycleProcessor接口实现类,广播容器更新完毕事件
finishRefresh();
} }
}
(二)接下来讲解几个我认为比较重要的部分,注意整篇随笔是根据方法的调用顺序一个个讲下去的
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
1、obtainFreshBeanFactory()方法,注释里有说明他的作用,具体深入下,主要操作在refreshBeanFactory()中,对beanFactory处理完成后getBeanFactory()去获得处理完成的beanFactory返回。
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);
}
}
2、进入refreshBeanFactory()方法,先是检查是否已经存在容器,第一次进都是没有的,之后进入红色字体方法createBeanFactory(),创建默认的beanFactory容器,设id和定制化操作后开始进入加载bean描述信息的loadBeanDefinitions()方法。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为beanFactory创建XmlBeanDefinitionReader即xml阅读器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 初始化阅读器的一些配置,比如加载环境信息,加载容器本身等等,由下面提供的运行调用截图可以看出,虽然在不同的子类调用方法,其本质都是XmlWebApplicationContext容器在运行
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类提供定制的阅读器进行初始化,之后处理加载bean描述信息
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
3、
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
接上面的方法,loadBeanDefinitions()方法开始加载xml信息了,此方法后也算结束了一阶段xmlWebApplicationContext容器的使用,进入阅读器解析xml阶段
此图可以看出从XmlWebApplicationContext到XmlBeanDefinitionReader的转换。
4、
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//省略。。
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
//省略。。
}
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
counter += loadBeanDefinitions(resource);
}
return counter;
}
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//省略
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
//省略
}
我尽量把它进入的一个个步骤都列出来,以免看客觉得怎么突然到了这个方法,但还是会省略一些细节,比如进入上述最后一个方法后,会对encodedResource进行保存操作等等,这里只列出特别主要的部分。上述操作进行了对资源的编解码,这么多方法的层层进入都还是准备工作,主要工作的方法为doLoadBeanDefinitions().
5、
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
//加载资源信息成Document对象
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
//开始
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建beanDefinition文档阅读器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(getEnvironment());
//获取DefalutListableBeanFactory中已经被加载的beanDefinition资源的多少
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//这个方法是在DefalutListablebeanFactory中进行的,可以看出bean描述信息被保存在了这个类中
@Override
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
//这个方法开始对节点解析了,之前创建的文档阅读器开始发挥作用,
//开始从XmlBeanDefinitionReader类处理转向DefaultBeanDefinitionDocumentReader类处理(BeanDefinitionDocumentReader的实现类)看下图
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
6、
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
//创建beanDefinition解析代理
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);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
//根节点前置处理,预留子类实现
preProcessXml(root);
//使用代理,解析bean定义
parseBeanDefinitions(root, this.delegate);
//根节点后置处理,预留子类实现
postProcessXml(root); this.delegate = parent;
} protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) { BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//初始化代理,查询根节点的各项默认属性并赋给代理文档默认定义的类DocumentDefaultsDefinition,这些属性包括lazy-init,autowire,init-method,destory-method等等
delegate.initDefaults(root, parentDelegate);
return delegate;
}
7、
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
//这里面开始对root根节点下的元素节点遍历
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);
}
}
//这里主要是对context:component-scan,mvc:annotation-driven等特别功能进行解析,同时对注解的类进行相应功能的操作,解析出对应的beanDefinfition
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//取得xml文件命名空间url
String namespaceUri = getNamespaceURI(ele);
//初始化命名空间处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//根据传入元素获得相应的beanDefinition解析器解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
8、对另一个解析方法parseDefaultElement()再进行探讨,它主要用来解析我们自定义的<bean>、<import>、<alias>、<beans>标签
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//这块代码很清晰的表示了对各明确元素的处理
//对improt元素处理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//对alias元素处理
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//对bean元素处理
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//对beans元素处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
9、就以bean处理为例子吧,对bean的处理也是最复杂的
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析bean,取得bean信息持有者
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//包装bean,额外操作
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册被包装好的最终的beanDefintion
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
10、由代理对bean元素解析
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//根据英文名也很好理解,获取id和name
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); //省略 String beanName = id;
//省略
//对bean元素各方面进行解析,里面有很多细节处理,它的描述信息主要在这里生成
//在这里我们获得的描述信息有:scope是否单例,isAbstract,lazy-init,autowire,dependCheck,depend-on,autowire-candidate
//primary,init-method,destory-method,factory-method,factory-bean,className,parent,description,look-up,replace-method,
//构造方法的name,type,index,属性的name,value等等,还设置extractSource,额外的资源供使用者扩展使用
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//省略(若没有beanName则系统生成,有别名获取别名)
//创建beanDefinitionHolder这个bean信息持有者类,将beanDefinition等信息传进去,并返回
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} return null;
}
11、正式注册处理完的beanDefinition信息,调用registerBeanDefinition()方法
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // 用beanName作为key值,注册beanDefinition信息
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 如果有别名,注册别名和beanName绑定
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//省略(可靠性判断)
//这里主要就是把beanDefinition放入Map集合中由容器保存,这里省掉了对是否有旧的同名beanName的判断
this.beanDefinitionMap.put(beanName, beanDefinition); //省略
}
到此为止就把bean信息全部解析注册完成了,至于最后一步fireComponentRegistered()方法,是对BeanDefinition再做了一层包装,然后通知监听器,spring没有再进行处理,此处是用来给用户扩展用的。
(pass:对bean的解说只是加载了一个bean的全部信息,执行完fireComponentRegistered()方法后,又进入了元素节点的循环解析,直到全部解析完成,XmlWebApplicationContext将保存存储有beanDefiniton信息的DefaultListableBeanFactory beanFactory,至此obtainFreshBeanFactory()方法才算运行完毕)
对spring web启动时IOC源码研究(二)的更多相关文章
- 对spring web启动时IOC源码研究
研究IOC首先创建一个简单的web项目,在web.xml中我们都会加上这么一句 <context-param> <param-name>contextConfigLocatio ...
- Chrome自带恐龙小游戏的源码研究(二)
在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: Cloud.co ...
- spring mvc 启动过程及源码分析
由于公司开源框架选用的spring+spring mvc + mybatis.使用这些框架,网上都有现成的案例:需要那些配置文件.每种类型的配置文件的节点该如何书写等等.如果只是需要项目能够跑起来,只 ...
- Nginx源码研究二:NGINX的事件处理概论
NGINX作为服务端的应用程序,在客户端发出数据后,服务端在做着这样一些处理,数据先会经过网卡,网卡会和操作系统做交互,经过操作系统的协议栈处理,再和不同的应用程序交互. 在这里面涉及两个概念,一个是 ...
- 一文读懂Spring动态配置多数据源---源码详细分析
Spring动态多数据源源码分析及解读 一.为什么要研究Spring动态多数据源 期初,最开始的原因是:想将答题服务中发送主观题答题数据给批改中间件这块抽象出来, 但这块主要使用的是mq消息的方式 ...
- Chrome自带恐龙小游戏的源码研究(三)
在上一篇<Chrome自带恐龙小游戏的源码研究(二)>中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替. 昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制. ...
- Chrome自带恐龙小游戏的源码研究(一)
目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏 ...
- Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...
- Spring Ioc源码分析系列--Ioc的基础知识准备
Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...
随机推荐
- 扩展 KMP
扩展KMP解决这样一些问题: 给定两个字符串 S 和 T(长度分别为 n 和 m),下标从 0 开始,定义extend[i]等于S[i]...S[n-1]与 T 的最长相同前缀的长度,求出所有的ext ...
- 洛谷3月月赛 R1 Step! ZERO to ONE
洛谷3月月赛 R1 Step! ZERO to ONE 普及组难度 290.25/310滚粗 t1 10分的日语翻译题....太难了不会... t2 真·普及组.略 注意长为1的情况 #include ...
- 安卓中圆角背景图被拉伸的解决方案——.9.png
举个例子: 从网上找了一张图片 如果我们直接用这张蓝色的图来做登录按钮的背景.将这个图片设为背景以后 我们可以发现四个角全部变形了,一点也不美观.针对此问题,我们通过.9图来解决. 首先我们先了解一下 ...
- JDBC常见面试题
以下我是归纳的JDBC知识点图: 图上的知识点都可以在我其他的文章内找到相应内容. JDBC常见面试题 JDBC操作数据库的步骤 ? JDBC操作数据库的步骤 ? 注册数据库驱动. 建立数据库连接. ...
- Hexo博客框架
https://hexo.io/docs/#What-is-Hexo hexo博客应用1 hexo博客应用2 Spark Streaming 消费kafka到HDFS 搭建篇-使用Github-hex ...
- dnspython模块安装
wget http://www.dnspython.org/kits/1.12.0/dnspython-1.12.0.tar.gz tar -zxvf dnspython-1.12.0.tar.gz ...
- javascript同步分页
目前网上分页的例子比较多,但是对其原理不是很了解,平时用的时候只是拿来调用,今天花了点时间,采用面向对象方式写了一个demo.对其方法做了封装,对外只提供一个调用接口. window.loadPage ...
- 初步探究Android App API接口测试--实战
一.Android App API接口测试 1.如何学好Android App API接口测试 postman可以用来实现API接口自动化测试,但是也有弊端,无法实现接口测试数据的参数化,为了达到接口 ...
- CentOS7关闭/开启防火墙出现 Unit iptables.service failed to load
在vm中安装好tomcat,而且在liunx中使用nc命令可以返回成功,但是更换到window中访问不到tomcat的情况,是由于linux防火墙的问题造成的,传统的解决方式有2中 第一种解决方案: ...
- Java Integer类型比较
今天做了一道题目题目如下: Integer a=10; Integer b=10; System.out.print(a==b); Integer c=200; Integer d=200; Syst ...