spring 源码研究---bean包-- xml解析成bean对象

<?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)
     * 提醒的使用了日志记录
     */

   

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值