一、概念:
注入依赖对象有手工装配和自动装配两种方式。所谓的装配就是创建应用对象之间的协作关系的行为。
二、手工装配【构造器注入、setter注入(包括P标签的用法)】
1、构造器注入
- 这里以“歌手唱歌为例子”,Singer类有singerName、age、songName三个属性
- 创建Singger类,并添加构造函数
public class Singer implements Person {
private String singerName;
private int age;
private String songName;
public Singer(String singerName, int age, String songName) {
this.singerName = singerName;
this.age = age;
this.songName = songName;
}
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲是‘"+songName+"’");
}
}
- 在配置文件中,用constructor-arg 注入相应的属性
<bean id="singer" class="com.chensr.test.Singer">
<constructor-arg index="0" type="java.lang.String" value="chensr"></constructor-arg>
<constructor-arg index="1" type="int" value="18" ></constructor-arg>
<constructor-arg index="2" type="java.lang.String" value="英雄泪" ></constructor-arg>
</bean>
注:配置文件constructor-arg中的index表示参数的位置,type表示参数类型,value表示参数值。Index、type主要用于防止类中多个构造器匹配混乱的问题。
- 测试类
public class Test {
@org.junit.Test
public void test(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-context.xml");
Person singer = (Person)ac.getBean("singer");
singer.perform();
}
}
- 运行结果:我是歌手chensr,今年18岁,即将演唱的歌曲是‘英雄泪’
----------------------------------------------------------------------------------------------------------------------
- 上面例子用的是值注入,如果要注入对象,使用的是在constructor-arg的ref属性。例子“歌手不仅唱歌,还表演吉他”。
- 添加音乐工具类
public interface MusicalInstruments {
public void perform();
}
- 建一个Guitar类,实现MusicalInstruments接口
public class Guitar implements MusicalInstruments{
@Override
public void perform() {
System.out.println("吉他表演...");
}
}
- 修改Singer类,添加吉他属性
public class Singer implements Person {
private String singerName;
private int age;
private String songName;
private MusicalInstruments guitar;
public Singer(String singerName, int age, String songName, MusicalInstruments guitar) {
this.singerName = singerName;
this.age = age;
this.songName = songName;
this.guitar = guitar;
}
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲是‘"+songName+"’");
if(guitar!=null){
guitar.perform();
}
}
}
- 修改配置文件
<bean id="guitar" class="com.chensr.test.Guitar"></bean>
<bean id="singer" class="com.chensr.test.Singer">
<constructor-arg index="0" type="java.lang.String" value="chensr"></constructor-arg>
<constructor-arg index="1" type="int" value="18" ></constructor-arg>
<constructor-arg index="2" type="java.lang.String" value="英雄泪" ></constructor-arg>
<constructor-arg index="3" ref="guitar"></constructor-arg>
</bean>
- 测试输出结果:
我是歌手chensr,今年18岁,即将演唱的歌曲是‘英雄泪’
吉他表演...
2、setter注入
- 修改Singer类,删掉构造方法,并为属性添加setter方法
public class Singer implements Person {
private String singerName;
private int age;
private String songName;
private MusicalInstruments guitar;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲是‘"+songName+"’");
if(guitar!=null){
guitar.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongName(String songName) {
this.songName = songName;
}
public void setGuitar(MusicalInstruments guitar) {
this.guitar = guitar;
}
}
- 在配置文件中,用property注入相应的属性
<bean id="guitar" class="com.chensr.test.Guitar"></bean>
<bean id="singer" class="com.chensr.test.Singer">
<property name="singerName" value="chensr"></property>
<property name="age" value="11"></property>
<property name="songName" value="英雄泪"></property>
<property name="guitar" ref="guitar"></property>
</bean>
- 测试输出结果:
我是歌手chensr,今年18岁,即将演唱的歌曲是‘英雄泪’
吉他表演...
3、P标签的引用
- 引起p标签的命名空间xmlns:p="http://www.springframework.org/schema/p"
l 修改配置文件
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd" >
<bean id="guitar" class="com.chensr.test.Guitar"></bean>
<bean id="singer" class="com.chensr.test.Singer" p:singerName="chensr" p:age="18" p:songName="英雄泪" p:guitar-ref="guitar">
</bean>
</beans>
此时运行测试类,已经能得到setter注入一样的运行结果
三、自动装配【byName,byType,constructor和autodetect】
1、byName:把与bean的属性具有相同名字(或者ID)的其他bean自动装配到Bean对应的属性中,如果没有个属性名字相匹配的bean,则该属性不进行装配,如果发现多个,则抛出异常。
- 比如将Guitar自动装配到Singer中
- Singer类代码不变
public class Singer implements Person {
private String singerName;
private int age;
private String songName;
private MusicalInstruments guitar;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲是‘"+songName+"’");
if(guitar!=null){
guitar.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongName(String songName) {
this.songName = songName;
}
public void setGuitar(MusicalInstruments guitar) {
this.guitar = guitar;
}
}
- 修改配置文件类的实例
<bean id="guitar" class="com.chensr.test.Guitar"></bean>
<bean id="singer" class="com.chensr.test.Singer" autowire="byName">
<property name="singerName" value="chensr"/>
<property name="age" value="18"/>
<property name="songName" value="英雄泪"/>
</bean>
- 测试运行测试类,依旧能够在控制台看到:吉他表演...
2、byType:把与bean的属性具有相同类型的其他bean自动装配到bean的对象属性中,找不到相应类型,则不装配。
3、Constructor:把与bean的构造器入参具有相同类型的其他bean自动装配到bean构造器的对应入参中。
4、autodetect:先尝试constructor自动装配,如果失败,在尝试使用byType进行自动装配。
注:可以在配置文件的beans节点中添加default-autowire属性进行全局配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"
default-autowire="byName" >
四、基于注解注入
l Spring支持@Atutowierd、@Inject、@Resource三种注解,使用注解需要使用context命名空间,并配置自动扫描,让spring可以通过注解实例化相应的对象
<?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.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 开启注解 -->
<context:annotation-config/>
<!-- 自动扫描:使用annotation自动注册bean, 并保证@Required、@Autowired的属性被注入 -->
<context:component-scan base-package="com.chensr">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
</beans>
1、@Atutowierd(默认按类型装配,匹配到多个时按名称装配)
- 为Guitar添加注解@Component
@Component
public class Guitar implements MusicalInstruments{
@Override
public void perform() {
System.out.println("吉他表演...");
}
}
- 为Singer添加相应的注解
@Component
public class Singer implements Person {
@Value("chensr") //值注解
private String singerName;
@Value("18")
private int age;
@Value("英雄泪")
private String songName;
@Autowired //注入注解
private MusicalInstruments guitar;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲是‘"+songName+"’");
if(guitar!=null){
guitar.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongName(String songName) {
this.songName = songName;
}
public void setGuitar(MusicalInstruments guitar) {
this.guitar = guitar;
}
}
注:这里的setter方法可以删掉,只是基于编程习惯将它写上
- 测试运行测试类,在控制台看到
我是歌手chensr,今年18岁,即将演唱的歌曲是‘英雄泪’
吉他表演...
----------------------------------------------------------------------------------------------------------------------
注意:
1、@Atutowierd除了可以标注在定义属性的地方,还可以标注在需要装配bean引用的任意地方,比如setter、构造器。但如果只@Atutowierd时,缺点比较大,就是应用中必须只有一个bean合适装配到@Atutowierd标注的属性或参数中,否则就会出错。
- 解决上面问题,需要用到required属性,该属性默认为true,当将属性设置为false时,当找不到适合的bean时,表示注入NULL值。
@Autowired (required = false)
- 此时将Guitar的注解@Component删掉,运行测试类依旧正常运行只是不会打印“吉他表演...”
2、当匹配到多个时,按名字装配时又匹配不到时,系统会报错
- 添加Piano类,同样继承MusicalInstruments
@Component
public class Piano implements MusicalInstruments {
@Override
public void perform() {
System.out.println("钢琴表演...");
}
}
- 修改Singer属性private MusicalInstruments guitar为 MusicalInstruments musicalInstruments
@Component
public class Singer implements Person {
@Value("chensr") //值注解
private String singerName;
@Value("18")
private int age;
@Value("英雄泪")
private String songName;
@Autowired (required = false) //注入注解
private MusicalInstruments musicalInstrument;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲是‘"+songName+"’");
if(musicalInstrument!=null){
musicalInstrument.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongName(String songName) {
this.songName = songName;
}
public void setGuitar(MusicalInstruments musicalInstrument) {
this.musicalInstrument = musicalInstrument;
}
}
- ·运行测试类,会报错:Error creating bean with name 'singer': Unsatisfied dependency expressed through field 'musicalInstrument': No qualifying bean of type
- 解决这个问题,需要添加@Qualifier注解,比较我现在要注入piano,添加@Qualifier(“piano”)即可
@Component
public class Singer implements Person {
@Value("chensr") //值注解
private String singerName;
@Value("18")
private int age;
@Value("英雄泪")
private String songName;
@Autowired (required = false) //注入注解
@Qualifier("piano")
private MusicalInstruments musicalInstrument;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲是‘"+songName+"’");
if(musicalInstrument!=null){
musicalInstrument.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongName(String songName) {
this.songName = songName;
}
public void setGuitar(MusicalInstruments musicalInstrument) {
this.musicalInstrument = musicalInstrument;
}
}
- 运行测试类,控制台打印:
我是歌手chensr,今年18岁,即将演唱的歌曲是‘英雄泪’
钢琴表演...
2、@Resource(默认按名称装配,当找不到与名称匹配的bean才会按类型匹配)
- @Resource有一个name属性,用于按名称装配:@Resource(name = "piano")
- @Resource是jdk6引入的,不是spring本身自带的
3、@Inject(默认根据类型装配)
- 该注解没有required属性,所以注解所标注的依赖关系必须存在
- 提供了@Named用于按名字匹配,跟@Qualifier用法一样
注:现在企业开发,一般都是使用@Atutowierd
五、集合类型注入(这里不用注解)
- Spring提供了set、list、map、properties集合类型的注入方法。如果一个singer想唱几首歌的话,我们可以在Singer类中设置一个songsName属性,并为其生成setter方法
1、set的用法
- Singer类
public class Singer implements Person {
private String singerName;
private int age;
private Set<String> songsName;
private MusicalInstruments musicalInstrument;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲有‘"+ Arrays.asList(songsName)+"’");
if(musicalInstrument!=null){
musicalInstrument.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongsName(Set<String> songsName) {
this.songsName = songsName;
}
public void setMusicalInstrument(MusicalInstruments musicalInstrument) {
this.musicalInstrument = musicalInstrument;
}
}
- 配置文件
<bean id="guitar" class="com.chensr.test.Guitar"/>
<bean id="singer" class="com.chensr.test.Singer">
<property name="singerName" value="chensr"></property>
<property name="age" value="18"></property>
<property name="musicalInstrument" ref="guitar"></property>
<property name="songsName">
<set>
<value>英雄泪</value>
<value>封锁我一生</value>
</set>
</property>
</bean>
- 运行测试类,控制台打印:
我是歌手chensr,今年18岁,即将演唱的歌曲有‘[[英雄泪, 封锁我一生]]’
吉他表演...
2、List的用法
- Singer类
public class Singer implements Person {
private String singerName;
private int age;
private List<String> songsName;
private MusicalInstruments musicalInstrument;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲有‘"+ Arrays.asList(songsName)+"’");
if(musicalInstrument!=null){
musicalInstrument.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongsName(List<String> songsName) {
this.songsName = songsName;
}
public void setMusicalInstrument(MusicalInstruments musicalInstrument) {
this.musicalInstrument = musicalInstrument;
}
}
- 配置文件
<bean id="guitar" class="com.chensr.test.Guitar"/>
<bean id="singer" class="com.chensr.test.Singer">
<property name="singerName" value="chensr"></property>
<property name="age" value="18"></property>
<property name="musicalInstrument" ref="guitar"></property>
<property name="songsName">
<list>
<value>英雄泪</value>
<value>封锁我一生</value>
</list>
</property>
</bean>
- 运行测试类,控制台打印:
我是歌手chensr,今年18岁,即将演唱的歌曲有‘[[英雄泪, 封锁我一生]]’
吉他表演...
3、map用法
- Map是基于键值对存值的,这里有它来存放歌名和对应的语言
- Singer类
public class Singer implements Person {
private String singerName;
private int age;
private Map<String,String> songsName;
private MusicalInstruments musicalInstrument;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲有‘"+ Arrays.asList(songsName)+"’");
if(musicalInstrument!=null){
musicalInstrument.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongsName(Map<String, String> songsName) {
this.songsName = songsName;
}
public void setMusicalInstrument(MusicalInstruments musicalInstrument) {
this.musicalInstrument = musicalInstrument;
}
}
- 配置文件
<bean id="guitar" class="com.chensr.test.Guitar"/>
<bean id="singer" class="com.chensr.test.Singer">
<property name="singerName" value="chensr"></property>
<property name="age" value="18"></property>
<property name="musicalInstrument" ref="guitar"></property>
<property name="songsName">
<map>
<entry key="英雄泪" value="中文"></entry>
<entry key="Hear me cry" value="英文"></entry>
</map>
</property>
</bean>
- 运行测试类,控制台打印:
我是歌手chensr,今年18岁,即将演唱的歌曲有‘[{英雄泪=中文, Hear me cry=英文}]’
吉他表演...
4、Properties用法(与set相似,键值对必须是String)
- Singer类
public class Singer implements Person {
private String singerName;
private int age;
private Properties songsName;
private MusicalInstruments musicalInstrument;
public void perform(){
System.out.println("我是歌手"+singerName+",今年"+age+"岁,即将演唱的歌曲有‘"+ Arrays.asList(songsName)+"’");
if(musicalInstrument!=null){
musicalInstrument.perform();
}
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public void setAge(int age) {
this.age = age;
}
public void setSongsName(Properties songsName) {
this.songsName = songsName;
}
public void setMusicalInstrument(MusicalInstruments musicalInstrument) {
this.musicalInstrument = musicalInstrument;
}
}
- 配置文件
<bean id="guitar" class="com.chensr.test.Guitar"/>
<bean id="singer" class="com.chensr.test.Singer">
<property name="singerName" value="chensr"></property>
<property name="age" value="18"></property>
<property name="musicalInstrument" ref="guitar"></property>
<property name="songsName">
<props>
<prop key="英雄泪" >中文</prop>
<prop key="Hear me cry">英文</prop>
</props>
</property>
</bean>
- 运行测试类,控制台打印:
我是歌手chensr,今年18岁,即将演唱的歌曲有‘[{英雄泪=中文, Hear me cry=英文}]’
吉他表演...
5、Util的使用
- 通过util命名空间将集合定义成一个外部bean,再通过ref引用该集合bean
- 以Properties为例修改配置文件(添加util命名空间和集合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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<util:properties id="properties">
<prop key="英雄泪" >中文</prop>
<prop key="Hear me cry">英文</prop>
</util:properties>
<bean id="guitar" class="com.chensr.test.Guitar"/>
<bean id="singer" class="com.chensr.test.Singer">
<property name="singerName" value="chensr"></property>
<property name="age" value="18"></property>
<property name="musicalInstrument" ref="guitar"></property>
<property name="songsName" ref="properties"></property>
</bean>
</beans>
- 运行结果跟上文Properties例子是一样的