(十)Spring 面试秘籍:攻克常见难题,轻松拿 offer

我是阳仔,本篇文章给大家结束一下Spring 相关的知识,也是比较基础的JAVA框架知识;


spring的基础的主要结构

一、Spring 概念类问题

  1. 什么是 Spring 框架?它的核心特性有哪些?

Spring 是一个开源的 Java 平台,它提供了全面的基础设施支持,让开发者能够更轻松地开发 Java 应用程序。

核心特性

  • 依赖注入(Dependency Injection,DI):降低组件之间的耦合度,将对象的创建和管理交给 Spring 容器来实现
  • 面向切面编程(Aspect - Oriented Programming,AOP):将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来;
  • 声明式事务管理:简化了事务的处理流程,开发者只需通过配置文件或注解来定义事务的边界和规则,无需编写繁琐的事务代码。
  1. 请解释 Spring 中的 IoC(控制反转)和 DI(依赖注入)的区别和联系

IoC 是一种设计原则,它强调将对象的控制权从对象自身转移到容器中。在 Spring 中,容器负责创建和管理对象的生命周期,并且将依赖关系注入到对象中。

DI 是 IoC 的一种实现方式,它通过将依赖对象作为参数传递给被依赖对象的构造方法或 setter 方法来实现。简单来说,IoC 是一种概念,而 DI 是实现这种概念的具体技术手段,在 Spring 中,它们共同作用,实现了对象之间的解耦。

二、Spring 配置与使用相关

  1. 如何在项目中引入 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 框架的各种功能。

  1. 请描述一下 Spring 配置文件的基本结构和常用标签

Spring 配置文件通常是一个 XML 文件,其基本结构如下:

xml:




    
    
        
        
    

  • 是根元素,用于定义 Spring 容器中的 Bean。
  • 标签用于定义一个具体的 Bean,id 属性是 Bean 的唯一标识符,class 属性指定 Bean 的类全限定名。
  • 标签用于设置 Bean 的属性值,name 属性指定属性名称,value 属性指定属性值。
  • <import> 标签用于导入其他配置文件
  • 标签用于为 Bean 定义别名等。

spring开发相关

三、Spring AOP 问题

  1. 什么是 AOP?请举例说明 AOP 在实际项目中的应用场景

AOP(Aspect - Oriented Programming,面向切面编程)是一种编程范式,它旨在将横切关注点(如日志记录、安全检查、事务管理等)从业务逻辑中分离出来,以减少代码的重复和耦合。

主要应用场景: 日志记录场景,权限验证等;

  1. 请解释 Spring AOP 中的切面(Aspect)、连接点(Joinpoint)、切点(Pointcut)和通知(Advice)的概念
  2. 切面(Aspect) :切面是横切关注点的模块化实现,它包含了切点和通知等元素。例如,一个日志记录切面可能包含日志记录的逻辑(通知)以及定义哪些方法需要记录日志(切点)。
  3. 连接点(Joinpoint) :连接点是程序执行过程中的某个特定位置,如方法执行前、方法执行后、异常抛出等位置。在 Spring AOP 中,连接点通常是指方法执行的时刻。
  4. 切点(Pointcut) :切点用于定义一组连接点,它通过表达式来匹配哪些连接点需要被切面所关注。例如,我们可以定义一个切点匹配所有以 "set" 开头的方法,这样所有符合这个条件的方法都会被选中进行切面处理。
  5. 通知(Advice) :通知是切面在特定连接点上执行的动作,它有多种类型,如前置通知(在方法执行前执行)、后置通知(在方法执行后执行)、异常通知(在方法抛出异常时执行)、环绕通知(围绕方法执行)等。

四、Spring 事务管理问题

  1. Spring 中如何管理事务?有哪些事务管理方式?

Spring 提供了两种事务管理方式:编程式事务管理和声明式事务管理。

编程式事务管理:是通过编写代码来手动控制事务的提交和回滚,这种方式灵活性较高,但会使业务逻辑代码与事务管理代码混合在一起,不利于维护。

声明式事务管理:则是通过配置文件或注解的方式来定义事务的边界和规则,它将事务管理与业务逻辑分离,使代码更加简洁和清晰。

在实际项目中,声明式事务管理更为常用,它可以通过 @Transactional 注解来实现,该注解可以应用于方法或类上,指定事务的传播行为、隔离级别等属性。

  1. 请解释 Spring 中的事务传播行为和事务隔离级别
  2. 事务传播行为

事务传播行为定义了当一个事务内的方法调用另一个方法时,事务如何在这些方法之间传播。

  • REQUIRED: 是最常用的传播行为,表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则挂起当前事务)
  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行;
  1. 事务隔离级别

事务隔离级别定义了当前事务在并发环境下与其他事务的隔离程度。

  • 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的执行流程?

详细流程:

  1. 用户发起请求:用户通过浏览器或其他客户端工具发起一个 HTTP 请求。
  2. 请求到达前端控制器(DispatcherServlet):所有请求都会被前端控制器拦截,它是 SpringMVC 的核心,负责协调其他组件处理请求。
  3. 寻找处理器映射器(HandlerMapping):DispatcherServlet 根据请求的 URL,查找与之匹配的处理器(Controller)。
  4. 确定处理器适配器(HandlerAdapter):处理器映射器找到处理器后,DispatcherServlet 使用处理器适配器来适配处理器,使处理器可以按照统一的方式执行。
  5. 执行处理器(Controller):处理器适配器调用处理器中的方法,处理用户请求,并返回一个 ModelAndView 对象。
  6. 视图解析器(ViewResolver)解析视图:控制器返回的 ModelAndView 包含了逻辑视图名,例如 success。DispatcherServlet 使用视图解析器将逻辑视图名解析为实际的物理视图(如 JSP 页面、HTML 文件等)。
  7. 生成响应:视图解析器解析出具体的视图后,由视图组件负责生成最终的响应内容(HTML、XML、JSON 等),并通过前端控制器返回给客户端。

Spring相关的常见注解有哪些?都有什么用?

Spring常见注解总结

核心注解

依赖注解

AOP相关注解

事务相关注解

下文只展示面试中比较常用的注解和相关作业.

  • 核心注解
  1. @Autowired:自动装配依赖,按类型或名称注入。
  2. @Component / @Service / @Repository / @Controller:标识组件。
  3. @Bean:在配置类定义 Bean。
  4. @Configuration:标识配置类。
  • AOP 注解
  1. @Aspect:定义切面类。
  2. @Before / @After / @Around:定义切面通知。
  • 事务注解
  1. @Transactional:声明式事务管理。
  • Scope 注解
  1. @Scope:指定 Bean 的作用域。
  • 其他常用注解
  1. @Value:注入配置文件的值。
  2. @PostConstruct / @PreDestroy:标识初始化和销毁方法。
  3. @RestController / @RequestMapping:用于 REST 控制器和请求映射。
  4. @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 的作用域。

原文链接:,转发请注明来源!