依赖注入有两种方式:基于构造函数的依赖注入和基于Setter的依赖注入
实现方式
- 使用ApplicationContext描述所有bean的配置元数据创建和初始化。配置元数据可以由XML,Java代码或注释指定。
- 对于每个bean,它的依赖关系以属性,构造函数参数或static-factory方法的参数的形式表示(如果使用它而不是普通的构造函数)。实际创建bean时,会将这些依赖项提供给bean。
- 每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。
- 作为值的每个属性或构造函数参数都从其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring能够转换成字符串格式提供给所有内置类型的值,例如int, long,String,boolean,等等。
Spring IoC容器在创建容器时会验证每个bean的配置,但是,在实际创建bean之前,不会设置bean属性。创建容器时使用单例模式创建bean并会设置bean的默认值。
基于构造函数的依赖注入
循环依赖的问题
使用构造函数的依赖注入时,无法创建循环依赖的场景,如类A通过构造函数注入需要类B的实例,而类B通过构造函数注入需要类A的实例。如果将A类和B类的bean配置为相互注入,则Spring IoC容器会在运行时检测到此循环引用,并抛出BeanCurrentlyInCreationException。
constructor-arg 标签属性
type:显式指定构造函数参数类型
index:按顺序显式指定构造函数参数
value:构造函数参数值
代码示例
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
配置文件示例
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
构造函数参数类型匹配
对于前面的示例可以使用***type***属性显式指定构造函数参数的类型,示例如下:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
构造函数参数索引
也可以使用***index***属性通过索引的方式显式指定构造参数,示例如下
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
基于Setter的依赖注入
property标签属性
name:参数名称
ref:参数的bean依赖
配置示例
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
ExampleBean类示例
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
依赖注入配置详解
p命名空间
常见的配置方式
<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命名空间更简洁的配置
<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>
注:p命名空间虽然更简洁,但如果不使用支持自动属性提示的IDE,有时会有拼写错误
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 容器使用JavaBean将***<value/>***标签内的内容转换为java.util.Properties的实例PropertyEditor。
idref标签
idref标签只是一种防错标签,用于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>
第一种方式和第二种方式的区别主要是,第一种方式允许Spring容器在部署时验证引用的bean是否实际存在。第二种方式只有在实例化bean时才会发现错误。如果client为pojo类,可能部署很长时间之后才会发现产生的异常。
ref标签
用于<constructor-arg/>或<property/> 标签中,将bean的指定属性的值设置为容器管理的另一个bean的引用。
内部bean
要理解内部bean,首先看一下常见的配置方式
<bean id="theTargetBean" class="..." />
<bean id="client" class="...">
<property name="targetName" ref="theTargetBean"/>
</bean>
在一般情况下,引用这样也没有问题,当你需要将theTargetBean只让client引用时,而不是单独定义一个theTargetBean,就可以使用内部bean的方式来进行定义,这样其它的bean就不能通过ref来引用到theTargetBean了。
示例如下:
<bean id="client" class="...">
<property name="targetName">
<bean>
<property name="name" value="...">
</bean>
</propery>
</bean>
内部bean的定义方式和普通bean一样,只是不需要定义id或name,即使定义了,容器也不会使用这两个值作为bean的标识符。同理,内部bean也支持构造函数的依赖注入。示例如下:
<bean id="CustomerBean" class="com.ray.common.Customer">
<constructor-arg>
<bean class="com.ray.common.Person">
<property name="name" value="Ray"/>
<property name="address" value="zhuhai"/>
<property name="age" value="17"/>
</bean>
</constructor-arg>
</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>
空字符串值和Null
- 空字符串
代码示例:
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
上面的bean定义等同于下面的java代码:
exampleBean.setEmail("");
- Null值
代码示例:
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
上面的bean定义等同于下面的java代码:
exampleBean.setEmail(null);
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="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>
注:P命名空间虽然写法简单,但不如标准的XML配置格式灵活,另外P命名空间因为写法的问题有时会带来冲突,所以建议不使用P命名空间,如果一定要使用,需要和开发团队的其它成员沟通清楚。
C命名空间
与P命名空间类似,从Spring 3.1开始可以使用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="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
<!-- traditional declaration with optional argument names -->
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="something@somewhere.com"/>
</bean>
<!-- c-namespace declaration with argument names -->
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
</beans>
还有一种构造函数参数索引的方式,示例代码如下:
<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
c:_2="something@somewhere.com"/>
depends-on属性
通常情况下,两个bean之间的依赖使用ref来完成,但,有时bean之间的依赖关系不那么直接,例如:需要触发类中的静态初始化程序 ,数据库驱动程序注册等。depends-on属性在初始化bean之前,可以显式强制性的初始化一个或者多个bean。示例代码如下:
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
当表示多个bean的依赖关系时,可以使用逗号,空格和分号,代码示例如下:
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
延迟初始化(lazy-init)
默认情况下,当容器初始化时,会预先实例化所有的bean,但当不需要这样做时,可以通过lazy-init告诉IoC容器在第一次请求时才去创建bean实例,示例代码如下:
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
另外也可以使用default-lazy-init属性来控制整个beans的延迟初始化,代码如下:
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
方法注入
//TODO