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。

相关推荐
考虑考虑26 分钟前
Jpa使用union all
java·spring boot·后端
用户3721574261351 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊2 小时前
Java学习第22天 - 云原生与容器化
java
渣哥4 小时前
原来 Java 里线程安全集合有这么多种
java
间彧4 小时前
Spring Boot集成Spring Security完整指南
java
间彧4 小时前
Spring Secutiy基本原理及工作流程
java
Java水解5 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆7 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学8 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole8 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端