目录
一.DI的定义
DI(依赖注入)是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象。简单来说,就是把对象取出来放到某个类的属性中。
注意,在有一些文章中,依赖注入也被称为“对象注入”,“属性装配"
二.Spring提供的依赖注入的方式
关于依赖注入,Spring 提供的三种方式:
- 属性注入(Field Injection)
- 构造方法注入(Constructor Injection)
- Setter 注入(Setter Injection)
a.属性注入
属性注入是使用@Autowired实现的。
演示代码如下
代码结构如下
UserService代码如下
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void say(){
System.out.println("this is UserService");
}
}
UserController代码如下
package com.example.demo.controller;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
//方法1 属性注入
@Autowired
private UserService userService;
public void say(){
System.out.println("this is UserController");
userService.say();
}
}
DemoApplication代码如下
package com.example.demo;
import com.example.demo.controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
UserController userController= (UserController) context.getBean("userController");
userController.say();
}
}
运行结果如下
去掉@Autowired
会报NullpointerException,因为userService是空的
b.构造方法注入

UserController2代码如下
package com.example.demo.controller;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController2 {
// 注入方法2 构造方法
private UserService userService;
@Autowired
public UserController2(UserService userService){
this.userService=userService;
}
public void say(){
System.out.println("this is UserController2");
userService.say();
}
}
DemoApplication代码如下
package com.example.demo;
import com.example.demo.controller.UserController;
import com.example.demo.controller.UserController2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
// UserController userController= (UserController) context.getBean("userController");
UserController2 userController2= (UserController2) context.getBean("userController2");
userController2.say();
}
}
运行结果如下
注意事项
注意事项:如果类只有一个构造方法,那么@Autowired
注解可以省略;如果类中有多个构造方法,那么需要添加上@Autowired
来明确指定到底使用哪个构造方法。
如果有多个构造方法,不添加@Autowired
稍微修改一下代码结构
新添加一个StudentService类
StudentService类代码如下
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
public void say(){
System.out.println("this is StudentService");
}
}
并在UserController2类中增加一个构造方法
运行结果如下
java.lang.NoSuchMethodException
异常在这个上下文中通常表示 Spring 找不到默认的无参构造方法
那我们如果加上无参构造方法会怎么样
运行结果
通过这个异常,我们就可以推出结论,当有多个构造方法时,会选择默认的无参构造方法.
我们也可以通过@Autowired来指定默认的构造方法
运行结果
结论
- 如果只有一个构造方法,@Autowired 可以省略
- 如果存在多个构造方法,默认的构造方法是 无参的构造方法,也可以通过 @Autowired 来指定默认的构造方法
c.Setter注入
Setter注入和属性的Setter方法实现类似,只不过在设置set方法的时候需要加上@Autowired注解
代码结构如下
UserController3代码如下
package com.example.demo.controller;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController3 {
//注入方法3 setter方法注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void say(){
System.out.println("this is UserController3");
userService.say();
}
}
DemoApplication代码如下
package com.example.demo;
import com.example.demo.controller.UserController;
import com.example.demo.controller.UserController2;
import com.example.demo.controller.UserController3;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
// UserController userController= (UserController) context.getBean("userController");
// UserController2 userController2= (UserController2) context.getBean("userController2");
UserController3 userController3= (UserController3) context.getBean("userController3");
userController3.say();
}
}
结果如下
去掉@Autowired
结果如下
不能调用,因为userService是空的。
d.三种注入方法的优缺点
- 属性注入
- 优点:简洁,使用方便;
- 缺点:
- 只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)
- 不能注入一个 Final 修饰的属性
- 构造函数注入 (Spring 4.X 推荐)
- 优点:
- 可以注入 final 修饰的属性
- 注入的对象不会被修改
- 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.
- 通用性好,构造方法是 JDK 支持的,所以更换任何框架,他都是适用的
- 缺点:
- 注入多个对象时,代码会比较繁琐
- 优点:
- Setter 注入 (Spring 3.X 推荐)
- 优点:方便在类实例之后,重新对该对象进行配置或者注入
- 缺点:
- 不能注入一个 Final 修饰的属性
- 注入对象可能会被改变,因为 setter 方法可能会被多次调用,就有被修改的风险.
三. @Autowired存在的问题
当同一类型存在多个bean是,使用@Autowired会存在问题
代码结构如下
User类代码
package com.example.demo.model;
import lombok.Data;
@Data
public class User {
private String name;
private int age;
}
UserService类代码
import com.example.demo.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// public void say(){
// System.out.println("this is UserService");
// }
@Bean("u1")
public User user1(){
User user=new User();
user.setName("Maybe");
user.setAge(15);
return user;
}
@Bean
public User user2(){
User user=new User();
user.setName("lisi");
user.setAge(18);
return user;
}
}
UserController类代码
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
// 方法1 属性注入
@Autowired
private UserService userService;
@Autowired
private User user;
public void say(){
System.out.println("this is UserController");
System.out.println(user);
}
}
DemoApplication类代码
package com.example.demo;
import com.example.demo.controller.UserController;
import com.example.demo.controller.UserController2;
import com.example.demo.controller.UserController3;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//获取Spring上下文
ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
UserController userController= (UserController) context.getBean("userController");
// UserController2 userController2= (UserController2) context.getBean("userController2");
// UserController3 userController3= (UserController3) context.getBean("userController3");
// userController3.say();
userController.say();
}
}
运行结果
UserContro只需要一个Bean对象,但是出现了2个。
解决方案如下
Spring提供了以下几种解决方案:
@Primary
@Qualifier
使用@Qualifier
注解:指定当前要注入的 bean 对象。在@Qualifier
的 value 属性中,指定注入的 bean 的名称。
@Qualifier
注解不能单独使用,必须配合@Autowired
使用
没有和@Autowired搭配使用
则结果为null
@Resource
使用@Resource
注解:是按照 bean 的名称进行注入。通过name
属性指定要注入的 bean 的名称。
四.@Autowired和@Resource的区别
@Autowired
是 Spring 框架提供的注解,而@Resource
是 JDK 提供的注解@Autowired
默认是按照类型注入,而@Resource
是按照名称注入。相比于@Autowired
来说,@Resource
支持更多的参数设置,例如 name 设置,根据名称获取 Bean。