DI详解
DI的意义是容器在创建Bean的时候能够提供依赖对象进行使用
DI的具体使用有三种注入方式:属性注入,方法构造注入,Setter注入
属性注入
属性注入是通过注解@Autowired实现将Bean的实例注入到另一个Bean的实例中
例如,我有一个类由@Service进行Bean管理,现在我要将Service中的实例注入到Controller中
@Service
public class UserService {
public void sayHi() {
System.out.println("Hi,UserService");
}
}
Controller类,将Service中的Bean实例通过@Autowired进行注入
@Controller
public class UserController {
//注⼊⽅法1: 属性注⼊
@Autowired
private UserService userService;
public void sayHi(){
System.out.println("hi,UserController...");
userService.sayHi();
}
}
在启动类实例化controller,调用其中的方法看看会是什么结果
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
//获取Spring上下⽂对象
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
//从Spring上下⽂中获取对象
UserController userController = (UserController)
context.getBean("userController");
//使⽤对象
userController.sayHi();
}
}
我们可以观察到在将Service中的Bean实例注入到Controller的Bean实例后,两个Bean实例产生了依赖关系,后续实例化Controller的实例调用方法的时候将Service实例中的方法也有执行
构造方法注入
构造方法注入就是使用@Autowired注解在构造方法中进行Bean实例注入
@Controller
public class UserController {
//注⼊⽅法: 构造方法注⼊
private UserService userService;
@Autowired
public UserController(UserService userService)
{
this.userService = userService;
}
public void sayHi(){
System.out.println("hi,UserController...");
userService.sayHi();
}
}
Setter注入
Setter注入和属性的Setter方法实现类似,就是需要添加@Autowired注解
@Controller
public class UserController {
//注⼊⽅法: Setter注⼊
private UserService userService;
@Autowired
public void setUserService(UserService userService)
{
this.userService = userService;
}
public void sayHi(){
System.out.println("hi,UserController...");
userService.sayHi();
}
}
三种注入方式的区别:
属性注入——优点:简洁,用起来方便。缺点:只能用于Ioc容器,无法注入final定义的属性
构造方法——优点:通用性强——因为构造方法是JDK支持的;会对注入对象进行初始化且注入的对象不会被修改,还能注入被final定义的属性
缺点:注入多个对象的时候会比较繁琐,需要频繁重新写构造方法
Setter注入——优点:方便在类实例之后进行配置或者注入 缺点:面临setter方法被频繁调用导致注入对象被修改的风险,不能注入final定义的变量
@Autowired存在的问题
如果遇到一个依赖中有多个Bean,单纯引用@Autowired进行注入会存在问题,依赖对象交给Ioc管理的Bean究竟是哪个?(无特殊声明,Bean默认是单例模式)
为了解决这一个问题,延伸出@Resource注解,在依赖注入时当依赖对象有多个Bean开发者更多的时选择使用@Resource,通过name属性来指定依赖对象中对应的Bean,这是最为干脆的解决方法
@Controller
public class UserController {
@Resource(name = "user2")
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}
还有一个方法就是——既然依赖对象有多个Bean,那么开发者直接指定其中的一个Bean作为依赖注入时默认使用的Bean,这个注解就是@Primary和@Qualifier
@Component
public class BeanConfig {
@Primary //指定该bean为默认bean的实现
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}
@Qualifier和@Primary的区别就在于前者需要和@Autowired搭配使用,且前者是在注入的时候进行声明要指定哪个Bean作为注入的Bean,后者是在依赖对象中对Bean进行设置
@Controller
public class UserController {
@Qualifier("user2") //指定bean名称
@Autowired
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}
经典面试题:@Autowired和@Resource的区别?
①@Autowired是Spring框架提供的注解,@Resource是JDK提供的注解
②@Autowired是按照类型进行注入,@Resource是根据name来进行注入,相比于前者,后者更支持更多的参数设置