spring 源码研究---bean包-- xml解析成bean对象
2016-03-10 06:53
627 查看
<?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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd" > <description>这个是描述</description> <!-- <import resource=""/> <alias name="" alias=""/> <beans></beans> --> <!-- parent="user" 拷贝user对应的bean属性配置,则可以重写与user不同的属性及配置,使用相同的属性及配置创建bean对象 --> <bean id="userSub" class="com.zghw.spring.demo.demo.User" parent="user" scope="prototype"> <property name="name" value="override"/> <property name="soso"> <!-- merge="true" 对于引用parent,如果子bean要使用父bean的数组 集合或者map等则需要使用merge=“true”, 否则会覆盖父类数组中的全部元素 --> <array value-type="int" merge="true"> <value>5555</value> </array> </property> </bean> <!--abstract="true" 定义为抽象的bean则不会创建该对象的实例,在创建bean之前,会检查到时抽象的就不会创建该bean实例 --> <bean id="abstractBean" class="com.zghw.spring.demo.demo.AbstractBean" abstract="true"> <constructor-arg name="name" type="String" index="0" value="zhangsan"></constructor-arg> <constructor-arg name="count" type="int" index="1" value="23"></constructor-arg> </bean> <!--depends-on="userSub,carFactory"依赖于其他bean对象,在依赖的bean一个个创建完成后 才创建该bean。依赖于多个就使用逗号分开 --> <bean id="relationBean" class="com.zghw.spring.demo.demo.RelationBean" depends-on="userSub,carFactory" ></bean> <!-- bean的作用范围:scope的使用 默认情况下,scope是sington即调用该对象是全局唯一 prototype每次访问都是最新的对象 --> <!-- name作为bean名称 --> <bean name="user1" class="com.zghw.spring.demo.demo.User" scope ="prototype"> <property name="cardID" value="4110XXXXXXXXXX"/> <property name="name" value="zhxxxxx"/> <property name="age" value="111"/> </bean> <bean id="webSite1" class="com.zghw.spring.demo.demo.WebSite"> </bean> <bean id="webSite2" class="com.zghw.spring.demo.demo.WebSite" scope="prototype"> </bean> <!-- 默认构造参数 --> <bean id="webSite3" class="com.zghw.spring.demo.demo.WebSite"> </bean> <bean id="car" class="com.zghw.spring.demo.demo.Car" scope ="prototype"> <!-- 构造器的使用 name 参数名称 | type 参数类型 | index 参数位置从0开始 | value 参数值 构造器元素下可以放集合 数组 或引用bean等 --> <constructor-arg name="name" type="java.lang.String" index="0" value="audi"></constructor-arg> <constructor-arg name="price" type="double" index="1" value="2456546.22"></constructor-arg> </bean> <!-- 静态工厂方法创建实例 factory-method 静态的工厂方法名字 constructor-arg静态的工厂方法参数 --> <bean id="carFactory" class="com.zghw.spring.demo.demo.CarFactory" factory-method="getInstance" scope ="prototype"> <constructor-arg name="factoryName" type="java.lang.String" value="static factory method"></constructor-arg> </bean> <!-- 动态工厂方式创建对象 factory-bean 工厂对象 factory-method 工厂方法名字 constructor-arg 工厂方法参数 --> <bean id="carSub" class="com.zghw.spring.demo.demo.CarSub" scope ="prototype" factory-bean="carFactory" factory-method="getCar"> <constructor-arg name="namefactory" type="java.lang.String" value="AAAAAA"></constructor-arg> <constructor-arg name="countfactory" type="int" value="11122"></constructor-arg> </bean> <!--autowire属性,控制是否自动注入bean对象的字段值,默认是不自动注入 1.autowire="no",不自动注入 2.autowire="byName",根据bean的字段值名称匹配bean集合中名称相同的bean对象进行注入 3.autowire="byType" 根据bean的字段值类型匹配bean集合中类型相同的bean对象进行注入, 如果有多个相同类型,会失败,可以指定相应多个bean中一个bean为primary主选 4.autowire="constructor" --> <!-- init-method="init" destroy-method="destroy" 定义了bean创建前使用的方法和销毁前使用的方法 --> <bean id="relationBean1" class="com.zghw.spring.demo.demo.RelationBean" depends-on="userSub,carFactory" autowire="byType" init-method="init" destroy-method="destroy" scope="singleton" ></bean> <bean id="userDependency" class="com.zghw.spring.demo.demo.User"></bean> <!-- 给bean起别名 --> <alias name="user1" alias="uuu"/> <!-- 级联别名的使用 ws关联webSite1 ss又关联ws wss又关联ss --> <alias name="webSite1" alias="ws"/> <alias name="ws" alias="ss"/> <alias name="ss" alias="wss"/> <!-- primary="true" 表示如果有多个bean匹配到则使用当前的作为主bean,优先选择 默认primary=false id作为bean名称 --> <bean id="user" class="com.zghw.spring.demo.demo.User" primary="true"> <!-- 基本类型注入 --> <property name="cardID" value="411024198902151655"/> <property name="name" value="zhangsan"/> <property name="age" value="27"/> <property name="money" value="333333.33"></property> <property name="isMarried" value="false"/> <!-- 属性是一个引用,包含当前User对象 --> <property name="userChild" ref="user1"></property> <!-- 属性是一个bean --> <property name="computer"> <bean id="cp" class="com.zghw.spring.demo.demo.Computer"> <property name="cs"> <!-- 属性中引用其他bean --> <ref bean="carSub" /> <!--使用idref和上面效果一样 <idref bean="relationBean"/> --> </property> <property name="cs1"> <!--区分null属性 --> <null></null> </property> </bean> </property> <property name="arrayWebSite"> <!-- 数组类型注入 --> <array value-type="com.zghw.spring.demo.demo.WebSite"> <!-- 内部bean,和外部的bean不冲突 --> <bean id="webSite1" class="com.zghw.spring.demo.demo.WebSite"> </bean> <bean id="webSite2" class="com.zghw.spring.demo.demo.WebSite"> </bean> <bean id="webSite3" class="com.zghw.spring.demo.demo.WebSite"> </bean> </array> </property> <property name="soso"> <array value-type="int"> <value>11111</value> <value>2222</value> <value>333</value> </array> </property> <meta key="we" value="22"/> <!-- 集合数据注入 --> <property name="listCar"> <list value-type="com.zghw.spring.demo.demo.Car"> <ref bean="car"/> <bean id="car" class="com.zghw.spring.demo.demo.Car"> <!-- 构造器的使用 --> <constructor-arg name="name" type="java.lang.String" index="0" value="audi1"></constructor-arg> <constructor-arg name="number" type="java.lang.String" index="1" value="xxxx"></constructor-arg> <constructor-arg name="brand" type="java.lang.String" index="2" value="DsAuto"></constructor-arg> </bean> </list> </property> <property name="registedWebSite"> <!-- map注入 --> <map key-type="java.lang.String" value-type="com.zghw.spring.demo.demo.WebSite"> <entry> <key><value>"wwww.baidu.com"</value></key> <ref bean="webSite1"/> </entry> <entry> <key><value>"wwww.google.com"</value></key> <ref bean="webSite2"/> </entry> <entry key="wwww.facebook.com" value-ref="webSite3"> </entry> </map> </property> <!-- 枚举类型注入 --> <property name="fruit" value="APPLE"></property> <!-- 使用SPEL引用其他bean属性值或者调用方法都行作为值 给力哦 也可以使用三维运算符 ? : --> <property name="mapProp"> <props> <prop key="#{user1.cardID}"> "#{user1.getName()}" </prop> <prop key="#{user1.name}">"#{user1.getAge()}"</prop> </props> </property> <!-- 使用SPEL引入静态方法返回值作为值 --> <!-- <property name="systemProp" value="#{T(com.zghw.spring.demo.demo.User).getProperties()}"></property> <property name="systemProp" value="#{T(java.lang.System).getProperties()}"> </property> --> <!-- Class类型注入 --> <property name="clazz" value="com.zghw.spring.demo.demo.User"></property> </bean> <!-- 枚举类型不需要 <bean id="fruit" class="com.zghw.spring.demo.demo.Fruit"></bean> --> <bean id="computer" class="com.zghw.spring.demo.demo.Computer"></bean> </beans>
/**
* BeanDefinitionReader 把一个Resource资源或者一个String定位符加载成一个BeanDefinition对象
* 实现该接口可以设置不同的加载实现,及对BeanDefinition的format 当然你也不一定非要实现这个接口,可以通过其他方式加载bean
*
* 接口方法: 1.加在一个资源Resource 2.加载多个资源Resource 3.加载一个String location定位符
* 4.加载多个String location定位符 5.返回BeanDefinition注册表 6.返回资源加载器 7.返回类加载器
* 8.返回beanname生成器
*
* BeanNameGenerator 接口 BeanDefinition的name生成器 接口方法: 1.放入BeanDefinition
* 及BeanDefinitionResitory注册表 生成一个name
* DefaultBeanNameGenerator实现了BeanNameGenerator接口 1.得到beanclassname 然后
* 如果为空使用父类的classname+$child 如果父类classname为空就用工厂中bean的名称+$created
* 判断得到的最后name在注册表中的数量 最后得到BeanName#counter
*
* AbstractBeanDefinitionReader抽象类实现BeanDefinitionReader大部分工作,
* 并且实现了EnvironmentCapable运行环境属性 提供了共同的属性,比如bean工厂工作和使用的类加载器加载bean类。
* 他通过构造方法参数BeanDefinitionRegistry来创建该对象,使读到的BeanDefinition注入到注册器中
* XmlBeanDefinitionReader类继承了AbstractBeanDefinitionReader
* 它主要把工作委托给BeanDefinitionDocumentReader来工作
*
* PropertiesBeanDefinitionReader继承了AbstractBeanDefinitionReader
* 这个读取器可以读取属性格式的BeanDefinition 也可以读取map
* 然后把属性值或map循环取值匹配到BeanDefinition中,然后放入注册表中。 在spring中有很多final map
* 它们在spring容器中就像一个个数据结构一样或者说是数据库把
*
* DocumentLoader接口 xml文档加载器 策略模式加载xml文档对象
*
* 它加在xml为一个文档的方法需要三个参数
* 第一个参数 资源输入流 这个客户端给定
* 第二个参数 EntityResolver 实体解析器
* 第三个参数 errorHandler 错误处理器
* 第四个参数 验证模式 是验证DTD还是XSD
* 第五个参数 XML解析器是否应该被XML名称空间感知的
*
* 第一个参数通过包装客户端给的url成为一个inputstream
* 第二个参数是替换网络的url systemId 为本地的systemId
* spring实现EntityResolver接口有以下类:
* BeansDtdResolver :它为了转化带有.dtd的systemId,从访问网络的url转换为本地的url的dtd文件
* PluggableSchemaResolver:它为了转化带有.xsd的systemId,从访问网络的url转换为本地的url的.xsd文件
* 它把META-INF/spring.schemas循环放入map中 然后比较你定义的xsd是否在map中存在,如果存在则取出对应的url
* 把它转换为输入流放入Inputsource中
* 1.DelegatingEntityResolver 使用委托的方式,根据xml文件的systemId的后缀.dtd还是.xsd判断用
* BeansDtdResolver 还是PluggableSchemaResolver来处理。
* ResourceEntityResolver继承了DelegatingEntityResolver 主要解决好像是空systemId路径是默认当前系统的路径
* 第三个参数 :使用默认的SimpleSaxErrorHandler 就是抛异常
* 第四个参数 :验证是DTD还是XSD,默认自动验证,使用了XmlValidationModeDetector来自动验证,判断是否包含Document。
* 第五个参数 :XML解析器是否应该被XML名称空间感知的。默认设置是“假”。
* * 就是前一个是publicID,后一个是SystemId,是一一对应的
* http://www.springframework.org/schema/beans publicID
* http://www.springframework.org/schema/beans/spring-beans-2.5.xsd SystemId
*
* DefaultDocumentLoader 默认实现 DocumentLoader它使用了标准的JAXP-configured来解析XML文档
* 它设置了自动感知.xsd的文档
*
*
*################## BeanDefinitionDocumentReader ####
*BeanDefinitionDocumentReader接口从xmldocument对象中读取bean对象
* 读取的bean对象保存在了beanDefinition注册表中
* 接口方法:
* void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
* 读取Document文档注册,保存在了beanDefinition注册表中
* DefaultBeanDefinitionDocumentReader实现了BeanDefinitionDocumentReader 1.设置readerContext
2.开始document文档解析,查找根元素
3.首先使用XmlReaderContext创建BeanDefinitionParserDelegate委托对象,初始化委托对象,读取root属性,填充默认的DocumentDefaultsDefinition
1.default-lazy-init 默认为false
2.default-merge 默认为false
3.default-autowire 默认为no
4.设置default-dependency-check
5.存在就设置default-autowire-candidates
6.存在就设置default-init-method
7.存在就设置default-destroy-method
4.查询
如果root不是默认的命名空间:
则通过该root元素查找到namespaceURI,通过namespaceURI查找到NamespaceHandler,然后调用parse解析为BeanDefinition对象返回。
如果是默认的命名空间
8.存在就取profile设置环境
循环root下的子元素
如果子元素不是默认的命名空间则像上面一样处理
如果子元素是默认命名空间
<beans>下的节点:
1.import
2.alias
3.bean
4.beans 重复之前的步骤
1.import节点
resource属性,解析location有占位符时,e.g. "${user.dir}"
如果resource的资源定位正确,则reader.loadBeanDefinitions加载
2.alias节点
name属性:
alias属性:
注册器注册
3.bean元素
属性:
1.id
2.name
3.class
4.parent
开始创建AbstractBeanDefinition,仅仅设置了父类名称和class类型
5.scope
6.abstract
7.lazy-init 默认就是用root<beans>下的
8.autowire 0不自动装配 1 byname 2 bytype 3.by constructor
9.dependency-check
10.有就设置depends-on
11.autowire-candidate
12.存在就设置primary
13.有就设置init-method没有就看root是否有
14.destroy-method有就设置看root是否有
15.factory-method工厂方法
16.factory-bean
bean子节点元素
1.description
2.meta 属性:1.key 2.value 放入了BeanMetadataAttributeAccessor中 0-*
3.lookup-method 属性:1.name 2.bean 创建LookupOverride对象 0-*
4.replaced-method 属性:1.name 2.replacer 创建ReplaceOverride对象
5.arg-type 属性match
6.constructor-arg 属性1.index 2.type 3.name
7.property 属性1.name 这个复杂 有空在看
8.qualifier 属性1.type2.value 子元素:attribute属性1.key2.value
这样一个bean就组合好了返回这个BeanDefinitionHolder
如果定制的要包装bean的则包装
最后一步就是注册BeanDefinition及别名
property
属性:
name
ref | value | 元素
ref ---RuntimeBeanReference
value ---TypedStringValue
元素
1.bean ---BeanDefinitionHolder
2.ref 属性bean --RuntimeBeanReference
3.id-ref 属性bean --RuntimeBeanNameReference
4.null --TypedStringValue
5.array 属性 value-type merge ---ManagedArray
6.list 属性value-type merge ---ManagedList<Object>
7.set 属性value-type merge ---ManagedSet<Object>
8.map 属性key-type value-type merge ---ManagedMap<Object, Object>
元素: entry 属性: key key-ref value value-ref value-type
entry元素:
key 属性key-ref --TypedStringValue 或RuntimeBeanReference
value
9.props 属性 ---ManagedProperties
元素:prop 属性 key --TypedStringValue
DefaultsDefinition接口继承了BeanMetadataElement
具体实现通常是基于文档的默认值,例如在根标记级别进行指定在一个XML文档。
DocumentDefaultsDefinition类实现了DefaultsDefinition接口,它持有默认指定
<beans>基本的spring bean定义属性 比如:
default-lazy-init default-autowire
* #################### ReaderContext XmlReaderContext######################
* ReaderContext 定义读取容器,主要用于保存读取状态及资源
* ProblemReporter 用于解析document出现问题时问题反馈
* ReaderEventListener spring只提供了EmptyReaderEventListener空实现,如果需要你可以自定义
* SourceExtractor 这是个提取:解析document后返回的原生bean定义,如果你需要则可以实现这个接口,
* 放入到ReaderContext中,读取信息后就可以使用这些资源了。就像访问器一样。
* spring提供了NullSourceExtractor空实现,PassThroughSourceExtractor简单实现返回了对象资源。
* 如果需要你可以自定义
* XmlReaderContext类继承自ReaderContext
* 它还加入xml中命名空间的解析器NamespaceHandlerResolver和XmlBeanDefinitionReader实现类
*
* ####################### NamespaceHandlerResolver ####################
* NamespaceHandlerResolver接口给定一个命名空间的URL解析本地实现了NamespaceHandler的处理器
* DefaultNamespaceHandlerResolver类实现了NamespaceHandlerResolver
* 默认的会查询所有的jar包下的META-INF/spring.handlers文件,解析为map key--命名空间URL value-class类名(转换为对象判断是否是处理器实例),
* 然后使用命名空间URL可以查询里面对应的NamespaceHandler
* 当然你也可以使用DefaultNamespaceHandlerResolver的构造器DefaultNamespaceHandlerResolver(ClassLoader, String)
* 来为自己构造
*
* ####################### NamespaceHandler #####################
* NamespaceHandler接口用来处理spring的xml文件命名空间
* 比如:在xml文中引入了bean工厂不认识,也就是你定制的命名空间,在beans元素下使用了该
* 命名空间下的元素,则你需要提供一个实现了NamespaceHandler接口的处理器,来处理该
* 命名空间下的元素。
* SimpleConstructorNamespaceHandler 直接继承自 BeanDefinitionParser
* 用于解析构造器参数 这样简便的定义
* <bean id="author" class="..TestBean" c:name="Enescu" c:work-ref="compositions"/>
*
* SimplePropertyNamespaceHandler 直接继承自BeanDefinitionParser
* 解析属性值 简便定义
* <bean id="rob" class="..TestBean" p:name="Rob Harrop" p:spouse-ref="sally"/>
*
* 开发人员编写自己的定制元素扩展通常不会直接实现这个接口,而是利用所提供的NamespaceHandlerSupport类。
* 接口方法:
* 1.初始化方法,用于在需要定制的NamespaceHandler实现类时进行初始化
* 2.调用parse方法解析元素为一个BeanDefinition
* 3.装饰一个节点或属性为BeanDefinitionHolder
* NamespaceHandlerSupport抽象类,使用了final map 作为注册表
* 定义了三个注册表
* 1.key-elementName(元素的名称) value -BeanDefinitionParser(命名处理器对应元素的转化器)
* 2.key-elementName(元素的名称) value -BeanDefinitionDecorator(命名处理器对应元素的包装器)
* 3.key-attrName(属性的名称) value -BeanDefinitionDecorator(命名处理器对应元素属性的包装器)
* 让实现的子类通过以下方法注入到注册表中,这个设计有点意思。
* registerBeanDefinitionParser
* registerBeanDefinitionDecorator
* registerBeanDefinitionDecoratorForAttribute
* 子类要实现初始化方法。
* 在查找处理器时,通过解析后的命名空间名称查找到对应的处理器,使用转化器进行转换为BeanDefinition
* 实现 可以参考ContextNamespaceHandler
* UtilNamespaceHandler 实现了NamespaceHandlerSupport
* 它使用初始化方法 把key “constant” "property-path" "list" "set" "map" "properties"元素
* 及内部类实现了AbstractSingleBeanDefinitionParser的对象,来注册到父类注册表中
####################### BeanDefinitionParser ##########################
* BeanDefinitionParser接口用来转换xml文件中的元素为一个BeanDefinition
*
* ############################# ProblemReporter #################
* Location在解析资源出问题时可以得到本地资源位置,
* ParseState 是一个轨迹栈,在解析深层次xml元素时,如果出错需要记录一层一层的错误记录。
* 使用ParseState把元素压入栈,用完出栈,在出现问题时,从Stack中读取统计的栈信息以树的形式。
* 元素继承ParseState的内部接口Entry
* 其中BeanEntry、ConstructorArgumentEntry、PropertyEntry、和QualiflerEntry
* 每当读取这些元素就把相应的元素入栈和出栈
* Problem 对象使用ParseState Location组合信息输出
* ProblemReporter接口接受一个Problem定义了问题的性质
* 1.致命的
* 2.错误
* 3.提醒
* FailFastProblemReporter类实现了ProblemReporter接口
* 对于致命和错误的抛出BeanDefinitionParsingException(Problem)
* 提醒的使用了日志记录
*/
相关文章推荐
- MWeb Lite以及Eclipse的使用感想
- Eclipse launch configuration ---自动执行
- Java并发
- java 类型转换
- 看看异常
- Java中List遍历删除以及Map的输出方法
- 尚硅谷Java基础笔记
- java序列化
- Spring4.1新特性——静态资源处理增强
- Java四种引用详解
- 初识Java多线程
- EditPlus编写java的大括号自动补全
- Java基础之理解Annotation
- Spring IoC学习笔记(1):注解配置Bean
- java线程安全总结
- 学习java
- 基于Spring4.X和Hibernate4.x开发Restful风格WebService
- java 获取路径的
- java enum(枚举)使用详解 + 总结
- Java培训 Unit 1阶段项目总结 一 看书吧小说在线阅读器