- 浏览: 779580 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
萨琳娜啊:
Java读源码之Netty深入剖析网盘地址:https://p ...
Netty源码学习-FileRegion -
飞天奔月:
写得有趣 ^_^
那一年你定义了一个接口 -
GoldRoger:
第二个方法很好
java-判断一个自然数是否是某个数的平方。当然不能使用开方运算 -
bylijinnan:
<script>alert("close ...
自己动手实现Java Validation -
paul920531:
39行有个bug:"int j=new Random ...
java-蓄水池抽样-要求从N个元素中随机的抽取k个元素,其中N无法确定
以FileSystemXmlApplicationContext为例,把Spring IoC容器的初始化流程走一遍:
ApplicationContext context = new FileSystemXmlApplicationContext ("C:/Users/ZARA/workspace/HelloSpring/src/Beans.xml"); <beans> <import resource="classpath:config/spring/that-other-xml-conf.xml"/> <alias name="trianglePoint" alias="triangle-alias"/> <bean id="yourCoolBean" class="org.jdong.MyCoolBean"> <property name="age">100</property> </bean> </beans>
整个流程可用一句话简单概括:
读入并解析XML文件,把文件中Bean的定义保存在Map里
相应的要做以下几件事:
1.定位Resource
2.读入Resource,解析并创建BeanDefinition
3.把BeanDefinition注册到Map里
第1步由DefaultResourceLoader实现(FileSystemXmlApplicationContext是DefaultResourceLoader的子类)
第2步交给了BeanDefinitionReader,而BeanDefinitionReader又委托给BeanDefinitionParserDelegate
注意这一步创建的不是Bean的实例,Bean的实例化是在整个配置文件解析完毕之后再进行
第3步比较简单,交给BeanDefinitionReaderUtils
首先要明确,FileSystemXmlApplicationContext是实现了ResourceLoader、ResourcePatternResolver接口,继承了DefaultResourceLoader:
利用org.apache.commons.lang.ClassUtils可以查看FileSystemXmlApplicationContext都实现了哪些接口,都继承了哪些类
allInterfaces: interface org.springframework.beans.factory.BeanNameAware interface org.springframework.beans.factory.InitializingBean interface org.springframework.context.ConfigurableApplicationContext interface org.springframework.context.ApplicationContext interface org.springframework.beans.factory.ListableBeanFactory interface org.springframework.beans.factory.BeanFactory interface org.springframework.beans.factory.HierarchicalBeanFactory interface org.springframework.context.MessageSource interface org.springframework.context.ApplicationEventPublisher interface org.springframework.core.io.support.ResourcePatternResolver interface org.springframework.core.io.ResourceLoader interface org.springframework.context.Lifecycle interface org.springframework.beans.factory.DisposableBean allSuperclasses: class org.springframework.context.support.AbstractXmlApplicationContext class org.springframework.context.support.AbstractRefreshableConfigApplicationContext class org.springframework.context.support.AbstractRefreshableApplicationContext class org.springframework.context.support.AbstractApplicationContext class org.springframework.core.io.DefaultResourceLoader class java.lang.Object
因此查看AbstractXmlApplicationContext就不会迷惑了:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //ResourceLoader是this,因为AbstractXmlApplicationContext实现了ResourceLoader接口 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
由于Spring支持太多的配置方式了,阅读源码时跳来跳去,容易迷失
因此,最好是根据上面要做的几件事,一个一个的去找
1.资源定位
DefaultResourceLoader的getResource方法显示,有三种形式的Resource:
public Resource getResource(String location) { if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { return getResourceByPath(location); } } }
FileSystemXmlApplicationContext是第三种:
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }
DefaultResourceLoader.getResource(string)在哪被调用呢?
在AbstractBeanDefinitionReader:
public int loadBeanDefinitions(String location, Set actualResources) { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader instanceof ResourcePatternResolver) { try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); return loadCount; } } }
OK,Resource找到了
2.读取XML并创建BeanDefinition
XmlBeanDefinitionReader(Spring是用DOM的方式来解析XML的):
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); }
顺着上面的方法一直看下去(我们只看XML Namespace为default的那部分)
到了DefaultBeanDefinitionDocumentReader:
//遍历XML root里的每个Element(通常是一个Bean定义) 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(ele, delegate); } } //三部分,import, alias, bean我们主要看bean那部分 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } } protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //读取bean配置 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); //注册到Map BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); }
接着去到了BeanDefinitionParserDelegate,这个类承担了大部分的“脏活”:
//从下面代码中可见,如果定义了id,则beanName=id,否则取name列表中的第一个为beanName。除了beanName外,其他称为alias public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); } AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); //主要看一下这个方法 parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); return bd; } //可能有多个Property public void parsePropertyElements(Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); parsePropertyElement((Element) node, bd); } } public void parsePropertyElement(Element ele, BeanDefinition bd) { Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { /*Property有多种形式,例如 <property name="message" value="Hello World!"/> <property name="age"> <value>1</value> </property> 也可以是List, Map等等 */ // Should only have one child 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); subElement = (Element) node; } } boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); //ref="..." if (hasRefAttribute) { String refName = ele.getAttribute(REF_ATTRIBUTE); RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } //value="..." else if (hasValueAttribute) { TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } //非直接量,例如List else if (subElement != null) { return parsePropertySubElement(subElement, bd); } } //这个方法涵盖了List, Map, Array等等 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { if (nodeNameEquals(ele, REF_ELEMENT)) { // A generic reference to any name of any bean. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { // A reference to the id of another bean in the same XML file. refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); if (!StringUtils.hasLength(refName)) { // A reference to the id of another bean in a parent context. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean', 'local' or 'parent' is required for <ref> element", ele); return null; } } } if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } else if (nodeNameEquals(ele, NULL_ELEMENT)) { // It's a distinguished null value. Let's wrap it in a TypedStringValue // object in order to preserve the source location. TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
Spring IoC容器启动过程是相当复杂,因为它支持各种灵活的配置——这可能是一个框架所必须的吧
但就我个人而言,在可能的情况下,我会限定只采用一两种配置(例如XML),从而简化逻辑,所谓“约定大于配置”
参考文章:http://www.ibm.com/developerworks/cn/java/j-lo-beanxml/
参考文章里面把流程做成了图片,方便我们把握整个脉络
发表评论
-
深入纠结maven的资源过滤
2016-05-13 21:47 7133关于maven的资源过滤,官方文档有个例子: <pro ... -
重新发明轮子——解析xml并实例化类
2015-03-11 21:00 1774需求如图: 说明: bl ... -
Spring源码学习-JdbcTemplate queryForObject
2014-05-09 19:45 2991JdbcTemplate中有两个可能会混淆的queryForO ... -
Spring源码学习-JdbcTemplate batchUpdate批量操作
2014-05-07 16:21 18760Spring JdbcTemplate的batch操作最后还是 ... -
Spring源码学习-PropertyPlaceholderHelper
2014-04-25 18:47 2598今天在看Spring 3.0.0.RELEASE的源码,发现P ... -
Spring中JdbcDaoSupport的DataSource注入问题
2013-11-19 17:04 3877参考以下两篇文章: http://www.mkyong.com ...
相关推荐
Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- ...
Java-Spring-SpringIoC容器-SpringIoC的学习 SpringIoC容器的学习笔记 主要介绍了IoC容器工作原理以及如何配置IoC容器 通过标签和注解配置容器
Spring.net二----初探IOC容器.rar
spring Ioc容器配置 IOC容器数据源配置 <!-- 配置数据源 --> destroy-method="close"> <value>org.gjt.mm.mysql.Driver <value>jdbc:mysql://localhost:3306/demo <value>root ...
Spring IoC简单示例,使用XML配置依赖注入,使用Maven构建项目 Maven使用及Spring IoC教程参考我的Blog:http://blog.csdn.net/boyazuo
主要介绍了Spring IOC 容器启动流程分析,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
spring-context-support:对IOC的扩展,以及IOC子容器 spring-context-indexer:类管理组件和Classpath扫描 spring-expression:表达式语句 切面编程: spring-aop:面向切面编程,CGLB,JDKProxy spring-aspects:集成...
spring IOC容器依赖注入XML配置详解 运行环境:eclipse 构建工具:maven 不提供maven构建,maven用来解决jar包的依赖
Otus Spring框架开发人员学生:Andrei Bogomja 课程:...使用以下命令进入文件夹: cd otus-spring-hw-01-ioc-xml 使gradlew脚本可执行(如果在Windows上运行,则使用gradlew.bat ): chmod +x gradlew 构建可执行的
spring version: 5.0.0; jdk: 1.8 IOC大致调用顺序(IOC调用的AOP标签解析)
本系统实现了spring的控制反转思想(IOC)
Spring源码分析_Spring_IOC
Spring-IoC容器
spring入门学习-2、IOC、DI知识.pdf
Spring的IoC容器初始化源码解析,包括资源定位、加载、注册3个过程
spring-demo10-注解-IOC.zip
Spring IoC容器实现的结构分析
proxy,jdkproxy-transaction,jdkproxy-salary,day02-itheima11-spring-08-cglibproxy,day02-itheima11-spring-09-aop.xml,day02-itheima11-spring-10-aop.xml.advice,day02-itheima11-spring-11-aop.xml.exception等...
讲的非常不错的Spring书籍,特别有利于对IOC容器的理解,特别推荐哦
spring IoC, 使用配置文件方式配置Bean的实例化和依赖注入