Spring核心技术(三)——Spring的依赖及其注入(续)
本文将继续前文,针对依赖注入的细节进行描述
依赖注入细节
如前文所述,开发者可以通过定义Bean的依赖的来引用其他的Bean或者是一些值的,Spring基于XML的配置元数据通过支持一些子元素<property/>
以及<constructor-arg/>
来达到这一目的。
内在值类型(Java Primitives类型,字符串等)
元素<property/>
有value
属性来对人友好易读的形式配置一个属性或者构造参数。Spring的便利之处就是用来将这些字符串的值转换成指定的类型。
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="masterkaoli"/>
</bean>
下面的例子使用的p命名空间,是更为简单的XML配置。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/>
</beans>
上面的XML更为的简洁,但是因为属性的类型是在运行时确定的,而非设计时确定的,所以除非使用IntelliJ IDEA或者Spring Tool Suite这些工具才能在定义Bean的时候自动完成属性配置。当然很推荐使用这些IDE。
开发者也可以定义一个java.util.Properties
实例,比如:
<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
Spring的容器会将<value/>
里面的文本通过使用JavaBean的PropertyEditor
机制转换成一个java.util.Properties
实例。这也是一个捷径,也是一些Spring团队更喜欢使用嵌套的<value/>
元素而不是value
属性风格。
idref元素
idref
元素是一种简单的提前校验错误的方式,通过id来关联容器中的其他的Bean的方式。
<bean id="theTargetBean" class="..."/>
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean" />
</property>
</bean>
上述的Bean的定义在运行时,和如下定义是完全一致的。
<bean id="theTargetBean" class="..." />
<bean id="client" class="...">
<property name="targetName" value="theTargetBean" />
</bean>
第一种方式是更值得提倡的,因为使用了idref
标签,会是的容器在部署阶段就针对Bean进行校验,确保Bean一定存在。而第二个版本的话,是没有任何校验的。只有实际上引用了Bean client
,在实例化client的时候才会发现。如果client
是一个prototype
的Bean,那么类似拼写之类的错误会在容器部署以后很久才能发现。
idref
元素的local
属性在4.0以后的xsd中已经不再支持了,而是使用了bean
引用。如果更新了版本的话,需要将idref local
引用都转换成idref bean
即可。
引用其他的Bean
ref
元素在<constructor-arg/>
或者<property/>
中的一个终极标签。开发者可以通过这个标签配置一个Bean来引用另一个Bean。当需要引用一个Bean的时候,被引用的Bean会先实例化,然后配置属性,也就是引用的依赖。如果该Bean是单例Bean的话,那么该Bean会早由容器初始化。最终的引用另一个对象的所有引用。Bean的范围以及校验取决于开发者是否通过bean
,local
,parent
这些属性来指定对象的id或者name属性。
通过指定Bean的bean
属性中的<ref/>
来指定依赖是最常见的一种方式,可以引用容器或者父容器中的Bean,无论是否在同一个XML文件定义都可以引用。其中bean
属性中的值可以和其他引用Bean中的id
属性一致,或者和其中的一个name
属性一致的。
<ref bean="someBean"/>
通过指定Bean的parent
属性会创建一个引用到当前容器的父容器之中。parent
属性的值可以跟跟目标Bean的id
属性一致,或者和目标Bean的name
属性中的一个一致,目标Bean必须是当前引用目标Bean容器的父容器。开发者一般只有在存在层次化容器,并且希望通过代理来包裹父容器中一个存在的Bean的时候才会用到这个属性。
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
<!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
</property>
<!-- insert other configuration and dependencies as required here -->
</bean>
与
idref
标签一样,ref
元素中的local
标签在xsd 4.0以后已经不再支持了,开发者可以通过将已存在的ref local
改为ref bean
来完成更新Spring。
内部Bean
定义在<bean/>
元素的<property/>
或者<constructor-arg/>
元素之内的Bean叫做内部Bean
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>
内部Bean的定义是不需要指定id或者名字的。如果指定了,容器也不会用之作为分别Bean的区分标识。容器同时也会无视内部Bean的scope
标签:内部Bean 总是 匿名的,而且 总是 随着外部的Bean同时创建的。开发者是无法将内部的Bean注入到外部Bean以外的其他Bean的。
当然,也有可能从指定范围接收到破坏性回调。比如:一个请求范围的内部Bean包含了一个单例的Bean,那么内部Bean实例会绑定到包含的Bean,而包含的Bean允许访问到request
的scope
生命周期。这种场景不常见,内部Bean通常只是共享它的外部Bean。
集合
在<list/>
,<set/>
,<map/>
和<props/>
元素中,开发者可以配置Java集合类型List
,Set
,Map
以及Properties
的属性和参数。
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
当然,map的key或者value,或者是集合的value都可以配置为下列之中的一些元素:
bean | ref | idref | list | set | map | props | value | null
集合合并
Spring的容器也支持来合并集合。开发者可以定义一个父样式的<list/>
,<map/>
,<set/>
或者<props/>
,同时有子样式的<list/>
,<map/>
,<set/>
或者<props/>
继承并且覆盖父集合。也就是说,子集合的值是父元素和子元素集合的合并值。比如下面的例子。
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>
可以发现,我们在child
Bean上使用了merge=true
属性。当child
Bean由容器初始化且实例化的时候,其实例中包含的adminEmails
集合就是child
的adminEmails
以及parent
的adminEmails
集合。如下:
administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk
child
的Properties
集合的值继承了parent
的<props/>
,child
的值也支持重写parent
的值。
这个合并的行为和<list/>
,<map/>
以及<set/>
之类的集合类型的行为是类似的。<list/>
的特定的例子中,与List
集合类型类似,有隐含的ordered
概念的。所有的父元素里面的值,是在所有孩子元素的值之前的。但是像Map
,Set
或者Properties
的集合类型,是不存在顺序的。
集合合并的限制
开发者是不能够合并不同类型的集合的(比如Map
和List
合并),如果开发者这么做,会抛出异常。merge
的属性是必须特指到更低级或者继承者,子节点的定义上。特指merge
属性到父集合的定义上是冗余的,而且在合并上也没有任何效果。
强类型集合
在Java 5以后,开发者可以使用强类型的集合了。也就是,开发者可以声明一个Collection
类型,然后这个集合只包含String
元素(举例来说)。如果开发者通过Spring来注入强类型的Collection
到Bean中,开发者就可以利用Spring的类型转换支持来做到。
public class Foo {
private Map<String, Float> accounts;
public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>
当foo
的属性accounts
准备注入的时候,accounts
的泛型信息Map<String, Float>
就会通过反射拿到。这样,Spring 的类型转换系统能够识别不同的类型,如上面的例子Float
然后将字符串的值9.99, 2.75
以及3.99
转换成对应的Float
类型。
Null以及空字符串
Spring将会将属性的空参数,直接当成空字符串来处理。下面的基于XML的元数据配置就会将email属性配置为String
的值为""
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
上面的例子和下列JAVA代码是一致的。
exampleBean.setEmail("")
而<null/>
元素来处理null
的值。如下:
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
上面的代码和下面的Java代码是一样的效果:
exampleBean.setEmail(null)
XML 快捷方式p命名空间
p命名空间令开发者可以使用bean
的属性,而不用使用嵌套的<property/>
元素,就能描述开发者想要注入的依赖。
Spring是支持基于XML的格式化的命名空间扩展的。本节讨论的beans
的配置都是基于XML的,p命名空间并不是定义在XSD文件,而是定义在Spring Core之中的。
下面展示了两种XML片段是同样的解析结果:第一个使用的标准的XML格式,而第二种使用了p命名空间。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="classic" class="com.example.ExampleBean">
<property name="email" value="foo@bar.com"/>
</bean>
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="foo@bar.com"/>
</beans>
上面的例子在Bean中展示了email属性的定义。这种定义告知Spring这是一个属性的声明。如前面所描述,p命名空间并没有标准模式定义,所以你可以配置属性的名字为依赖名字。
下面的例子包括了2个Bean定义,引用了另外的Bean:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
从上述的例子中可以看出,john-modern
不止包含一个属性,也同时使用了特殊的格式来声明一个引用指向另一个Bean。第一个Bean定义使用的是<property name="spouse" ref="jane"/>
来创建的Bean引用到另外一个Bean,而第二个Bean的定义使用了p:spouse-ref="jane"
来作为一个指向Bean的引用。在这个例子中spouse
是属性的名字,而-ref
部分表名这个依赖不是直接的类型,而是引用另一个Bean。
p命名空间并不同标准的XML格式一样灵活。比如,声明属性的引用可能和一些以
Ref
结尾的属性相冲突,而标准的XML格式就不会。Spring团队推荐开发者能够和团队商量一下,要使用哪一种方式,而不要同时使用3种方法。
XML 快捷方式c命名空间
与p命名空间类似,c命名空间是在Spring 3.1首次引入的,c命名空间允许内联的属性来配置构造参数而不用使用constructor-arg
元素。
下面就是一个使用了c命名空间的例子:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
<!-- traditional declaration -->
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
<constructor-arg value="foo@bar.com"/>
</bean>
<!-- c-namespace declaration -->
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>
</beans>
c:
命名空间使用了和p:
命名空间相类似的方式(使用了-ref
来配置引用)。而且,同样的,c命名空间也不是定义在XSD的模式之中(但是在Spring Core之中)。
在少数的例子之中,构造函数的参数名字并不可用(通常,如果字节码没有debug信息的编译),开发者可以使用下面的例子:
<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>
根据XML语法,索引的概念的存在要求使用
_
作为XML属性名字不能以数字开始。
实际上,构造函数的解析机制在匹配参数是很高效的,除非必要,Spring团队推荐在配置中使用命名空间。
混合的属性
开发者可以在配置属性的时候配置混合的属性,只要所有的组件路径(除了最后一个属性名字)不能为null
。
参考如下的定义。
<bean id="foo" class="foo.Bar">
<property name="fred.bob.sammy" value="123" />
</bean>
foo
有一个fred
的属性,而其中fred
属性有一个bob
属性,而bob
属性之中有一个sammy
属性,那么最后这个sammy
属性会配置为123
。想要上述的配置能够生效,fred
属性需要有一个bob
属性且在fred
构造只是构造之后不为null
,否则会抛出NullPointerException
。
Spring核心技术(三)——Spring的依赖及其注入(续)的更多相关文章
- Spring重温(三)--Spring依赖注入(DI)
前言:在Spring框架中,DI(依赖注入)是用来定义对象彼此间的依赖,主要有set方法注入和构造器注入两种方式.另外,当一个类包含多个构造函数带的参数相同,它总是会造成构造函数注入参数类型歧义的问题 ...
- spring学习(三) ———— spring事务操作
前面一篇博文讲解了什么是AOP.学会了写AOP的实现,但是并没有实际运用起来,这一篇博文就算是对AOP技术应用的进阶把,重点是事务的处理. --wh 一.jdbcTemplate 什么是JdbcTem ...
- Spring(三)实例化Bean以及注入对象
使用xml实例化bean 在xml中实例化bean的三种方式 <bean id="springService" class="com.zhiyou100.crm.t ...
- 玩转Spring MVC(三)----spring基本配置文件
这篇文章总结一下spring mvc的基本配置,首先贴一张我的项目的目录截图,有一些多余的文件,大家不必在意: 用到的一些jar包在这:<a>http://download.csdn.ne ...
- Spring学习(三)-----Spring自动装配Beans
在Spring框架,可以用 auto-wiring 功能会自动装配Bean.要启用它,只需要在 <bean>定义“autowire”属性. <bean id="custom ...
- Spring学习(六)-----Spring使用@Autowired注解自动装配
Spring使用@Autowired注解自动装配 在上一篇 Spring学习(三)-----Spring自动装配Beans示例中,它会匹配当前Spring容器任何bean的属性自动装配.在大多数情况下 ...
- Spring总结三:DI(依赖注入)
简介: 所谓的依赖注入,其实是当一个bean实例引用到了另外一个bean实例时spring容器帮助我们创建依赖bean实例并注入(传递)到另一个bean中,比如你使用Spring容器创建的对象A里面需 ...
- Spring核心技术(四)——Spring的依赖及其注入(续二)
前面两篇文章描述了IoC容器中依赖的概念,包括依赖注入以及注入细节配置.本文将继续描述玩全部的依赖信息. 使用 depends-on 如果一个Bean是另一个Bean的依赖的话,通常来说这个Bean也 ...
- spring ioc三种注入方式
spring ioc三种注入方式 IOC ,全称 (Inverse Of Control) ,中文意思为:控制反转 什么是控制反转? 控制反转是一种将组件依赖关系的创建和管理置于程序外部的技术. 由容 ...
- Spring IOC 三种注入方式
1. 接口注入 2. setter注入 3. 构造器注入 对象与对象之间的关系可以简单的理解为对象之间的依赖关系:A类需要B类的一个实例来进行某些操作,比如在A类的方法中需要调用B类 ...
随机推荐
- Eclipse 使用Anaconda python 解释器
问题: ubuntu16.04 Anaconda 安装成功 Eclispe 写Python代码 无法使用 (pandas库等) 原因: Eclispe 此时的python解释器==>用的并不是A ...
- 昆石VOS3000_2.1.3.2完整安装包及安装脚本
安装包下载地址:http://www.51voip.org/post/55.html 安装教程: 上传安装包 核实 关闭selinux 是否关闭 /usr/sbin/sestatus -v cd /r ...
- C++中的常量(一) const限定符
最近在重新看<<C++ Primer>>,第一遍的时候const和constexpr看得并不太懂,这次又有了些更新的理解,当然可能仍然有许多不对的地方... 首先,const限 ...
- hdu1213 并查集不压缩
题意:题意:一个人请人吃饭,相互认识的朋友在一张桌子,相互认识的朋友的意思是如果A认识B,B认识C,那么A.B.C是朋友,对于每组输入输出桌子的张数. Sample Input 2 5 3 1 2 2 ...
- SpringBoot整合SpringSecurity简单案例
在我们开发项目的过程中经常会用到一些权限管理框架,Java领域里边经常用的可能就是shiro了,与之对应的还有SpringSecurity,SpringSecurity可以说是非常强大,与Spring ...
- 全面学习ORACLE Scheduler特性(6)设置Repeat Interval参数
3.3 设置Repeat Interval Job 和Schedule中REPEAT_INTERVAL参数都是用来控制执行的频率或周期,虽然说周期是一个时间性概念,不过REPEAT_INTERVAL指 ...
- 376 Wiggle Subsequence 摆动序列
A sequence of numbers is called a wiggle sequence if the differences between successive numbers stri ...
- 【转】rpm包和源码包安装的区别
转自:https://blog.csdn.net/junjie_6/article/details/59483785 建议在安装线上的生产服务器软件包时都用源码安装,这是因为源码安装可以自行调整编译参 ...
- C语言指针的理解以及指针的指针的理解
指针指向的是内存地址编号,内存地址编号指向的是对应的内容. 我们需要一个变量,来储存内存地址编号,这个变量的值是一个内存地址编号,但是我们可以通过修改变量的值,来不断的改变内存地址编号. 但是,我们如 ...
- Ps 快捷键全解
一.工具箱(多种工具共用一个快捷键的可同时按[Shift]加此快捷键选取)矩形.椭圆选框工具 [M]移动工具 [V]套索.多边形套索.磁性套索 [L]魔棒工具 [W]裁剪工具 [C]切片工具.切片选择 ...