- 浏览: 788443 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
萨琳娜啊:
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 7225关于maven的资源过滤,官方文档有个例子: <pro ... -
重新发明轮子——解析xml并实例化类
2015-03-11 21:00 1826需求如图: 说明: bl ... -
Spring源码学习-JdbcTemplate queryForObject
2014-05-09 19:45 3084JdbcTemplate中有两个可能会混淆的queryForO ... -
Spring源码学习-JdbcTemplate batchUpdate批量操作
2014-05-07 16:21 18876Spring JdbcTemplate的batch操作最后还是 ... -
Spring源码学习-PropertyPlaceholderHelper
2014-04-25 18:47 2648今天在看Spring 3.0.0.RELEASE的源码,发现P ... -
Spring中JdbcDaoSupport的DataSource注入问题
2013-11-19 17:04 3931参考以下两篇文章: http://www.mkyong.com ...
相关推荐
例如,关于IoC(Inversion of Control,控制反转)的实现,Spring使用了XML配置或注解来定义bean的依赖关系,通过反射机制动态加载和管理bean。另外,AOP模块实现了切面编程,允许我们定义横切关注点,如日志、事务...
深入学习Spring源码,不仅能够帮助我们理解其工作原理,还能提升我们在实际项目中的问题排查能力。例如,当我们遇到Spring的依赖注入问题时,可以通过查看`BeanFactory`和`ApplicationContext`的相关源码来定位问题...
例如,Spring的事件驱动模型、AOP的实现原理、以及IoC容器的内部工作流程等。此外,源码还展示了Spring如何与其他技术(如JDBC、JMS、EJB等)无缝集成,以及如何利用注解简化配置。 通过阅读和分析"spring-...
通过XML配置或注解方式定义bean,IoC容器负责实例化、装配和管理这些bean。 2. **AOP(Aspect Oriented Programming)**:Spring的AOP模块实现了面向切面编程,允许开发者定义方法拦截器和切入点表达式,用于代码的...
### Spring的IoC容器初始化源码解析 #### 一、Spring框架的核心——IoC容器 Spring框架是一个开源的轻量级Java开发框架,其核心功能是IoC(Inversion of Control,控制反转)容器和AOP(Aspect Oriented ...
- **spring-framework-5.1.13.RELEASE-schema.zip**:这个文件包含Spring框架的XML配置文件的架构定义,这对于理解如何通过XML配置来驱动Spring容器的行为至关重要。 4. **关键模块解析** - **Core Container**...
在源码中,我们可以看到`org.springframework.beans`和`org.springframework.context`包,它们是IoC容器的基础,通过BeanFactory和ApplicationContext实现对象的创建、配置和管理。 2. AOP支持:Spring的AOP模块...
2. **spring-beans**: 支持Bean工厂和XML配置,是IoC容器实现的基础。 3. **spring-context**: 扩展了IoC容器,引入了上下文概念,提供了对国际化、事件、资源、缓存等支持。 4. **spring-aop**: 实现了面向切面...
根据提供的文件信息,本次解读将围绕Spring框架的核心概念与源码分析进行展开。Spring框架作为Java企业级开发中不可或缺的一部分,其源码的学习对于深入...希望以上内容能够为大家学习Spring源码提供一定的参考和帮助。
Spring Core负责基础的IoC(Inversion of Control)容器,Spring Beans则实现了Bean的生命周期管理和配置,Spring Context则是基于Core之上构建的,提供了一个应用上下文,可以管理Bean并与其他Spring模块集成。...
文档可能将深入探讨Spring IoC容器初始化、Bean生命周期管理、依赖注入等关键概念,并以2021年的开源版本为背景进行分析。 从提供的部分文档内容来看,我们可以提炼出以下几个知识点: 1. **BeanFactory与...
XML配置是Spring 1.0中的主要配置方式,通过元素定义Bean,通过属性指定类路径、构造参数、依赖关系等。例如: ```xml ``` 五、未来的发展 Spring 1.0的发布为后续版本奠定了坚实的基础。随着版本的升级,...
1. **IoC(Inversion of Control)容器**:Spring的核心组件,负责管理对象的生命周期和依赖关系。在`org.springframework.beans`和`org.springframework.context`包中,我们可以看到BeanFactory和...
IoC容器负责管理对象的生命周期和依赖关系,而AOP则提供了一种模块化处理横切关注点的方式。在数据访问方面,Spring支持JDBC、ORM(Object-Relational Mapping)框架如Hibernate和MyBatis,以及对NoSQL数据库的支持...
Spring的IoC容器是其核心功能之一,通过XML或注解配置,它可以管理对象的生命周期和依赖关系。在源码中,`BeanFactory`和`ApplicationContext`接口定义了容器的基本操作,如`getBean`、`refresh`等。`...
IoC容器是Spring的核心,它负责管理对象的生命周期和依赖关系。AOP则为应用程序提供了一种模块化和解耦的方式,可以方便地实现横切关注点,如日志、事务管理等。 在Spring 5.2.1中,源码的改进主要集中在以下几个...
2. **Spring容器**:Spring的核心是IoC容器,如BeanFactory和ApplicationContext,它们负责读取配置,实例化、配置及管理Bean。 3. **Bean定义**:如何在XML或Java配置中定义Bean,包括其类名、初始化方法、属性...
Spring的IOC容器是其核心组件,它负责管理对象的生命周期和对象之间的依赖关系。IOC使得开发者不再手动创建和管理对象,而是将这些工作交给容器处理,降低了耦合度,提高了代码的可测试性和可维护性。在源码中,`org...
深入学习Spring源码,不仅可以提高我们的开发效率,还能使我们更好地应对复杂的业务场景。通过对源码的分析,我们可以了解Spring如何实现IoC、AOP等高级特性,以及它如何优化性能和提高可扩展性。这将对提升我们的...
3. 分析关键组件的源码,如IoC容器、AOP、MVC等。 4. 创建小型示例应用,利用源码中的功能,以便于实际操作和理解。 5. 使用断点、日志等方式进行调试,观察Spring框架内部工作流程。 通过以上步骤,开发者不仅可以...