1. IOC 详解
1.1 Bean 的声明
IOC 控制反转,就是将对象的控制权交给 Spring 的 IOC 容器,由 IOC 容器创建及管理对象。IOC 容器创建的对象称为 bean 对象。
而 Spring 框架为了更好的标识 Web 应用程序开发当中,bean 对象到底归属于哪一层,又提供了 @Component
的衍生注解:

注意:
- 声明 bean 的时候,可以通过注解的 value 属性指定 bean 的名字,如果没有指定,默认为类名首字母小写。
- 使用以上四个注解都可以声明 bean,但是在 springboot 集成 web 开发中,声明控制器 bean 只能用
@Controller
。
1.2 组件扫描
问题:使用前面学习的四个注解声明的 bean,一定会生效吗?
答案:不一定。(原因:bean 想要生效,还需要被组件扫描)
- 前面声明 bean 的四大注解,要想生效,还需要被组件扫描注解
@ComponentScan
扫描。 - 该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解
@SpringBootApplication
中,默认扫描的范围是启动类所在包及其子包。

所以,我们在项目开发中,只需要按照如上项目结构,将项目中的所有的业务类,都放在启动类所在包的子包中,就无需考虑组件扫描问题。
2. DI 详解
上面我们讲解了控制反转 IOC 的细节,接下来呢,我们了解依赖注解 DI 的细节。
依赖注入,是指 IOC 容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。
使用 @Autowired
这个注解,完成依赖注入的操作,而这个 Autowired 翻译过来叫:自动装配。
@Autowired
注解,默认是按照类型进行自动装配的(去 IOC 容器中找某个类型的对象,然后完成注入操作)
2.1 @Autowired
用法
@Autowired
进行依赖注入,常见的方式,有如下三种:
-
属性注入
@RestController public class UserController {//方式一: 属性注入@Autowiredprivate UserService userService; }
- 优点:代码简洁、方便快速开发。
- 缺点:隐藏了类之间的依赖关系、可能会破坏类的封装性。
-
构造函数注入
@RestController public class UserController {//方式二: 构造器注入private final UserService userService;@Autowired //如果当前类中只存在一个构造函数, @Autowired可以省略public UserController(UserService userService) {this.userService = userService;} }
- 优点:能清晰地看到类的依赖关系、提高了代码的安全性。
- 缺点:代码繁琐、如果构造参数过多,可能会导致构造函数臃肿。
- 注意:如果只有一个构造函数,
@Autowired
注解可以省略。(通常来说,也只有一个构造函数)。
省略写法(推荐):
@RestController @RequiredArgsConstructor // 会自动生成 final 变量的构造函数 public class UserController {private final UserService userService; // 必须使用 final 修饰符 }
-
setter 注入
// 用户信息 Controller @RestController public class UserController {//方式三: setter 注入private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;} }
- 优点:保持了类的封装性,依赖关系更清晰。
- 缺点:需要额外编写 setter 方法,增加了代码量。
在项目开发中,基于 @Autowired
进行依赖注入时,基本都是第一种和第二种方式。(官方推荐第二种方式,因为会更加规范)但是在企业项目开发中,很多的项目中,也会选择第一种方式因为更加简洁、高效(在规范性方面进行了妥协)。
2.2 注意事项
那如果在 IOC 容器中,存在多个相同类型的 bean 对象,会出现什么情况呢?
在下面的例子中,我们准备了两个 UserService 的实现类,并且都交给了IOC容器管理。 代码如下:

此时,我们启动项目会发现,控制台报错了:

出现错误的原因呢,是因为在 Spring 的容器中,UserService 这个类型的 bean 存在两个,框架不知道具体要注入哪个 bean 使用,所以就报错了。
解决方案:
-
方案一:使用
@Primary
注解当存在多个相同类型的 Bean 注入时,加上
@Primary
注解,来确定默认的实现。@Primary @Service public class UserServiceImpl implements UserService { ... }
类似于设置默认值。
-
方案二:使用
@Qualifier
注解指定当前要注入的 bean 对象。 在
@Qualifier
的 value 属性中,指定注入的 bean 的名称。@Qualifier
注解不能单独使用,必须配合@Autowired
使用。@RestController public class UserController {@Autowired@Qualifier("userServiceImpl") // 指定要注入的 beanprivate UserService userService; }
-
方案三:使用
@Resource
注解是按照 bean 的名称进行注入。通过 name 属性指定要注入的 bean 的名称。
@RestController public class UserController {@Resource(name = "userServiceImpl")private UserService userService; }
2.3 扩展
@Autowird
与 @Resource
的区别:
@Autowired
是 spring 框架提供的注解,而@Resource
是 JDK 提供的注解。@Autowired
默认是按照类型注入,而@Resource
是按照名称注入。