Spring依赖注入
Spring依赖注入
Spring的IOC(Inversion Of Control 控制反转)是通过DI(Dependency Injection 依赖注入)实现的,依赖注入在Spring主要有以下几种方式:
一、基于XML的依赖注入
基于XML的依赖注入,指的是修改XML配置文件来实现依赖注入,主要包括setter设值注入和构造注入,另外还有一种自动注入,自动注入主要是针对引用类型属性的注入。
我们对XML依赖注入的讨论基于以下类和测试:
public class Staff {
private String name;
private int age;
private Company company;
public Staff() { }
public Staff(String name, int age, Company company) {
this.name = name;
this.age = age;
this.company = company;
}
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; }
public void setCompany(Company company) { this.company = company; }
@Override
public String toString() {
return "Staff{" +
"name='" + name + '\'' +
", age=" + age +
", company=" + company +
'}';
}
}
public class Company {
private String name;
private String address;
public Company() { }
public Company(String name, String address) {
this.name = name;
this.address = address;
}
public void setName(String name) { this.name = name; }
public void setAddress(String address) { this.address = address; }
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
public class AppTest {
@Test
public void Test() {
String resource = "ApplicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(resource);
Staff staff = (Staff) context.getBean("Staff");
System.out.println(staff);
}
}
1、设值注入
使用
设值注入通过<property>标签完成,它有name、value和ref属性。
<property>属性:
name: 属性名
value: 简单类型值
ref: 引用类型值
-
对简单类型(基本类型 + String)的属性,使用
name和value属性:<property name="属性名" value="属性值"/> -
对引用类型的属性,使用
name和ref属性:<property name="属性名" ref="引用类型<bean>id属性值"/>
示例
测试
原理
我们在Staff和Company的无参构造函数和各属性setter方法中打印一些提示信息,以便于我们观察整个注入流程:
public class Staff {
private String name;
private int age;
private Company company;
public Staff() {
System.out.println("Staff's non-arg constructor."); //提示信息
}
public Staff(String name, int age, Company company) {
this.name = name;
this.age = age;
this.company = company;
}
public void setName(String name) {
System.out.println("Staff's setName " + name); //提示信息
this.name = name;
}
public void setAge(int age) {
System.out.println("Staff's setAge " + age); //提示信息
this.age = age;
}
public void setCompany(Company company) {
System.out.println("Staff's setCompany " + company); //提示信息
this.company = company;
}
@Override
public String toString() {
return "Staff{" +
"name='" + name + '\'' +
", age=" + age +
", company=" + company +
'}';
}
}
public class Company {
private String name;
private String address;
public Company() {
System.out.println("Company's non-arg constructor."); //提示信息
}
public Company(String name, String address) {
this.name = name;
this.address = address;
}
public void setName(String name) {
System.out.println("Company's setName " + name); //提示信息
this.name = name;
}
public void setAddress(String address) {
System.out.println("Company's setAddress " + address); //提示信息
this.address = address;
}
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
- Spring会先调用bean的无参构造函数,然后调用属性的setter方法,完成注入
运行观察:
对象的创建:
If no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. This means that, if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. In other words, the bean is instantiated (if it is not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.
- Spring是通过将"set"和<property>标签中的
name属性(首字符变大写)拼接形成的方法名来调用setter方法,例如:
<property name="age" value="19"/> <!--调用setAge方法-->
<property name="company" ref="Company"/> <!--调用setCompany方法-->
此时我们向Staff的<bean>标签中再添加一个<property>标签:
向Staff类中增加setAny方法(但没有any属性):
运行观察:
2、构造注入
使用
构造注入通过<constructor-arg>标签完成。它有name、index、value和ref属性。
<constructor-arg>属性:
name: 形参名
index: 形参位置, 从0开始
value: 简单类型值
ref: 引用类型值
-
对简单类型:
<constructor-arg name="形参名" value="形参值"/><constructor-arg index="形参位置,从0开始" value="形参值"/> -
对引用类型:
<constructor-arg name="形参名" ref="引用类型<bean>id属性值"/><constructor-arg index="形参位置,从0开始" ref="引用类型<bean>id属性值"/>
示例
测试
原理
我们在Staff和Company的有参构造函数中打印一些提示信息,以便于我们观察整个注入流程:
public class Staff {
private String name;
private int age;
private Company company;
public Staff() { }
public Staff(String name, int age, Company company) {
System.out.println("Staff's arg constructor."); //提示信息
this.name = name;
this.age = age;
this.company = company;
}
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; }
public void setCompany(Company company) { this.company = company; }
@Override
public String toString() {
return "Staff{" +
"name='" + name + '\'' +
", age=" + age +
", company=" + company +
'}';
}
}
public class Company {
private String name;
private String address;
public Company() { }
public Company(String name, String address) {
System.out.println("Company's arg constructor."); //提示信息
this.name = name;
this.address = address;
}
public void setName(String name) { this.name = name; }
public void setAddress(String address) { this.address = address; }
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
- Spring会调用对应的有参构造函数,完成注入
运行观察:
TODO:至于对象的创建顺序,这里挖一个坑,以后再填。
3、自动注入
自动注入是针对引用类型属性的注入。当我们的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="xxx" class="xxx.xxx.xxx">
<property name="..." ref="..."/>
<property name="..." ref="..."/>
<property name="..." ref="..."/>
<property name="..." ref="..."/>
...
</bean>
<bean id="xxx" class="xxx.xxx.xxx">
...
</bean>
<bean id="xxx" class="xxx.xxx.xxx">
...
</bean>
<bean id="xxx" class="xxx.xxx.xxx">
...
</bean>
...
</beans>
ref引用的都是其他<bean>,那我们有什么办法让Spring自己在配置文件中搜索相应的<bean>,并注入到相应属性当中去呢?Spring提供了byName和byType两种方法。
准备工作
-
为了完成此部分的分析,我们添加一个
Company的子类InternetCompany:public class InternetCompany extends Company { public InternetCompany() { System.out.println("InternetCompany's non-arg constructor"); } public InternetCompany(String name, String address) { super(name, address); System.out.println("InternetCompany's arg constructor"); } } -
类型同源:
- 相同类型
- A是B的父类
- A是接口,B是其实现类
使用
-
byName
byName通过在<bean>标签中将
autowire属性设为byName实现,Spring会查找和属性同名的<bean>标签,如果类型同源则注入。因为有<bean>标签id属性和类属性同名的限制,所以如果有多个同源<bean>也无所谓。 -
byType
byType通过在<bean>标签中将
autowire属性设为byType实现,Spring会查找同源bean,并注入。不可以有多个同源<bean>标签。
示例
- byName
- byType
测试
- byName
- byType
二、基于注解的依赖注入
常用注解:
- @Component (@Repository、@Service、@Controller)
- @Value
- @Autowired
- @Qualifier
- @Resource
我们先讲解这几个注解的用法,然后通过示例展示。
一、@Component
@Repository: 放在dao的实现类上面, 创建dao对象
@Service: 放在service的实现类上面, 创建service对象
@Controller: 放在控制器类上面, 创建控制器对象
@Component:
- 作用:创建对象, 等同于<bean>标签
- 属性:value值等同于<bean>标签的id值,若未设置value值,则使用首字母小写的类名
- 位置:放在除以上类的上面,一般是util类
二、@Value
@Value:
- 作用:简单类型赋值
- 属性:value值即为属性值
- 位置:
- 在简单属性定义上面,无需setter方法
- 在setter方法上面,调用setter方法
三、@Autowired
@Autowired:
-
作用:引用类型赋值,自动注入。支持byName和byType,默认是byType
-
属性:required
- 默认为true,表示必须存在满足条件的bean,不然就产生异常;
- 如果为false,则不要求必须存在满足条件的bean
-
位置:
- 在引用类型属性定义上面,无需setter方法
- 在setter方法上面,调用setter方法
四、@Qualifier
@Qualifier
- 作用:和@Autowired配合完成byName功能
- 属性:value,引用的其他bean的id值
- 位置:
- 在引用类型属性定义上面,和@Autowired无先后顺序,无需setter方法
- 在setter方法上面,和@Autowired无先后顺序,调用setter方法
五、@Resource
@Resource
-
作用:引用类型赋值,自动注入。默认先byName,后byType
-
属性:
- name,只要指定了name的值,就只采用byName
- type,采用byType
-
位置:
- 在引用类型属性定义上面,无需setter方法
- 在setter方法上面,调用setter方法
六、示例
Company:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "Company") // Company
public class Company {
@Value("Google") // @Value 简单属性上
private String name;
@Value("California") // @Value 简单属性上
private String address;
public Company() {
System.out.println("Company's non-arg constructor.");
}
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
Staff示例1:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "Staff") // @Component
public class Staff {
@Value(value = "小明") // @Value 简单属性上
private String name;
@Value(value = "19") // @Value 简单属性上
private int age;
@Autowired // @Autowired 默认byType
private Company company;
public Staff() {
System.out.println("Staff's non-arg constructor.");
}
@Override
public String toString() {
return "Staff{" +
"name='" + name + '\'' +
", age=" + age +
", company=" + company +
'}';
}
}
Staff示例2:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "Staff") // @Component
public class Staff {
@Value(value = "小明") // @Value 简单属性上
private String name;
private int age;
@Autowired // @Autowired
@Qualifier(value = "Company") // @Qualifier 和上面的@Autowired 完成byName
private Company company;
public Staff() {
System.out.println("Staff's non-arg constructor.");
}
@Value(value = "19") // @Value 简单属性setter方法上
public void setAge(int age) {
System.out.println("Staff's setAge " + age);
this.age = age;
}
@Override
public String toString() {
return "Staff{" +
"name='" + name + '\'' +
", age=" + age +
", company=" + company +
'}';
}
}
Staff示例3:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component(value = "Staff") // @Component
public class Staff {
@Value(value = "小明") // @Value 简单属性上
private String name;
private int age;
@Resource // @Resource默认先byName再byType
private Company company;
public Staff() {
System.out.println("Staff's non-arg constructor.");
}
@Value(value = "19") // @Value 简单属性setter方法上
public void setAge(int age) {
System.out.println("Staff's setAge " + age);
this.age = age;
}
@Override
public String toString() {
return "Staff{" +
"name='" + name + '\'' +
", age=" + age +
", company=" + company +
'}';
}
}
示例1、2、3结果:
> Annotation injection is performed before XML injection. Thus, the XML configuration overrides the annotations for properties wired through both approaches.

浙公网安备 33010602011771号