所谓自动装配,就是Spring 自动从 Spring 容器当中获取 Bean 对象并注入到需要的属性上的过程,也叫依赖注入(DI)。要想了解都有哪些装配方法,首先需要了解 Spring 对 Bean 的命名规则。Bean 的命名规则http://t.csdn.cn/45BwD
Spring 中实现依赖注入有以下三种方法:
- 属性注入(Field Injection)
- Setter 注入(Setter Injection)
- 构造器注入(Constructor Injection)
简单类型注入
简单类型的注入使用 @Value 注解,该注解可以出现在属性上、setter 上和构造器的入参上。使用方法如下:
// 这里三种方法全部写了,实际使用任选其一,一般推荐使用属性注入方式
@Component
public class User {
@Value("张三")
private String name;
@Value("张三")
public void setName(String name) {
this.name = name;
}
public User(@Value("张三") String name) {
this.name = name;
}
}
对象类型注入
属性注入
属性注入的实现代码共有三种情况,当然 Setter 注入和构造器注入也有类似情况,所以统一在属性注入这里详细描述,后面谈到 Setter 注入和构造器注入时就不过多赘述了。具体实现代码如下:
// 情况一:容器中只有一个类型匹配上的 Bean 的注入情况
@Component
public class FieldInjectionDemo {
@Autowired
private Student student;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
FieldInjectionDemo demo = context.getBean(FieldInjectionDemo.class);
System.out.println(demo.student.name);
}
}
@Component
class Students {
@Bean
public Student stu1() {
return new Student("张一");
}
}
class Student {
String name;
public Student(String name) {
this.name = name;
}
}
情况一运行结果:
// 情况二:容器中有多个类型匹配上的 Bean 的注入情况
@Component
public class FieldInjectionDemo {
@Autowired
private Student student;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
FieldInjectionDemo demo = context.getBean(FieldInjectionDemo.class);
System.out.println(demo.student.name);
}
}
@Component
class Students {
@Bean
public Student stu1() {
return new Student("张一");
}
@Bean
public Student stu2() {
return new Student("张二");
}
@Bean
public Student student() {
return new Student("张三");
}
}
情况二运行结果:
// 情况三:容器中有多个类型匹配上的 Bean,但是 Bean 名称无法与属性名相吻合的注入情况
@Component
public class FieldInjectionDemo {
@Autowired
@Qualifier("stu2")
private Student student;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
FieldInjectionDemo demo = context.getBean(FieldInjectionDemo.class);
System.out.println(demo.student.name);
}
}
@Component
class Students {
@Bean
public Student stu1() {
return new Student("张一");
}
@Bean
public Student stu2() {
return new Student("张二");
}
@Bean
public Student stu3() {
return new Student("张三");
}
}
情况三运行结果:
Setter 注入
Setter 注入的实现代码如下:
@Component
public class SetterInjectionDemo {
private Student student;
@Autowired
public void setStudent(Student student) {
this.student = student;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
SetterInjectionDemo demo = context.getBean(SetterInjectionDemo.class);
System.out.println(demo.student.name);
}
}
@Component
class Students {
@Bean
public Student stu1() {
return new Student("张一");
}
}
构造器注入
构造器注入的实现代码如下:
@Component
public class ConstructorInjectionDemo {
private Student student;
@Autowired
public ConstructorInjectionDemo(Student student) {
this.student = student;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ConstructorInjectionDemo demo = context.getBean(ConstructorInjectionDemo.class);
System.out.println(demo.student.name);
}
}
@Component
class Students {
@Bean
public Student stu1() {
return new Student("张一");
}
}
当构造器只有一个的时候,还可以将 @Autowired 省略,如:
@Component
public class ConstructorInjectionDemo {
private Student student;
public ConstructorInjectionDemo(Student student) {
this.student = student;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ConstructorInjectionDemo demo = context.getBean(ConstructorInjectionDemo.class);
System.out.println(demo.student.name);
}
}
当容器中存在多个类型匹配上的 Bean 时需要注意的是:@Qualifier 不可以标记到构造器上,但是可以标记到构造器入参上,代码如下:
@Autowired
public FieldInjectionDemo(@Qualifier("stu2") Student student) {
this.student = student;
}
使用方法总结
-
使用 @Autowired 进行对象注入时,首先根据对象的类型从容器中获取对象,如果类型匹配上的对象只有一个,那么就直接将对象注入;
-
如果容器中有多个匹配的对象,此时会根据属性名 / 入参名和容器中的 Bean 的名称进行匹配注入;
-
如果容器中有多个匹配的对象,但是没有一个 Bean 名称与属性名 / 入参名相吻合,那么此时需要配合注解 @Qualifier 来指定要注入的 Bean 名称。
@Resource
- @Resource 注解是 JDK 扩展包中的,也就是说属于 JDK 的一部分。所以该注解是标准注解,更加具有通用性。
- 执行流程:默认根据 name 装配,未指定 name 时,使用属性名作为 name。通过 name 找不到的话会通过类型装配。
- 只能用在属性上和 setter 上,不能用在构造器上。
@Resource 注解属于 JDK 扩展包,所以不在 JDK 当中,需要额外引入以下依赖:【如果是 JDK8 的话不需要引入。高于 JDK11 或低于 JDK8 需要引入】
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
一定要注意:如果你用 Spring 6,要知道 Spring 6 不再支持 JavaEE,它支持的是 JakartaEE9。(Oracle 把 JavaEE 贡献给 Apache 了,Apache 把 JavaEE 的名字改成 JakartaEE 了,大家之前所接触的所有的 javax.* 包名统一修改为 jakarta.* 包名了)
关于 @Autowired 和 @Resource 的其他区别请见磊哥博客:@Autowired 报错的 4 种情况!http://t.csdn.cn/QV56w
三种注入方式的优缺点
- 属性注⼊
- 优点:简洁,使⽤⽅便;
- 缺点:过于依赖 IoC 容器,如果没有 IoC 容器则不可⽤;并且不能为 final 修饰的变量注入对象。
- Setter 注入
- 优点:通用性好,不依赖 IoC 容器,即使没有 IoC 容器也可以手动调用 setter 注入。可以解决循环依赖问题。
- 缺点:不能为 final 修饰的变量注入对象;注入的对象可被修改,因为在任意位置都可以调用 Setter 来重新赋值。
- 构造器注入
- 优点:可以为 final 修饰的变量注入对象;注入的对象不能被修改,因为创建对象的时候才会调用构造器,并且只会调用一次;通用性更好,不依赖 IoC 容器;构造器注入是目前 Spring 官方推荐的方式。