八股文笔记 #5 Java Spring

Spring

1# 说一下你对 Spring 的理解

img

Spring框架核心特性包括:

  • IoC容器:Spring通过控制反转实现了对象的创建和对象间的依赖关系管理。开发者只需要定义好Bean及其依赖关系,Spring容器负责创建和组装这些对象。
  • AOP:面向切面编程,允许开发者定义横切关注点,例如事务管理、安全控制等,独立于业务逻辑的代码。通过AOP,可以将这些关注点模块化,提高代码的可维护性和可重用性。
  • 事务管理:Spring提供了一致的事务管理接口,支持声明式和编程式事务。开发者可以轻松地进行事务管理,而无需关心具体的事务API。
  • MVC框架:Spring MVC是一个基于Servlet API构建的Web框架,采用了模型-视图-控制器(MVC)架构。它支持灵活的URL到页面控制器的映射,以及多种视图技术。

2# Spring 是怎么管理对象的?

  • 核心是 IoC(控制反转)容器,由 ApplicationContext 管理对象(Bean)。
  • Spring 负责对象的:创建 → 初始化 → 生命周期管理 → 销毁
  • Bean 定义写在注解/XML/配置类里,Spring 容器启动时会扫描并实例化。
  • 注入依赖时用 依赖注入(DI),避免开发者手动 new 对象。

3# @Autowired 和 @Resource 的区别?

  • @Autowired(Spring 提供):默认 按类型 注入,需要配合 @Qualifier 指定 Bean 名称。
  • @Resource(JDK 提供):默认 按名称 注入,找不到再按类型。
  • 依赖冲突时:
    • @Autowired 必须配合 @Qualifier@Primary
    • @Resource 直接指定 name

4# Spring 的核心思想说说你的理解?

核心思想 解决的问题 实现手段 典型应用场景
IOC 对象创建与依赖管理的高耦合 容器管理Bean生命周期 动态替换数据库实现、服务组装
DI 依赖关系的硬编码问题 Setter/构造器/注解注入 注入数据源、服务层依赖DAO层
AOP 横切逻辑分散在业务代码中 动态代理与切面配置 日志、事务、权限校验统一处理

Spring通过这IOC、DI、AOP三大核心思想,实现了轻量级、高内聚低耦合的企业级应用开发框架,成为Java生态中不可或缺的基石。

5# 依赖倒置,依赖注入,控制反转分别是什么?

  • 控制反转:“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。
  • 依赖注入:依赖注入和控制反转恰恰相反,它是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
  • 依赖倒置:这条原则跟控制反转有点类似,主要用来指导框架层面的设计。高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。

6# 怎么理解 Spring IoC?

IOC:Inversion Of Control,即控制反转,是一种设计思想。在传统的 Java SE 程序设计中,我们直接在对象内部通过 new 的方式来创建对象,是程序主动创建依赖对象;

img

而在Spring程序设计中,IOC 是有专门的容器去控制对象。

img

所谓控制就是对象的创建、初始化、销毁。

  • 创建对象:原来是 new 一个,现在是由 Spring 容器创建。
  • 初始化对象:原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入。
  • 销毁对象:原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。

总结:IOC 解决了繁琐的对象生命周期的操作,解耦了我们的代码。所谓反转:其实是反转的控制权,前面提到是由 Spring 来控制对象的生命周期,那么对象的控制就完全脱离了我们的控制,控制权交给了 Spring 。这个反转是指:我们由对象的控制者变成了 IOC 的被动控制者。

7# Spring 的 AOP 介绍一下

Spring AOP是Spring框架中的一个重要模块,用于实现面向切面编程。

我们知道,Java 就是一门面向对象编程的语言,在 OOP 中最小的单元就是“Class 对象”,但是在 AOP 中最小的单元是“切面”。一个“切面”可以包含很多种类型和对象,对它们进行模块化管理,例如事务管理。

在面向切面编程的思想里面,把功能分为两种

  • 核心业务:登陆、注册、增、删、改、查、都叫核心业务
  • 周边功能:日志、事务管理这些次要的为周边业务

在面向切面编程中,核心业务功能和周边功能是分别独立进行开发,两者不是耦合的,然后把切面功能和核心业务功能 “编织” 在一起,这就叫AOP。

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度,并有利于未来的可拓展性和可维护性

在 AOP 中有以下几个概念:

  • AspectJ:切面,只是一个概念,没有具体的接口或类与之对应,是 Join point,Advice 和 Pointcut 的一个统称。
  • Join point:连接点,指程序执行过程中的一个点,例如方法调用、异常处理等。在 Spring AOP 中,仅支持方法级别的连接点。
  • Advice:通知,即我们定义的一个切面中的横切逻辑,有“around”,“before”和“after”三种类型。在很多的 AOP 实现框架中,Advice 通常作为一个拦截器,也可以包含许多个拦截器作为一条链路围绕着 Join point 进行处理。
  • Pointcut:切点,用于匹配连接点,一个 AspectJ 中包含哪些 Join point 需要由 Pointcut 进行筛选。
  • Introduction:引介,让一个切面可以声明被通知的对象实现任何他们没有真正实现的额外的接口。例如可以让一个代理对象代理两个目标类。
  • Weaving:织入,在有了连接点、切点、通知以及切面,如何将它们应用到程序中呢?没错,就是织入,在切点的引导下,将通知逻辑插入到目标方法上,使得我们的通知逻辑在方法调用时得以执行。
  • AOP proxy:AOP 代理,指在 AOP 实现框架中实现切面协议的对象。在 Spring AOP 中有两种代理,分别是 JDK 动态代理和 CGLIB 动态代理。
  • Target object:目标对象,就是被代理的对象。

Spring AOP 是基于 JDK 动态代理和 Cglib 提升实现的,两种代理方式都属于运行时的一个方式,所以它没有编译时的一个处理,那么因此 Spring 是通过 Java 代码实现的。

8# Spring AOP 主要想解决什么问题?

它的目的是对于面向对象思维的一种补充,而不是像引入命令式、函数式编程思维让他顺应另一种开发场景。在我个人的理解下AOP更像是一种对于不支持多继承的弥补,除开对象的主要特征(我更喜欢叫“强共性”)被抽象为了一条继承链路,对于一些“弱共性”,AOP可以统一对他们进行抽象和集中处理。

举一个简单的例子,打印日志。需要打印日志可能是许多对象的一个共性,这在企业级开发中十分常见,但是日志的打印并不反应这个对象的主要共性。而日志的打印又是一个具体的内容,它并不抽象,所以它的工作也不可以用接口来完成。而如果利用继承,打印日志的工作又横跨继承树下面的多个同级子节点,强行侵入到继承树内进行归纳会干扰这些强共性的区分。

这时候,我们就需要AOP了。AOP首先在一个Aspect(切面)里定义了一些Advice(增强),其中包含具体实现的代码,同时整理了切入点,切入点的粒度是方法。最后,我们将这些Advice织入到对象的方法上,形成了最后执行方法时面对的完整方法。

img

9# Spring AOP 的原理了解吗?

Spring AOP的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象,而不是在编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。

Spring AOP支持两种动态代理:

  • 基于JDK的动态代理:使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。这种方式需要代理的类实现一个或多个接口。
  • 基于CGLIB的动态代理:当被代理的类没有实现接口时,Spring会使用CGLIB库生成一个被代理类的子类作为代理。CGLIB(Code Generation Library)是一个第三方代码生成库,通过继承方式实现代理。

10# AOP 实现有哪些注解?

常用的注解包括:

  • @Aspect:用于定义切面,标注在切面类上。
  • @Pointcut:定义切点,标注在方法上,用于指定连接点。
  • @Before:在方法执行之前执行通知。
  • @After:在方法执行之后执行通知。
  • @Around:在方法执行前后都执行通知。
  • @AfterReturning:在方法执行后返回结果后执行通知。
  • @AfterThrowing:在方法抛出异常后执行通知。
  • @Advice:通用的通知类型,可以替代@Before、@After等。

11# 依赖注入了解吗?怎么实现依赖注入的?

在传统编程中,当一个类需要使用另一个类的对象时,通常会在该类内部通过new关键字来创建依赖对象,这使得类与类之间的耦合度较高。

而依赖注入则是将对象的创建和依赖关系的管理交给 Spring 容器来完成,类只需要声明自己所依赖的对象,容器会在运行时将这些依赖对象注入到类中,从而降低了类与类之间的耦合度,提高了代码的可维护性和可测试性。

具体到Spring中,常见的依赖注入的实现方式,比如构造器注入、Setter方法注入,还有字段注入。

  • **构造器注入:**通过构造函数传递依赖对象,保证对象初始化时依赖已就绪。
1
2
3
4
5
6
7
8
9
@Service
public class UserService {
private final UserRepository userRepository;

// 构造器注入(Spring 4.3+ 自动识别单构造器,无需显式@Autowired)
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
  • **Setter 方法注入:**通过 Setter 方法设置依赖,灵活性高,但依赖可能未完全初始化。
1
2
3
4
5
6
7
8
public class PaymentService {
private PaymentGateway gateway;

@Autowired
public void setGateway(PaymentGateway gateway) {
this.gateway = gateway;
}
}
  • **字段注入:**直接通过 @Autowired 注解字段,代码简洁但隐藏依赖关系,不推荐生产代码。
1
2
3
4
5
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
}

12# Bean 的生命周期?

img

  1. Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
  2. Bean实例化后对将Bean的引入和值注入到Bean的属性中
  3. 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
  4. 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
  5. 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
  6. 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
  7. 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
  8. 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
  9. 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
  10. 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用

13# Bean的作用域有哪些?

Spring框架中的Bean作用域(Scope)定义了Bean的生命周期和可见性。不同的作用域影响着Spring容器如何管理这些Bean的实例,包括它们如何被创建、如何被销毁以及它们是否可以被多个用户共享。

Spring支持几种不同的作用域,以满足不同的应用场景需求。以下是一些主要的Bean作用域:

  • Singleton(单例):在整个应用程序中只存在一个 Bean 实例。默认作用域,Spring 容器中只会创建一个 Bean 实例,并在容器的整个生命周期中共享该实例。
  • Prototype(原型):每次请求时都会创建一个新的 Bean 实例。次从容器中获取该 Bean 时都会创建一个新实例,适用于状态非常瞬时的 Bean。
  • Request(请求):每个 HTTP 请求都会创建一个新的 Bean 实例。仅在 Spring Web 应用程序中有效,每个 HTTP 请求都会创建一个新的 Bean 实例,适用于 Web 应用中需求局部性的 Bean。
  • Session(会话):Session 范围内只会创建一个 Bean 实例。该 Bean 实例在用户会话范围内共享,仅在 Spring Web 应用程序中有效,适用于与用户会话相关的 Bean。
  • Application:当前 ServletContext 中只存在一个 Bean 实例。仅在 Spring Web 应用程序中有效,该 Bean 实例在整个 ServletContext 范围内共享,适用于应用程序范围内共享的 Bean。
  • WebSocket(Web套接字):在 WebSocket 范围内只存在一个 Bean 实例。仅在支持 WebSocket 的应用程序中有效,该 Bean 实例在 WebSocket 会话范围内共享,适用于 WebSocket 会话范围内共享的 Bean。
  • Custom scopes(自定义作用域):Spring 允许开发者定义自定义的作用域,通过实现 Scope 接口来创建新的 Bean 作用域。

在Spring配置文件中,可以通过<bean>标签的scope属性来指定Bean的作用域。例如:

1
<bean id="myBean" class="com.example.MyBeanClass" scope="singleton"/>

在Spring Boot或基于Java的配置中,可以通过@Scope注解来指定Bean的作用域。例如:

1
2
3
4
5
@Bean  
@Scope("prototype")
public MyBeanClass myBean() {
return new MyBeanClass();
}

Spring MVC

1# MVC 分层介绍一下

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

  • 视图(view): 为用户提供使用界面,与用户直接进行交互。
  • 模型(model): 代表一个存取数据的对象或 JAVA POJO(Plain Old Java Object,简单java对象)。它也可以带有逻辑,主要用于承载数据,并对用户提交请求进行计算的模块。模型分为两类,一类称为数据承载 Bean,一类称为业务处理Bean。所谓数据承载 Bean 是指实体类(如:User类),专门为用户承载业务数据的;而业务处理 Bean 则是指Service 或 Dao 对象, 专门用于处理用户提交请求的。
  • 控制器(controller): 用于将用户请求转发给相应的 Model 进行处理,并根据 Model 的计算结果向用户提供相应响应。它使视图与模型分离。

img

流程步骤:

  1. 用户通过View 页面向服务端提出请求,可以是表单请求、超链接请求、AJAX 请求等;
  2. 服务端 Controller 控制器接收到请求后对请求进行解析,找到相应的Model,对用户请求进行处理Model 处理;
  3. 将处理结果再交给 Controller(控制器其实只是起到了承上启下的作用);
  4. 根据处理结果找到要作为向客户端发回的响应View 页面,页面经渲染后发送给客户端。

2# 了解 Spring MVC 的处理流程吗?

img

Spring MVC的工作流程如下:

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用处理器映射器HandlerMapping。
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
  4. DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
  5. 执行处理器Handler(Controller,也叫页面控制器)。
  6. Handler执行完成返回ModelAndView
  7. HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
  11. DispatcherServlet响应用户。

简单记忆

  1. 请求进入 DispatcherServlet
  2. DispatcherServlet → HandlerMapping:找到 Controller 和方法。
  3. HandlerAdapter 调用对应的 Controller 方法。
  4. 方法执行后返回 ModelAndView。
  5. DispatcherServlet → ViewResolver:解析视图。
  6. 渲染结果返回给客户端。

简化答法:前端控制器 → 映射 → 适配器 → 控制器 → 视图解析器 → 返回结果

3# 拦截器和过滤器的区别?

  • 拦截器(Interceptor):Spring MVC 提供,基于 AOP,拦截的是 Controller 方法,只能拦 HTTP 请求。
  • 过滤器(Filter):Servlet 规范提供,拦截的是 所有资源(包括 JSP、静态资源、Servlet)。
  • 生命周期:
    • 过滤器由容器(Tomcat)管理。
    • 拦截器由 Spring 容器管理。

4# 你是怎么做全局异常处理的?

  • 使用 @ControllerAdvice + @ExceptionHandler
  • 好处:统一返回格式,避免到处写 try-catch
  • 典型写法:
1
2
3
4
5
6
7
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handle(Exception e) {
return ResponseEntity.status(500).body(e.getMessage());
}
}

Spring Boot

1# 自动装配原理是什么?

自动装配是什么?

自动装配就是 Spring Boot 根据引入的依赖和配置,自动帮我们把相关的 Bean 装配到 IoC 容器中,省去了繁琐的 XML 或 @Bean 配置。比如只要引入 spring-boot-starter-data-redis 依赖,就能直接使用 RedisTemplate。


自动装配原理(核心流程)

  1. 入口注解
    • @SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
    • 其中 @EnableAutoConfiguration 是核心
  2. 关键机制
    • @EnableAutoConfiguration@Import(AutoConfigurationImportSelector.class)
    • AutoConfigurationImportSelector 的作用:
      • 扫描:从 META-INF/spring.factories 里读取所有自动配置类
      • 过滤:根据 @ConditionalOnXxx 条件注解筛选(类存在、配置开关、Bean 存在等)
      • 排序:根据 @AutoConfigureOrder@AutoConfigureBefore/After 排序
      • 导入:把满足条件的自动配置类注册到 IoC 容器
  3. 条件装配
    • 自动配置类里常用 @ConditionalOnXxx 注解,比如:
      • @ConditionalOnClass(类存在才装配)
      • @ConditionalOnMissingBean(没有 Bean 才装配)
      • @ConditionalOnProperty(配置开启才装配)

总结口诀

  • 入口@SpringBootApplication
  • 核心@EnableAutoConfiguration
  • 关键类AutoConfigurationImportSelector
  • 步骤:扫描 → 过滤 → 排序 → 导入
  • 依据META-INF/spring.factories + @ConditionalOnXxx

2# Spring Boot 有哪些重要的注解?

Spring Boot 中一些常用的注解包括:

  • @SpringBootApplication:用于标注主应用程序类,标识一个Spring Boot应用程序的入口点,同时启用自动配置和组件扫描。
  • @Controller:标识控制器类,处理HTTP请求。
  • @RestController:结合@Controller和@ResponseBody,返回RESTful风格的数据。
  • @Service:标识服务类,通常用于标记业务逻辑层。
  • @Repository:标识数据访问组件,通常用于标记数据访问层。
  • @Component:通用的Spring组件注解,表示一个受Spring管理的组件。
  • @Autowired:用于自动装配Spring Bean。
  • @Value:用于注入配置属性值。
  • @RequestMapping:用于映射HTTP请求路径到Controller的处理方法。
  • @GetMapping、@PostMapping、@PutMapping、@DeleteMapping:简化@RequestMapping的GET、POST、PUT和DELETE请求。

另外,一个与配置相关的重要注解是:

  • @Configuration:用于指定一个类为配置类,其中定义的bean会被Spring容器管理。通常与@Bean配合使用,@Bean用于声明一个Bean实例,由Spring容器进行管理。

3# Spring Boot 怎么开启事务?

在 Spring Boot 中开启事务非常简单,只需在服务层的方法上添加 @Transactional 注解即可。

例如,假设我们有一个 UserService 接口,其中有一个保存用户的方法 saveUser():

1
2
3
public interface UserService {
void saveUser(User user);
}

我们希望在这个方法中开启事务,只需在该方法上添加 @Transactional 注解,如下所示:

1
2
3
4
5
6
7
8
9
10
11
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepository;

@Override
@Transactional
public void saveUser(User user) {
userRepository.save(user);
}
}

这样,当调用 saveUser() 方法时,Spring 就会自动为该方法开启一个事务。如果方法执行成功,事务会自动提交;如果方法执行失败,事务会自动回滚。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

4# Spring Boot 怎么做到导入就可以直接使用的?

这个主要依赖于自动配置、起步依赖和条件注解等特性。

起步依赖

起步依赖是一种特殊的 Maven 或 Gradle 依赖,它将项目所需的一系列依赖打包在一起。例如,spring-boot-starter-web 这个起步依赖就包含了 Spring Web MVC、Tomcat 等构建 Web 应用所需的核心依赖。

开发者只需在项目中添加一个起步依赖,Maven 或 Gradle 就会自动下载并管理与之关联的所有依赖,避免了手动添加大量依赖的繁琐过程。

比如,在 pom.xml 中添加 spring-boot-starter-web 依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

自动配置

Spring Boot 的自动配置机制会根据类路径下的依赖和开发者的配置,自动创建和配置应用所需的 Bean。它通过 @EnableAutoConfiguration 注解启用,该注解会触发 Spring Boot 去查找 META - INF/spring.factories 文件。

spring.factories 文件中定义了一系列自动配置类,Spring Boot 会根据当前项目的依赖情况,选择合适的自动配置类进行加载。例如,如果项目中包含 spring-boot-starter-web 依赖,Spring Boot 会加载 WebMvcAutoConfiguration 类,该类会自动配置 Spring MVC 的相关组件,如 DispatcherServlet、视图解析器等。

开发者可以通过自定义配置来覆盖自动配置的默认行为。如果开发者在 application.propertiesapplication.yml 中定义了特定的配置,或者在代码中定义了同名的 Bean,Spring Boot 会优先使用开发者的配置。


条件注解

条件注解用于控制 Bean 的创建和加载,只有在满足特定条件时,才会创建相应的 Bean。Spring Boot 的自动配置类中广泛使用了条件注解,如 @ConditionalOnClass@ConditionalOnMissingBean 等。

比如,@ConditionalOnClass 表示只有当类路径中存在指定的类时,才会创建该 Bean。例如,在 WebMvcAutoConfiguration 类中,可能会有如下代码:

1
2
3
4
5
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
public class WebMvcAutoConfiguration {
// 配置相关的 Bean
}

这段代码表示只有当类路径中存在 ServletDispatcherServletWebMvcConfigurer 类时,才会加载 WebMvcAutoConfiguration 类中的配置。

5# Spring Boot 过滤器和拦截器说一下?

在 Spring Boot 中,过滤器(Filter)和拦截器(Interceptor)是用于处理请求和响应的两种不同机制。

特性 过滤器(Filter) 拦截器(Interceptor)
规范/框架 Servlet规范(javax.servlet.Filter Spring MVC框架(org.springframework.web.servlet.HandlerInterceptor
作用范围 全局(所有请求、静态资源) Controller层(仅拦截Spring管理的请求)
执行顺序 在Servlet之前执行 在DispatcherServlet之后、Controller方法前后执行
依赖注入支持 无法直接注入Spring Bean(需间接获取) 支持自动注入Spring Bean
触发时机 doFilter()在请求前/响应后被调用 preHandlepostHandleafterCompletion分阶段触发
适用场景 全局请求处理(编码、日志、安全) 业务逻辑相关的处理(权限、参数校验)

过滤器是 Java Servlet 规范中的一部分,它可以对进入 Servlet 容器的请求和响应进行预处理和后处理。过滤器通过实现 javax.servlet.Filter 接口,并重写其中的 initdoFilterdestroy 方法来完成相应的逻辑。当请求进入 Servlet 容器时,会按照配置的顺序依次经过各个过滤器,然后再到达目标 Servlet 或控制器;响应返回时,也会按照相反的顺序再次经过这些过滤器。

拦截器是 Spring 框架提供的一种机制,它可以对控制器方法的执行进行拦截。拦截器通过实现 org.springframework.web.servlet.HandlerInterceptor 接口,并重写其中的 preHandlepostHandleafterCompletion 方法来完成相应的逻辑。当请求到达控制器时,会先经过拦截器的 preHandle 方法,如果该方法返回 true,则继续执行后续的控制器方法和其他拦截器;在控制器方法执行完成后,会调用拦截器的 postHandle 方法;最后,在请求处理完成后,会调用拦截器的 afterCompletion 方法。

过滤器和拦截器的区别如下:

  • 所属规范:过滤器是 Java Servlet 规范的一部分,而拦截器是 Spring 框架提供的机制。
  • 执行顺序:过滤器在请求进入 Servlet 容器后,在到达目标 Servlet 或控制器之前执行;拦截器在请求到达控制器之后,在控制器方法执行前后执行。
  • 使用范围:过滤器可以对所有类型的请求进行过滤,包括静态资源请求;拦截器只能对 Spring MVC 控制器的请求进行拦截。
  • 功能特性:过滤器主要用于对请求和响应进行预处理和后处理,如字符编码处理、请求日志记录等;拦截器可以更细粒度地控制控制器方法的执行,如权限验证、性能监控等。

6# Spring Boot 为什么能开箱即用?

  • 自动配置:根据依赖和配置文件,自动帮你创建 Bean。
  • Starter 机制:常用功能打包成 Starter,加入依赖就能用。
  • 内嵌 Tomcat/Jetty,直接运行 main 方法即可启动。

7# Spring Boot 自动配置是怎么实现的?

  • 关键注解:@EnableAutoConfiguration(被 @SpringBootApplication 包含)。
  • 它会扫描 META-INF/spring.factories 文件,把里面声明的 @Configuration 类加载进来。
  • 配合 @ConditionalOnClass / @ConditionalOnMissingBean 等条件注解,决定是否生效。

8# Starter 的原理?

  • Starter 本质就是一个 Maven 依赖 + 自动配置类
  • 里面写好依赖库、配置类,Spring Boot 会自动加载。
  • 例子:spring-boot-starter-web → 自动配置 Spring MVC、Tomcat。

Mybatis

1# 与传统的 JDBC 相比,MyBatis 的优点?

  • 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任 何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用。
  • 与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不 需要手动开关连接;
  • 很好的与各种数据库兼容,因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持。
  • 能够与 Spring 很好的集成,开发效率高
  • 提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射 标签,支持对象关系组件维护。

9. MyBatis 和 Hibernate 的区别?

  • Hibernate:全自动 ORM,SQL 由框架生成。开发效率高,但 SQL 不可控。
  • MyBatis:半自动 ORM,SQL 需要开发者写,灵活、可控,性能调优方便。

10. MyBatis 一级缓存、二级缓存原理?

  • 一级缓存:默认开启,作用域是 SqlSession。同一个会话内执行相同 SQL,会从缓存中取。
  • 二级缓存:需要手动开启,作用域是 Mapper namespace,多个 SqlSession 共享。
  • 原理:查询结果存入 Map,key 是 SQL + 参数。

11. MyBatis 是如何把接口方法和 SQL 绑定的?(动态代理)

  • MyBatis 会为 Mapper 接口生成 JDK 动态代理对象
  • 代理对象执行时,会根据方法名找到对应的 MappedStatement(SQL 映射),再交给 Executor 执行。

Spring Cloud

1# 了解 Spring Cloud 吗,说一下他和 Spring Boot 的区别

Spring Boot是用于构建单个Spring应用的框架,而Spring Cloud则是用于构建分布式系统中的微服务架构的工具,Spring Cloud提供了服务注册与发现、负载均衡、断路器、网关等功能。

两者可以结合使用,通过Spring Boot构建微服务应用,然后用Spring Cloud来实现微服务架构中的各种功能。

2# 用过哪些微服务组件?

img

微服务常用的组件:

  • 注册中心:注册中心是微服务架构最核心的组件。它起到的作用是对新节点的注册与状态维护,解决了「如何发现新节点以及检查各节点的运行状态的问题」。微服务节点在启动时会将自己的服务名称、IP、端口等信息在注册中心登记,注册中心会定时检查该节点的运行状态。注册中心通常会采用心跳机制最大程度保证已登记过的服务节点都是可用的。
  • 负载均衡:负载均衡解决了「如何发现服务及负载均衡如何实现的问题」,通常微服务在互相调用时,并不是直接通过IP、端口进行访问调用。而是先通过服务名在注册中心查询该服务拥有哪些节点,注册中心将该服务可用节点列表返回给服务调用者,这个过程叫服务发现,因服务高可用的要求,服务调用者会接收到多个节点,必须要从中进行选择。因此服务调用者一端必须内置负载均衡器,通过负载均衡策略选择合适的节点发起实质性的通信请求。
  • 服务通信:服务通信组件解决了「服务间如何进行消息通信的问题」,服务间通信采用轻量级协议,通常是HTTP RESTful风格。但因为RESTful风格过于灵活,必须加以约束,通常应用时对其封装。例如在SpringCloud中就提供了Feign和RestTemplate两种技术屏蔽底层的实现细节,所有开发者都是基于封装后统一的SDK进行开发,有利于团队间的相互合作。
  • 配置中心:配置中心主要解决了「如何集中管理各节点配置文件的问题」,在微服务架构下,所有的微服务节点都包含自己的各种配置文件,如jdbc配置、自定义配置、环境配置、运行参数配置等。要知道有的微服务可能可能有几十个节点,如果将这些配置文件分散存储在节点上,发生配置更改就需要逐个节点调整,将给运维人员带来巨大的压力。配置中心便由此而生,通过部署配置中心服务器,将各节点配置文件从服务中剥离,集中转存到配置中心。一般配置中心都有UI界面,方便实现大规模集群配置调整。
  • 集中式日志管理:集中式日志主要是解决了「如何收集各节点日志并统一管理的问题」。微服务架构默认将应用日志分别保存在部署节点上,当需要对日志数据和操作数据进行数据分析和数据统计时,必须收集所有节点的日志数据。那么怎么高效收集所有节点的日志数据呢?业内常见的方案有ELK、EFK。通过搭建独立的日志收集系统,定时抓取各节点增量日志形成有效的统计报表,为统计和分析提供数据支撑。
  • 分布式链路追踪:分布式链路追踪解决了「如何直观的了解各节点间的调用链路的问题」。系统中一个复杂的业务流程,可能会出现连续调用多个微服务,我们需要了解完整的业务逻辑涉及的每个微服务的运行状态,通过可视化链路图展现,可以帮助开发人员快速分析系统瓶颈及出错的服务。
  • 服务保护:服务保护主要是解决了「如何对系统进行链路保护,避免服务雪崩的问题」。在业务运行时,微服务间互相调用支撑,如果某个微服务出现高延迟导致线程池满载,或是业务处理失败。这里就需要引入服务保护组件来实现高延迟服务的快速降级,避免系统崩溃。

SpringCloud Alibaba实现的微服务架构:

img

  • SpringCloud Alibaba中使用Alibaba Nacos组件实现注册中心,Nacos提供了一组简单易用的特性集,可快速实现动态服务发现、服务配置、服务元数据及流量管理。
  • SpringCloud Alibaba 使用Nacos服务端均衡实现负载均衡,与Ribbon在调用端负载不同,Nacos是在服务发现的同时利用负载均衡返回服务节点数据。
  • SpringCloud Alibaba 使用Netflix FeignAlibaba Dubbo组件来实现服务通行,前者与SpringCloud采用了相同的方案,后者则是对自家的RPC 框架Dubbo也给予支持,为服务间通信提供另一种选择。
  • SpringCloud Alibaba 在API服务网关组件中,使用与SpringCloud相同的组件,即:SpringCloud Gateway
  • SpringCloud Alibaba在配置中心组件中使用Nacos内置配置中心,Nacos内置的配置中心,可将配置信息存储保存在指定数据库
  • SpringCloud Alibaba在原有的ELK方案外,还可以使用阿里云日志服务(LOG)实现日志集中式管理。
  • SpringCloud Alibaba在分布式链路组件中采用与SpringCloud相同的方案,即:Sleuth/Zipkin Server
  • SpringCloud Alibaba使用Alibaba Sentinel实现系统保护,Sentinel不仅功能更强大,实现系统保护比Hystrix更优雅,而且还拥有更好的UI界面。

12. 注册中心宕机了怎么办?

  • Eureka:有自我保护机制,短时间内还能维持服务发现。
  • Nacos:支持 AP/CP 切换,推荐部署集群,避免单点故障。

13. Feign 是怎么实现远程调用的?

  • 本质是 声明式 HTTP 客户端
  • 通过 JDK 动态代理,拦截接口方法调用 → 构造 HTTP 请求 → 发送远程调用。
  • 底层默认用 Ribbon(负载均衡)+ RestTemplate,或者 OpenFeign + HTTPClient。

14. Gateway 的核心功能?

  • 路由转发:请求 → 目标服务。
  • 过滤器:日志、权限校验、参数处理。
  • 负载均衡:整合注册中心,实现动态路由。
  • 限流熔断:基于 Resilience4j、Redis 实现。

15. 你们项目里怎么做统一鉴权的?

  • Gateway 里加全局过滤器:
    • 拦截请求,校验 Token(JWT/Redis)。
    • 不通过 → 拦截返回 401。
    • 通过 → 请求继续转发到微服务。

16. 配置中心怎么实现动态刷新?

  • Spring Cloud Config / Nacos Config 支持 长轮询/消息推送
  • Spring Boot 应用里加 @RefreshScope,当配置中心推送更新时,Bean 会重新加载配置。

17. 什么是服务雪崩?怎么解决?

  • 服务雪崩:一个服务不可用,导致调用它的服务阻塞,最终整个系统不可用。
  • 解决办法:
    • 限流(RateLimit)
    • 熔断(CircuitBreaker)
    • 隔离(线程池/舱壁模式)
    • 降级(返回默认结果)

18. 熔断和限流有什么区别?

  • 熔断:下游服务不可用时,快速失败,避免请求堆积。
  • 限流:控制请求流量(QPS),保护系统不被压垮。
  • 类比:
    • 熔断 = 停电时保险丝断开。
    • 限流 = 水龙头限流阀。