JAVAEE之IoC&DI

Spring 是⼀个 IoC(控制反转)容器,作为容器, 那么它就具备两个最基础的功能:
• 存
• 取
Spring 容器管理的主要是对象, 这些对象, 我们称之为"Bean". 我们把这些对象交由Spring管理, 由
Spring来负责对象的创建和销毁.
我们程序只需要告诉Spring, 哪些需要存, 以及如何从Spring中取出对象

1.IoC

IoC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对
象。
也就是bean的存储.

1.1 Bean的存储

Spring框架为了更好的服务web应⽤程序, 提供了更丰富的注解.
共有两类注解类型可以实现:
1. 类注解:
@Controller (控制器存储)
@Service(服务器存储)
@Repository (仓库存储)
@Component (组件存储)
@Configuration (配置存储)
2. ⽅法注解:@Bean.

1.2 不同注解如何使用(建议)

和应⽤分层是呼应的.
让程序员看到类注解之后,就能直接了解当前类的⽤途(并不是必须的)
• @Controller:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应.(BookController)
• @Servie:业务逻辑层, 处理具体的业务逻辑.(BookService)
• @Repository:数据访问层,也称为持久层. 负责数据访问操作(BookDao)
• @Configuration:配置层. 处理项⽬中的⼀些配置信息(比如说固定的字符串创建的一些类)

1.3 类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:

其实这些注解⾥⾯都有⼀个注解 @Component ,说明它们本⾝就是属于 @Component 的"⼦类".
@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller , @Service ,
@Repository 等. 这些注解被称为 @Component 的衍⽣注解.
@Controller , @Service 和 @Repository ⽤于更具体的⽤例(分别在控制层, 业务逻辑层, 持
久化层), 在开发过程中, 如果你要在业务逻辑层使⽤ @Component 或@Service,显然@Service是更好的选择

1.4 方法注解@Bean

类注解是添加到某个类上的, 但是存在两个问题:

  1. 使用外部包里的类, 没办法添加类注解
  2. ⼀个类, 需要多个对象, ⽐如多个数据源
    这种场景, 我们就需要使⽤⽅法注解 @Bean

1.4.1 ⽅法注解要配合类注解使⽤

在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所⽰:

java 复制代码
@Component
public class BeanConfig {
 @Bean
 public User user(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
 }
}

1.4.2 定义多个对象

对于同⼀个类, 如何定义多个对象呢?
⽐如多数据源的场景, 类是同⼀个, 但是配置不同, 指向不同的数据源.
我们看下@Bean的使用

java 复制代码
@Component
public class BeanConfig {
 @Bean
 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;
 }
}

@Bean 可以针对同⼀个类, 定义多个对象

1.4.3 重命名 Bean

可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所⽰:

java 复制代码
@Bean(name = {"u1","user1"})
public User user1(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
}

1.4.4 扫描路径

使⽤五⼤注解声明的bean,要想⽣效, 还需要配置扫描路径, 让Spring扫描到这些注解
也就是通过 @ComponentScan 来配置扫描路径.
@ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解
@SpringBootApplication 中了
默认扫描的范围是SpringBoot启动类所在包及其⼦包
在配置类上添加 @ComponentScan 注解, 该注解默认会扫描该类所在的包下所有的配置类

2.DI

依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象.
在上⾯程序案例中,我们使⽤了 @Autowired 这个注解,完成了依赖注⼊的操作.
简单来说, 就是把对象取出来放到某个类的属性中.
在⼀些⽂章中, 依赖注⼊也被称之为 "对象注⼊", "属性装配".

关于依赖注⼊, Spring也给我们提供了三种⽅式:

  1. 属性注⼊(Field Injection)
  2. 构造⽅法注⼊(Constructor Injection)
  3. Setter 注⼊(Setter Injection)

Service 类的实现代码如下:

java 复制代码
import org.springframework.stereotype.Service;
@Service
public class UserService {
 public void sayHi() {
 System.out.println("Hi,UserService");
 }
}

2.1 属性注⼊

属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中

java 复制代码
@Controller
public class UserController {
 //注⼊⽅法1: 属性注⼊
 @Autowired
 private UserService userService;
 public void sayHi(){
 System.out.println("hi,UserController...");
 userService.sayHi();
 }
}

2.2 构造⽅法注⼊

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所⽰:

java 复制代码
@Controller
public class UserController2 {
 //注⼊⽅法2: 构造⽅法
 private UserService userService;
 @Autowired
 public UserController2(UserService userService) {
 this.userService = userService;
 }
 public void sayHi(){
 System.out.println("hi,UserController2...");
 userService.sayHi();
 }
}

注意事项:如果类只有⼀个构造⽅法,那么 @Autowired 注解可以省略;如果类中有多个构造⽅法,那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法。

2.3 Setter 注⼊

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注
解 ,如下代码所⽰:

java 复制代码
@Controller
public class UserController3 {
 //注⼊⽅法3: Setter⽅法注⼊
 private UserService userService;
 @Autowired
 public void setUserService(UserService userService) {
 this.userService = userService;
 }
 public void sayHi(){
 System.out.println("hi,UserController3...");
 userService.sayHi();
 }
}

2.4 三种注⼊优缺点分析

• 属性注⼊
◦ 优点: 简洁,使⽤⽅便;
◦ 缺点:
▪ 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指
针异常)
▪ 不能注⼊⼀个Final修饰的属性
• 构造函数注⼊(Spring 4.X推荐)
◦ 优点:
▪ 可以注⼊final修饰的属性
▪ 注⼊的对象不会被修改
▪ 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法
是在类加载阶段就会执⾏的⽅法.
▪ 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
◦ 缺点:
▪ 注⼊多个对象时, 代码会⽐较繁琐
• Setter注⼊(Spring 3.X推荐)
◦ 优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
◦ 缺点:
▪ 不能注⼊⼀个Final修饰的属性
▪ 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.

2.5 @Autowired存在问题

当同⼀类型存在多个bean时, 使⽤@Autowired会存在问题

java 复制代码
@Component
public class BeanConfig {
 @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;
 }
}
java 复制代码
@Controller
public class UserController {
 
 @Autowired
 private UserService userService;
 //注⼊user
 @Autowired
 private User user;
 public void sayHi(){
 System.out.println("hi,UserController...");
 userService.sayHi();
 System.out.println(user);
 }
}

//出现错误


报错的原因是,⾮唯⼀的 Bean 对象。
如何解决上述问题呢?Spring提供了以下⼏种解决⽅案:
• @Primary
• @Qualifier
• @Resource
使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来 确定默认 的实现.

java 复制代码
@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注解:指定当前要注⼊的bean对象。 在@Qualifier的value属性中,指定注⼊的bean
的名称。
• @Qualifier注解不能单独使⽤,必须配合@Autowired使⽤

java 复制代码
@Controller
public class UserController {
 @Qualifier("user2") //指定bean名称
 @Autowired
 private User user;
 public void sayHi(){
 System.out.println("hi,UserController...");
 System.out.println(user);
 }
}

使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称。

java 复制代码
@Controller
public class UserController {
 @Resource(name = "user2")
 private User user;
 public void sayHi(){
 System.out.println("hi,UserController...");
 System.out.println(user);
 }
}

2.6 @Autowird 与 @Resource的区别

• @Autowired 是spring框架提供的注解,⽽@Resource是JDK提供的注解
• @Autowired 默认是按照类型注⼊ ,⽽@Resource是按照名称注⼊. 相⽐于 @Autowired 来说, @Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean。

相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔3 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区8 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说9 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json