什么是DI
DI 就是依赖注入,,简单来讲,其实就是,把 IoC 容器中的 bean 对象取出来直接放到某个类的属性中,不再需要我们自己手的的new对象。例如下面代码,
在 Controller 包中创建了一个 SayHiContreoller 类,在 ServiceDemo 包中创建了一个SayHiService 类,现在,想要在 SayHiController 类中的 sayHi 方法里,调用 SayHiService 类中的sayHi()方法,所以,就需要手动的New一个SayHiService对象,然后再调用sayHi()方法,那么,现在不再手动的new对象了,而使用依赖注入的方式。
1.手动创建对象的方式
2.使用DI的方式
下图 Ioc 容器在创建 SayHiController 对象时,需要 SayHiService 对象,所以,通过 DI 的方式提供给 SayHiController 运行时所需要的资源(SayHiService对象)
关于依赖注入,不仅仅只有 @Autowired 一个注解,Spring 给我们提供了三种方式,如下
依赖注入的三种方式
- 属性注入(Filed Injection)
- 构造方法注入(Constructor Injection)
- Setter 注入(Setter Injection)
1.属性注入
“属性注入” 就是使用 @Autowired 注解,也就是上面我们演示的,将SayHiService 类注入到 SayHiController 类中。
SayHiController 类的代码实现:
@RestController
public class SayHiController {
@Autowired
private SayHiService service;
public void sayHi() {
service.sayHi();
}
}
SayHiService 类的代码实现:
@Service
public class SayHiService {
public void sayHi() {
System.out.println("hello SayHiService");
}
}
获取 SayHiController 中的 sayHi()
@SpringBootApplication
public class SpringDemo7Application {
public static void main(String[] args) {
//获取Spring上下文对象
ConfigurableApplicationContext context = SpringApplication.run(SpringDemo7Application.class, args);
//从上下文中获取到SayHiController对象
SayHiController bean = (SayHiController)context.getBean(SayHiController.class);
bean.sayHi();
}
}
去掉 @AutoWired 注解,再次运行,就会出现空指针异常
2.构造方法注入
构造方法注入是在类的构造方法中实现注入的,如下代码:
@RestController
public class SayHiController {
private SayHiService service;
@Autowired
public SayHiController(SayHiService service) {
this.service = service;
}
public void sayHi() {
service.sayHi();
}
}
注意:如果类中只有一个构造方法,@Autowired 注解可以省略,但如果有多个注解,那么就要使用 @Autowired 注解明确的指定使用哪个构造方法,如下代码演示:
3.Setter 注入
Setter 注入和属性的 Setter 方法类似,只不过要在set方法上使用 @Autowired 注解修饰,代码如下:
@RestController
public class SayHiController {
private SayHiService service;
@Autowired
public void setService(SayHiService service) {
this.service = service;
}
public void sayHi() {
service.sayHi();
}
}
同样,如果不加 @Autowired 注解就会报空指针异常
三种注入的优缺点:
-
属性注入
-
优点:简洁,适用方便,直接适用 @Autowired 修饰即可
-
缺点:
-
只能用于 IoC 容器,如果是非 Ioc 容器则不可用,并且只有在使用的时候才会出现空指针异常
-
不能注入一个 final 修饰的属性,如下图:
但是,如果想要注入一个 final 修饰的属性,有一个要求,需要满足以下任意一个条件:
1,在属性声明时要完成初始化,但是,如果声明时就已经初始化了,就不需要再进行注入了呀,所以这条等于没用
2,要在构造方法中进行赋值,如果在构造方法中进行赋值的话,直接使用构造函数注入就行了呀,所以这条也没用。
所以,这条缺点与下面的构造方法注入对比来讲。
-
-
-
构造方法注入
- 优点:
- 可以注入 final 修饰的属性,因为它本来就是在构造方法中进行赋值的
- 注入的对象不会被修改,因为依赖对象在使用前就已经被初始化了
- 依赖对象在使用前一定会被初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会被执行的
- 通用性好,构造方法是JDK支持的,所以更换任何框架,它都是适用的
- 缺点:
- 注入多个对象时,代码会比较繁琐
- 优点:
-
Setter 注入
- 优点:方便在类实例化之后,重新对该对象进行配置或注入
- 缺点:
- 不能注入一个 final 修饰的属性
- 注入对象可能会被改变,因为setter方法可能会给多次调用,就有被修改的风险
@Autowired 存在的问题
使用方法注解对同一个类型创建多个 bean对象时,使用 @Autowired 就会报错,代码如下:
创建一个 User 类
@Data
public class User {
private String name;
private int age;
}
创建一个 BeanConfig 类, 使用方法注解在该类中创建两个 User 类型的对象
@Service
public class BeanConfig {
@Bean
public User user1() {
User user = new User();
user.setName("张三");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("李四");
user.setAge(20);
return user;
}
}
注入Bean对象
@RestController
public class SayHiController {
@Autowired
private User user;
public void sayHi() {
System.out.println(user);
}
}
在使用 @Autowired 进行注入时,就会报出以下错误,表示有多个“User”类型的bean
如果将变量名 user 改成 user1 或者 user2 就可以运行成功
通过上述的验证,就可以发现,当同样类型存在多个bean对象时,就可能会出错,而 @Autowired 是先根据 bean 名称来获取bean对象,如果匹配到了,则成功,如果没有匹配到,就会根据类型进行匹配,如果匹配到多个,就会报错。但是通常不会修改变量名来获取某个bean,而是通过其它的手段来指定bean的名称。解决方法如下:
解决方案
Spring 为我们提供了以下几种方案:
- @Primary
- @Qualifier
- @Resource
使用 @Primary 注解:当存在多个相同类型的bean对象时,加上 @Primary 注解来确定默认的 bean 对象
@Service
public class BeanConfig {
@Primary //指定bean为默认bean的实现
@Bean
public User user1() {
User user = new User();
user.setName("张三");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("李四");
user.setAge(20);
return user;
}
}
使用 @Qualifier 注解:指定当前要注入的bean的名称,在 @Qualifier 的 value 属性上,指定注入bean的名称
注意,@Qualifier 注解不能单独使用,要配合 @Autowired 注解使用,如下图:
@RestController
public class SayHiController {
@Qualifier("user2")
@Autowired
private User user;
public void sayHi() {
System.out.println(user);
}
}
使用 @Resource 注解:按照 bean 的名称进行注入。通过 @Resource 的name属性来指定要注入的bean的名称,代码如下:
@RestController
public class SayHiController {
@Resource(name = "user2")
private User user;
public void sayHi() {
System.out.println(user);
}
}
而且,@Resource 注解是属于 JDK 里的,而不是属于 Spring 的,Spring 只是通过其他方式进行了实现,所以,就可以知道在Spring之前就有了注解,如下图:
标签:Autowired,Spring,DI,User,bean,user,图解,public,注入 From: https://blog.csdn.net/Shine0115/article/details/141368728@Autowired 和 @Resource 的区别
1.@Autowired 是 Spring 提供的,@Resource 是 JDK 提供的
2.@Autowired 是先根据名称来获取bean对象,如果匹配到了,则成功,如果没有匹配到,就会根据类型进行匹配,如果匹配到多个,检查有没有通过 @Qualifier指定名称,如果没有,则报错,@Resource 是按照名称注入的,相比 @Autowired 来说,@Resource 支持更多的参数设置,例如name设置,根据名称获取Bean