Spring DI
介绍
Spring DI 是 依赖注入,上一章我们学习到的 IoC 是控制反转,也就说将对象交由 Spring 来管理,也可以看作是一个存储的过程。而 Spring DI 就是从Spring 容器中获取对象的过程,也就说将对象赋值【注入】给某个引用
注入方式
Spring DI 注入的方式一共有三种:分别为 属性注入、构造方法注入、Setter 注入
我们以下面的 User 来举例
@AllArgsConstructor
@Data
public class User {
private String name;
private Integer age;
}
@Repository
public class Test {
@Bean
public User u1() {
return new User("zhangsan", 14);
}
}
我们将 u1 这个对象交给Spring 管理
属性注入
属性注入方式就是在属性上面添加 @Autowired 注解,注意 一个 @Autowired 注解只能对应一个 属性
@Controller
public class TestDI {
@Autowired
private User user;
public void print() {
System.out.println(user);
}
}
构造方法注入
构造方法注入在某些情况下可以省略 @Autowired 注解
private User user;
public TestDI(User user) {
this.user = user;
System.out.println(user);
}
依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法:
如果出现多个构造方法的话,默认的构造方法注入是 无参的构造方法:
@Controller
public class TestDI {
private User user;
public TestDI() {}
public TestDI(User user) {
this.user = user;
}
public void print() {
System.out.println(user);
}
}
最后发现 user 根本没有注入进去
如果你想要指定构造方法注入,可以在构造方法上加上 @Autowired 注解
@Autowired
public TestDI(User user) {
this.user = user;
}
如果只有一个构造方法那么 @Autowired 就可以省略
如果存在多个构造方法,可以通过 @Autowired 来指定默认 的构造方法
Setter 注入
Setter 注入需要在上面加上 @Autowired 注解
private User user;
@Autowired
public void setUser(User user) {
this.user = user;
}
优缺点总结
属性注入:
优点:简洁方便
缺点:只能使用于IoC 容器中,不能注入一个 final 修饰的属性
构造方法注入:
优点:可以注入final 修饰的属性,注入的对象不会被修改
依赖对象在使用前一定会被完全初始化,因为依赖是在构造方法中执行的,而构造方法是在类加载阶段就会执行的方法
通用性好,构造方法是JDK 支持的,因此可以更换任何框架
缺点:注入多个对象的时候,代码会比较繁琐
setter 注入:
优点:方便在类实例之后,重新对该对象进行配置或者注入
缺点:不能注入一个 final 修饰的属性
注入对象可能会被改变,因为 setter 方法可能被多次调用,就会有被修改的风险
多个对象的注入
在上面我们粗略感受三大注入方式,但是 一个 @Autowired 只能对应一个对象,如果一个类型有多个对象的话,我们需要指定对应的对象来进行依赖注入:
@Repository
public class Test {
@Bean
public User u1() {
return new User("zhangsan", 14);
}
@Bean
public User u2() {
return new User("lisi",15);
}
}
如果我们还是使用下面的方式进行依赖注入的话,就会失败
@Autowired
private User user;
上面的信息是指依赖注入需要 TestDI 的单例 bean ,但是找到了两个 bean
也就说如果一个类型创建了对应的多个 bean 的话,只是使用 @Autowired 是无法完成注入工作的,因为 Spring 不知道要使用哪个对象来进行注入
下面的 Action 给了我们两个解决方案,使用 @Primary 或者 @Qualifier 注解,下面我们就来讲解一下
@Primary
@Primary 指定默认的依赖对象注入
在 u1 再加上 @Primary 注解:
@Repository
public class Test {
@Primary
@Bean
public User u1() {
return new User("zhangsan", 14);
}
@Bean
public User u2() {
return new User("lisi",15);
}
}
那么下面的 user 就会被注入 zhangsan 这个对象
@Controller
public class TestDI {
@Autowired
private User user;
public void print() {
System.out.println(user);
}
}
如果是在 lisi 上面加上 @Primary 注解,那就是 默认 lisi 为默认注入对象
@Repository
public class Test {
@Bean
public User u1() {
return new User("zhangsan", 14);
}
@Primary
@Bean
public User u2() {
return new User("lisi",15);
}
}
@Qualifier
@Qualifier 是在 @Autowired 上面添加的,用来指定要注入的对象的名称
@Qualifier("u1")
@Autowired
private User user;
public void print() {
System.out.println(user);
}
通过查看源码,不仅可以得知 @Qualifier 可以使用在方法上,也可以使用在参数上:
@Repository
public class Test {
@Bean
public User u1() {
return new User("zhangsan", 14);
}
@Bean
public User u2() {
return new User("lisi",15);
}
}
@Autowired
public void setUser(@Qualifier("u2") User u1) {
this.user = u1;
}
@Resource
通过 name 属性来指定 对应 的 bean
@Resource(name = "u1")
private User user;
@Autowired 和 @Resource 的区别:
@Autowired 是 Spring 框架提供的注解,而 @Resource 是 JDK 提供的注解
@Autowired 是默认按照类型注入的,而 @Resource 是按照名称注入的,相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 的设置,根据 名称来获取 Bean
注入流程
只找到一个? 这种情况要么就是只有一个 bean 对象,要么就是使用了 @Primary 来指定默认的 bean 对象。