我是阳仔,本篇文章给大家结束一下Spring 相关的知识,也是比较基础的JAVA框架知识;
spring的基础的主要结构
一、Spring 概念类问题
- 什么是 Spring 框架?它的核心特性有哪些?
Spring 是一个开源的 Java 平台,它提供了全面的基础设施支持,让开发者能够更轻松地开发 Java 应用程序。
核心特性
- 依赖注入(Dependency Injection,DI):降低组件之间的耦合度,将对象的创建和管理交给 Spring 容器来实现
- 面向切面编程(Aspect - Oriented Programming,AOP):将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来;
- 声明式事务管理:简化了事务的处理流程,开发者只需通过配置文件或注解来定义事务的边界和规则,无需编写繁琐的事务代码。
- 请解释 Spring 中的 IoC(控制反转)和 DI(依赖注入)的区别和联系
IoC 是一种设计原则,它强调将对象的控制权从对象自身转移到容器中。在 Spring 中,容器负责创建和管理对象的生命周期,并且将依赖关系注入到对象中。
DI 是 IoC 的一种实现方式,它通过将依赖对象作为参数传递给被依赖对象的构造方法或 setter 方法来实现。简单来说,IoC 是一种概念,而 DI 是实现这种概念的具体技术手段,在 Spring 中,它们共同作用,实现了对象之间的解耦。
二、Spring 配置与使用相关
- 如何在项目中引入 Spring 框架?常用的依赖管理工具(如 Maven 和 Gradle)中如何配置 Spring 依赖?
在 Maven 项目中,我们可以在 pom.xml 文件中添加 Spring 相关的依赖。例如,要引入 Spring Core 模块,可以添加以下代码:
xml:
org.springframework
spring-core
5.3.10
对于 Gradle 项目,在 build.gradle 文件中添加类似下面的代码:
groovy:
dependencies {
implementation 'org.springframework:spring-core:5.3.10'
}
通过这些配置,Maven 或 Gradle 会自动从中央仓库下载所需的 Spring 依赖包,方便我们在项目中使用 Spring 框架的各种功能。
- 请描述一下 Spring 配置文件的基本结构和常用标签
Spring 配置文件通常是一个 XML 文件,其基本结构如下:
xml:
是根元素,用于定义 Spring 容器中的 Bean。 标签用于定义一个具体的 Bean,id 属性是 Bean 的唯一标识符,class 属性指定 Bean 的类全限定名。 标签用于设置 Bean 的属性值,name 属性指定属性名称,value 属性指定属性值。 - <import> 标签用于导入其他配置文件
标签用于为 Bean 定义别名等。
spring开发相关
三、Spring AOP 问题
- 什么是 AOP?请举例说明 AOP 在实际项目中的应用场景
AOP(Aspect - Oriented Programming,面向切面编程)是一种编程范式,它旨在将横切关注点(如日志记录、安全检查、事务管理等)从业务逻辑中分离出来,以减少代码的重复和耦合。
主要应用场景: 日志记录场景,权限验证等;
- 请解释 Spring AOP 中的切面(Aspect)、连接点(Joinpoint)、切点(Pointcut)和通知(Advice)的概念
- 切面(Aspect) :切面是横切关注点的模块化实现,它包含了切点和通知等元素。例如,一个日志记录切面可能包含日志记录的逻辑(通知)以及定义哪些方法需要记录日志(切点)。
- 连接点(Joinpoint) :连接点是程序执行过程中的某个特定位置,如方法执行前、方法执行后、异常抛出等位置。在 Spring AOP 中,连接点通常是指方法执行的时刻。
- 切点(Pointcut) :切点用于定义一组连接点,它通过表达式来匹配哪些连接点需要被切面所关注。例如,我们可以定义一个切点匹配所有以 "set" 开头的方法,这样所有符合这个条件的方法都会被选中进行切面处理。
- 通知(Advice) :通知是切面在特定连接点上执行的动作,它有多种类型,如前置通知(在方法执行前执行)、后置通知(在方法执行后执行)、异常通知(在方法抛出异常时执行)、环绕通知(围绕方法执行)等。
四、Spring 事务管理问题
- Spring 中如何管理事务?有哪些事务管理方式?
Spring 提供了两种事务管理方式:编程式事务管理和声明式事务管理。
编程式事务管理:是通过编写代码来手动控制事务的提交和回滚,这种方式灵活性较高,但会使业务逻辑代码与事务管理代码混合在一起,不利于维护。
声明式事务管理:则是通过配置文件或注解的方式来定义事务的边界和规则,它将事务管理与业务逻辑分离,使代码更加简洁和清晰。
在实际项目中,声明式事务管理更为常用,它可以通过 @Transactional 注解来实现,该注解可以应用于方法或类上,指定事务的传播行为、隔离级别等属性。
- 请解释 Spring 中的事务传播行为和事务隔离级别
- 事务传播行为 :
事务传播行为定义了当一个事务内的方法调用另一个方法时,事务如何在这些方法之间传播。
- REQUIRED: 是最常用的传播行为,表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则挂起当前事务)
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行;
- 事务隔离级别 :
事务隔离级别定义了当前事务在并发环境下与其他事务的隔离程度。
- READ_COMMITTED(读已提交,可防止脏读)
- REPEATABLE_READ(可重复读,可防止不可重复读)
- SERIALIZABLE(串行化,可防止幻读)
常见面试题:
Spring框架中的bean是线程安全的吗?
在 Spring 框架中,Bean 的线程安全性取决于其设计和使用方式,不能一概而论。
- Spring 的 singleton 作用域 Bean 本身并不保证线程安全,其线程安全性取决于 Bean 的实现。如果 Bean 无状态或使用同步机制、不可变对象等设计,它可以是线程安全的。
- 对于有状态的 Bean,应根据需求选择合适的作用域(如 prototype)或采用其他线程安全的方案来确保线程安全。
- singleton单例模式: 线程不安全 ;
- prototype 多例模式: 线程全;
1.Spring Bean 的作用域
Spring 几种常见作用域:
- singleton(单例):Spring 容器中仅存在一个实例,适用于无状态或线程安全的 Bean。
- prototype(原型):每次请求时都会创建一个新的实例,适用于有状态且需要线程隔离的 Bean。
2.Spring Bean 的线程安全特性
- singleton(单例)作用域 Bean:
- 无状态的单例 Bean:如果单例 Bean 中没有可变的成员变量,或者其成员变量在初始化后不会改变,那么这个 Bean 是线程安全的。
- 有状态的单例 Bean:如果单例 Bean 包含可变的成员变量,并且这些变量在多线程环境下被共享和修改,那么这个 Bean 是线程不安全的。例如,以下 Bean 中的 count 是可变状态,在多线程环境下会导致数据不一致:
- java复制
@Component
@Scope("singleton")
public class SingletonBean {
private int count = 0;
public void increment(){
count++;
// 线程不安全的操作 }
public int getCount(){
return count; }
}
- prototype(原型)作用域 Bean:(线程安全)
- 每次请求都会创建一个新的实例,因此没有线程安全的问题。即使 Bean 包含可变状态,每个线程都会操作自己的实例,不会影响其他线程。所以线程是安全的
4.解决 Spring Bean 线程安全问题的方法
以下是几种确保 Spring Bean 线程安全的方法:
- 设计为无状态 尽可能将 Bean 设计为无状态的,避免使用可变的成员变量。
- 例如:
@Component
public class StateLessService {
public String process(String input) {
return input.toUpperCase(); // 无状态的业务逻辑
}
}
- 同步机制 在有状态的 Bean 中,可以使用同步机制(如 synchronized 关键字、Lock 接口等)来保护共享的可变状态。
@Component
public class ThreadSafeBean {
private int count = 0;
public synchronized void increment() {
count++; // synchronized 保护线程安全
}
public int getCount() {
return count;
}
}
- 不可变对象 将 Bean 的状态设计为不可变的,一旦初始化后就无法修改。可以通过 final 关键字修饰成员变量来实现。
@Component
public class ImmutableBean {
private final String name;
@Autowired
public ImmutableBean(String name) {
this.name = name;
}
public String getName() {
return name; // 不可变状态
}
}
- 使用 @Scope 注解 如果需要线程隔离的有状态 Bean,可以将作用域改为 prototype 或其他恰当的作用域(如 request、session)。
@Component
@Scope("prototype") // 每次请求都会创建新实例
public class PrototypeBean {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
- 使用线程本地存储(ThreadLocal) 如果 Bean 中的状态需要在多线程中隔离,可以使用 ThreadLocal 将状态绑定到当前线程。每个线程都会拥有独立的副本,不会相互干扰。
@Component
public class ThreadLocalBean {
private ThreadLocal count = ThreadLocal.withInitial(() -> 0);
public void increment() {
count.set(count.get() + 1);
}
public int getCount() {
return count.get();
}
}
Spring的bean的生命周期?
1.实例化:Spring 容器通过构造器创建 Bean 实例。
2.属性赋值:Spring 容器为 Bean 的属性注入依赖。
3.初始化前处理:
- 如果 Bean 实现了 BeanNameAware,调用 setBeanName()。
- 如果 Bean 实现了 BeanFactoryAware,调用 setBeanFactory()。
- 如果 Bean 实现了 ApplicationContextAware,调用 setApplicationContext()。
4.初始化:
- 如果 Bean 实现了 InitializingBean,调用 afterPropertiesSet()。
- 如果配置了 init-method,调用指定的初始化方法。
5.Bean 准备就绪:Bean 可以被应用程序使用。
6.销毁前处理:
- 如果 Bean 实现了 DisposableBean,调用 destroy()。
- 如果配置了 destroy-method,调用指定的销毁方法。
Spring中的循环引用是什么?怎么处理的?
1. 什么是循环引用?
循环引用是指两个或多个 Bean 相互依赖,形成一个闭环。例如:
- Bean A 依赖 Bean B
- Bean B 依赖 Bean A
2. Spring 如何处理循环引用?
Spring 使用 三级缓存机制 来解决循环引用问题。具体步骤如下:
实例化阶段:
Spring 开始创建 Bean A,并将未完全初始化的 Bean A 放入 三级缓存。
属性注入阶段:
- 创建 Bean B 时,发现 Bean B 依赖 Bean A。
- 从 三级缓存 中获取 Bean A 的早期引用,注入到 Bean B 中。
完成创建:
- Bean B 创建完成后,将 Bean B 放入 二级缓存。
- 回到 Bean A 的创建,从 二级缓存 中获取 Bean B,完成 Bean A 的创建。
三级缓存机制
- 一级缓存:存放完全初始化的 Bean。
- 二级缓存:存放已实例化但未完全初始化的 Bean。
- 三级缓存:存放 Bean 工厂对象,用于获取早期引用。
注意事项
- 构造器注入不支持循环依赖:构造器注入无法解决循环依赖问题。
- 属性注入支持循环依赖:Spring 的三级缓存机制适用于属性注入。
SpringMVC的执行流程?
详细流程:
- 用户发起请求:用户通过浏览器或其他客户端工具发起一个 HTTP 请求。
- 请求到达前端控制器(DispatcherServlet):所有请求都会被前端控制器拦截,它是 SpringMVC 的核心,负责协调其他组件处理请求。
- 寻找处理器映射器(HandlerMapping):DispatcherServlet 根据请求的 URL,查找与之匹配的处理器(Controller)。
- 确定处理器适配器(HandlerAdapter):处理器映射器找到处理器后,DispatcherServlet 使用处理器适配器来适配处理器,使处理器可以按照统一的方式执行。
- 执行处理器(Controller):处理器适配器调用处理器中的方法,处理用户请求,并返回一个 ModelAndView 对象。
- 视图解析器(ViewResolver)解析视图:控制器返回的 ModelAndView 包含了逻辑视图名,例如 success。DispatcherServlet 使用视图解析器将逻辑视图名解析为实际的物理视图(如 JSP 页面、HTML 文件等)。
- 生成响应:视图解析器解析出具体的视图后,由视图组件负责生成最终的响应内容(HTML、XML、JSON 等),并通过前端控制器返回给客户端。
Spring相关的常见注解有哪些?都有什么用?
Spring常见注解总结
核心注解
依赖注解
AOP相关注解
事务相关注解
下文只展示面试中比较常用的注解和相关作业.
- 核心注解
- @Autowired:自动装配依赖,按类型或名称注入。
- @Component / @Service / @Repository / @Controller:标识组件。
- @Bean:在配置类定义 Bean。
- @Configuration:标识配置类。
- AOP 注解
- @Aspect:定义切面类。
- @Before / @After / @Around:定义切面通知。
- 事务注解
- @Transactional:声明式事务管理。
- Scope 注解
- @Scope:指定 Bean 的作用域。
- 其他常用注解
- @Value:注入配置文件的值。
- @PostConstruct / @PreDestroy:标识初始化和销毁方法。
- @RestController / @RequestMapping:用于 REST 控制器和请求映射。
- @Test / @SpringBootTest:用于单元测试和集成测试。
SpringMVC常见的注解有哪些?
@Controller:标识一个类为 MVC 控制器,常用于处理 HTTP 请求,配合 @RequestMapping 等注解使用。
@RestController :标识一个类为 REST 风格的控制器,自动将方法的返回值作为响应体返回(相当于 @Controller + @ResponseBody)。适用于返回数据(如 JSON 或 XML)而非视图的场景。
@RequestMapping :绑定 URL 请求与控制器方法,支持多种 HTTP 方法。可以通过 method 属性指定 HTTP 方法(如 GET、POST 等)。
@PostMapping、@GetMapping、@PutMapping、@DeleteMapping、@PatchMapping
- 用途:分别用于映射 POST、GET、PUT、DELETE、PATCH 请求。
- 说明:简化了 @RequestMapping 的使用,代码更清晰。
@RequestParam ,绑定查询参数(URL 参数)到控制器方法的参数中,常用 name、required 和 defaultValue 属性。
@PathVariable :绑定 URL 路径中的变量到控制器方法的参数中,用于 RESTful 风格的 URL。
@RequestBody:将请求体中的 JSON/XML 数据绑定到控制器方法的参数中。与 @ResponseBody 配合使用于数据的接收和返回。
@ResponseBody :将控制器方法的返回值作为响应体返回。使返回值直接写入 HTTP 响应体,通常与 @RestController 配合使用。
@ModelAttribute :将对象绑定到模型,以便在视图中使用。方法注解,用于向模型添加数据。
@SessionAttributes :将模型属性存在 HTTP Session 中。常与 @ModelAttribute 配合使用。
@ExceptionHandler :定义方法来处理特定类型的异常。在控制器类中使用,处理全局异常。
SpringBoot常见的注解有哪些?
@SpringBootApplication:启动 SpringBoot 应用程序,是 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 的组合。
@Configuration:标识一个配置类。
@ComponentScan:指定组件扫描的包路径。
@EnableAutoConfiguration:开启自动配置功能。
@RestController :标识一个 REST 风格的控制器。
@RestControllerAdvice :全局处理异常。
@Value :注入配置文件中的值。
@ConfigurationProperties :绑定配置文件中的属性到 Java 类。
@Scope :指定 Bean 的作用域。