菜单🤭🤭
前言:书接上回,该篇博客主要讲解DI与该章节常见面试题
1. DI详解(IoC的实现)
DI即依赖注入,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象。说白了就是把对象取出来放到某个类的属性中。
关于依赖注入,Spring也给我们提供了三种方式:
- 属性注入(Field Injection)
- 构造方法注入(Constructor Injection)
- Setter注入(Setter Injection)
接下来我将演示使用这三种方式把Sevice类注入到Controller类中。
1.1 属性注入
属性注⼊是使⽤@Autowired
实现的,将Service类注入到Controller类中.
Service类的实现代码如下:
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi() {
System.out.println("hi service!!!");
}
}
而Controller类的实现代码如下:
package com.example.demo.controller;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller //将对象存储至Spring中
public class UserController {
//注入方法1:属性注入
@Autowired
private UserService userService;
public void sayHi(){
System.out.println("你好,UserController");
userService.sayHi();
}
}
获取Controller类中的sayHi
方法:
@SpringBootApplication
public class IocDemoApplication {
public static void main(String[] args) {
//获取Spring上下文对象
ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);
//从Spring上下文中获取对象
UserController userController = context.getBean(UserController.class);
//使用对象
userController.sayHi();
}
}
运行结果如下:
然后我们将@AutoWired注释掉,再看看运行结果:(提示"this.userService" is null
)
👊注意:@AutoWired注入时是根据类型注入(进行匹配)的,所以上方:private UserService userService;
的userService可随意命名,无需固定格式。
1.2 构造方法注入
构造方法是在类的构造方法中实现注入,代码如下:
@Controller
public class UserController2 {
//注⼊⽅法2: 构造⽅法
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("你好,UserController2");
userService.sayHi();
}
}
⭐⭐注意:
- 如果类只有⼀个构造方法,那么
@Autowired
注解可以省略;但假如这个构造方法是无参的话,项目运行会报空指针异常。 - 如果类中有多个构造方法,Spring会默认调用无参构造方法,没有的话同样会报错,这是就需要添加**@Autowired**来明确指定到底使用哪个构造方法。
1.3 Setter注入
Setter注入和属性的Setter方法实现类似,只不过在设置set方法的时候需要加上@Autowired注解,代码如下:
@Controller
public class UserController3 {
//注⼊⽅法3: Setter⽅法注⼊
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("你好,UserController3");
userService.sayHi();
}
}
如若不加@AutoWired注解,会报空指针异常。
2 三种注入方式的优与劣
1. 属性注入
- 优:简洁,使用方便
- 劣:
- 只能用于IoC容器,如果是非IoC容器不可用,并且只有在使用的时候才会出现NullPointerException(空指针异常)
- 不能注入一个Final修饰的属性,解决方案如下:
- 定义时就进行赋值
- 构造方法中进行赋值
- 如果一个类型存在多个对象时,优先名称匹配,如果名称匹配不上,则会报错
2. 构造函数注入(Spring4.X推荐)
- 优:
- 可以注入final修饰的属性
- 注入的对象不会被修改
- 依赖对象在使用前⼀定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法
是在类加载阶段就会执行的方法。 - 通⽤性好,构造方法是JDK支持的,所以更换任何框架,他都是适用的。
- 劣: 注入多个对象时,代码会比较繁琐
3. Setter注入(Spring 3.X推荐)
- 优:方便在类实例之后,重新对该对象进行配置或者注入(用处不大🤭🤭)
- 劣:
- 不能注入一个Final修饰的属性。
- 注入对象可能会改变,因为setter方法可能会被多次调用,其中便会产生被修改的风险。
3 @AutoWired存在的问题
当同一类型存在多个bean时,使用@AutoWired便会存在问题
此时idea直接给出了一个error
,运行是一定出问题的,如下图:
大致意思就是:不是唯一的bean
对象
4 解决方案
- 属性名与需要使用的对象保持一致(不推荐)
- @Primary
- @Qualifier
- @Resource
4.1 @Primary
使用@Primary注解:当存在多个相同类型的Bean注入时,加上该注解来确定默认的实现。
示例代码如下:
4.2 @Qualifier
使用@Qualifier
注解:指定当前要注入的bean对象。在@Qualifier的value属性中,指定注入的bean的名称。
📌注意:@Qualifier注解不能单独使用,必须配合@Autowired使用
示例如下:
4.3 @Resource(常用)
使用@Resource
注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。
示例代码如下:
当然,如果你使用了@Resource
注解,并且通过它来进行依赖注入,就不需要再使用@Autowired
注解了。
5 常见面试题
5.1 @Autowired和@Resource区别?
- @Autowired是spring框架提供的注解,而@Resource是JDK提供的注解
- @Autowired默认是按照类型注入,而@Resource则是按照名称注入。相比于@Autowired来说,@Resource支持更多的参数设置,比如name设置,根据名称获取Bean。
5.2 Spring,SpringMVC,SpringBoot的关系以及区别?
- 区别:
- Spring:是一个综合性的 Java 开发框架,提供了诸多功能,包括依赖注入、面向切面编程、事务管理等。
- Spring MVC:是 Spring 框架中的一个模块,用于构建基于 MVC 架构的 Web 应用程序。
- Spring Boot:是 Spring 项目的一个子项目,旨在简化 Spring 应用程序的开发和部署,提供了自动配置和快速启动的能力。
- 关系:
- Spring MVC 是 Spring 框架的一部分,用于构建 Web 应用程序。
- Spring Boot 基于 Spring 框架,为 Spring 应用程序提供了自动配置和快速启动的能力。
总的来说,Spring 是一个全功能的 Java 开发框架,Spring MVC 是 Spring 框架的 Web 模块(Spring Web),而 Spring Boot 是一个简化 Spring 应用程序开发的工具。
5.3 ApplicationContext与BeanFactory的区别?
选中ApplicationContext,按下CTRL+ALT+U
,即可生成当前接口的继承关系图,如下图:
区别总结如下:
- 初始化时机:
BeanFactory
:延迟初始化,即在第一次请求时才会实例化bean。
ApplicationContext
:预先实例化所有的单例bean,在容器启动时就完成了bean的初始化。 - Bean实例化方式:
BeanFactory
:使用 getBean() 方法获取bean实例。
ApplicationContext
:除了可以使用 getBean() 方法外,还支持自动注入、Bean的生命周期回调等功能。 - 继承关系与功能:
BeanFactory和ApplicationContext都是 Spring 框架中的顶级接口。它们是 Spring 容器的两个主要实现,负责管理和装配 bean,并提供了对 Spring 应用程序上下文的访问和控制。其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子类,它除了继承了BeanFactory的所有功能之外,它还拥有独特的特性,还添加了对国际化支持、资源访问支持、AOP支持、以及事件传播等方面的支持。 - 性能:
BeanFactory
:由于延迟初始化的特性,相对于 ApplicationContext,启动速度会更快一些。
ApplicationContext
:在初始化时会预先加载和实例化所有的单例bean,因此在启动时会消耗更多的资源和时间。
所以,一般如果应用需要更多的功能,像事件处理、AOP、国际化等,建议使用 ApplicationContext;而如果应用对性能要求较高,可以考虑使用 BeanFactory。