欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > Spring相关面试题总结

Spring相关面试题总结

2025/4/3 12:12:23 来源:https://blog.csdn.net/cwtlw/article/details/146920811  浏览:    关键词:Spring相关面试题总结

Spring面试题

1.对SpringIOC的理解

1.什么是IOC

​ IOC(Inversion of Control):对象的创建、依赖注入及生命周期管理由Spring容器负责,而非程序员手动控制(如 new 对象)。

​ 核心思想:将控制权交给框架,实现解耦。

2.IOC的实现方式

​ 依赖注入(DI,Dependency Injection):Spring 通过 构造函数注入,Setter注入 或 字段注入自动装配依赖对象。

3.IOC的优势

​ 解耦:对象间依赖由容器管理,降低代码耦合度。

​ 可测试性:依赖可替换(如 Mock 对象)。

​ 同一管理:Bean的生命周期、作用域(Singleton/Prototype)由容器控制。

2.对SpringAOP的理解

1.什么是AOP?

​ AOP(Aspect-Oriented Programming):通过横向切割代码,将通用逻辑(如日志、事务、权限)从业务逻辑中分离,实现解耦和代码复用。

​ 核心思想:在不修改源代码的情况下,动态增强方法功能。

2.AOP的核心概念

术语说明
切面(Aspect)封装横切逻辑的模块(如日志切面、事务切面),通常是一个@Aspect类。
连接点(JoinPoint)程序执行的点(如方法调用、异常抛出),Spring AOP仅支持方法级别。
通知(Advice)切面在连接点的动作(如@Before@After)。
切入点(Pointcut)定义哪些连接点会被切面处理(通过表达式匹配,如execution(* com.example.*.*(..)))。
目标对象(Target)被代理的原始对象(如UserService)。
代理(Proxy)Spring通过JDK动态代理或CGLIB生成增强后的对象。

3.SpringAOP的实现方式

​ 注解配置(主流方式)

@Aspect
@Component
public class LogAspect {// 定义切入点:匹配com.example.service包下所有方法@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}// 前置通知:在目标方法执行前运行@Before("serviceMethods()")public void beforeAdvice(JoinPoint jp) {System.out.println("方法调用前: " + jp.getSignature().getName());}// 环绕通知:控制方法执行@Around("serviceMethods()")public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("方法执行前...");Object result = pjp.proceed(); // 调用目标方法System.out.println("方法执行后...");return result;}
}

4.Spring AOP的局限

​ 仅支持方法级别的拦截:无法拦截字段访问或构造器调用。

​ 仅作用于Spring管理的Bean:对非容器对象无效。

​ 自调用问题:同一类内方法调用不会触发AOP(需通过代理对象调用)。。

3.说一下SpringMVC的运行流程

1.用户发起请求

​ 用户通过浏览器发送 HTTP 请求,请求到达Web容器(如 Tomcat)。

2.DispatcherServlet 拦截请求

​ 前端控制器 DispatcherServlet 拦截所有请求,作为统一入口。

3.调用 HandlerMapping

​ DispatcherServlet 通过 HandlerMapping 查找请求对应的处理器(Handler)和 拦截器(Interceptor)。

4.执行拦截器(Interceptor)

​ 若配置了拦截器链(如登录校验),按顺序执行 preHandle() 方法。 若任意拦截器返回 false,流程终止。

5.调用 HandlerAdapter

​ HandlerAdapter 适配器调用目标处理器(Controller 方法)。

​ 常见实现:

RequestMappingHandlerAdapter:处理 @Controller 注解方法。

HttpRequestHandlerAdapter:处理 HttpRequestHandler 接口。

6.执行 Controller

​ Controller 方法处理业务逻辑,返回逻辑视图名(String)或 ModelAndView 对象。

7.处理返回值(ViewResolver)

​ 视图解析器(ViewResolver)将逻辑视图名解析为实际视图对象(如JSP、Thymeleaf模板)。

8.渲染视图(View)

​ 视图对象渲染模型数据,生成HTML相应。

​ 若返回 @ResponseBody,直接写入 JSON/XML数据(跳过视图解析)。

9.返回响应

​ 渲染结果通过 DispatcherServlet 返回给用户浏览器。

4.SpringMVC常见的注解有哪些

1.请求映射注解

注解作用示例
@RequestMapping通用请求映射(可指定HTTP方法、路径等)。@RequestMapping("/user")
@GetMapping简化版GET请求映射(等价于@RequestMapping(method=RequestMethod.GET))。@GetMapping("/list")
@PostMapping简化版POST请求映射。@PostMapping("/create")
@PutMapping简化版PUT请求映射。@PutMapping("/update/{id}")
@DeleteMapping简化版DELETE请求映射。@DeleteMapping("/delete/{id}")

2.参数处理注解

注解作用示例
@RequestParam获取URL参数或表单数据(默认必传,可设required=false)。@RequestParam("name") String username
@PathVariable获取RESTful路径变量。@GetMapping("/user/{id}") public User getById(@PathVariable Long id)
@RequestBody解析请求体为对象(如JSON/XML)。@PostMapping("/save") public void save(@RequestBody User user)
@RequestHeader获取HTTP请求头。@RequestHeader("User-Agent") String userAgent

3.响应处理注解

注解作用示例
@ResponseBody将返回值直接写入HTTP响应体(如返回JSON)。@ResponseBody @GetMapping("/info")
@RestController组合注解(@Controller + @ResponseBody),用于REST API。@RestController @RequestMapping("/api")

4.文件上传

注解作用示例
@MultipartFile处理文件上传(需配合multipart/form-data)。@PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile file)

5.跨域处理

注解作用示例
@CrossOrigin允许跨域请求(可细化到方法或控制器)。@CrossOrigin(origins = "https://example.com")

5.谈谈你对SpringBoot的理解以及优点

1.什么是SpringBoot?

​ SpringBoot 是 Spring 生态的快速开发框架,通过约定大于配置的理念,简化了Spring 应用的初始搭建和开发流程。

​ 核心目标:让开发者专注于业务逻辑,而非繁琐的配置。

2.SpringBoot的核心优点

优点说明示例
1. 快速启动内置 Tomcat/Jetty,无需部署 WAR 包,直接运行 main 方法启动应用。@SpringBootApplication + SpringApplication.run(MyApp.class, args)
2. 自动配置根据依赖自动配置 Bean(如引入 spring-boot-starter-data-jpa 自动配数据源)。无需手动配置 DataSourceEntityManager 等。
3. 起步依赖通过 starter 依赖一键引入相关技术栈(如 spring-boot-starter-web)。Maven 中只需添加一个依赖,而非多个分散的库。
4. 无 XML 配置完全基于 Java 注解和配置类,告别繁琐的 XML。使用 @Configuration 替代 applicationContext.xml
5. 嵌入式服务器内置 Tomcat/Jetty/Undertow,无需额外安装服务器。打包为可执行 JAR,直接 java -jar 运行。
6. 生产就绪提供监控端点(如 /actuator/health)、指标收集、外部化配置等。集成 Spring Actuator 监控应用状态。
7. 丰富的生态整合无缝集成 Spring 生态(Spring Data、Spring Security等)及第三方库(Redis、Kafka)。通过 starter 快速集成 Redis:spring-boot-starter-data-redis

6.SpringBoot读取配置的方式有几种

1.默认配置文件

​ 通过@Value注解进行读取

​ 优先级:最低(被其他配置覆盖)

# application.yml
app:name: "MyApp"timeout: 5000
@Value("${app.name}")
private String appName;

2.环境变量与命令行参数

​ 优先级:最高(覆盖其他配置)

命令行参数:

java -jar app.jar --server.port=8081

环境变量:

export APP_NAME="MyApp"
@Value("${APP_NAME}")
private String appName;

3.@ConfigurationProperties绑定到对象

适用场景:批量读取配置文件并封装为Java对象

# application.yml
myapp:api:url: "https://api.example.com"key: "123456"
@Configuration
@ConfigurationProperties(prefix = "myapp.api")
public class ApiConfig {private String url;private String key;// Getter/Setter
}// 注入使用
@Autowired
private ApiConfig apiConfig;

4.编程式读取(Environment接口)

适用场景:动态判断配置是否存在或灵活处理

@Autowired
private Environment env;public void demo() {String dbUrl = env.getProperty("spring.datasource.url");boolean debug = env.getProperty("app.debug", Boolean.class, false);
}

7.SpringBoot配置文件有几种类型,区别是什么

(1) .properties 文件

​ 格式:键值对形式,使用 = 或 :分割

​ 特点:简单直观,适合简单配置。 不支持层级结构,重复前缀较多。

server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb

(2) .yml/.yaml 文件

​ 格式:基于缩进的层级结构,使用:和换行表示层级

​ 特点:结构化清晰,适合复杂配置。 支持多环境配置(通过—分割)。注意缩进(必须使用空格,不能用Tab)

server:port: 8080
spring:datasource:url: jdbc:mysql://localhost:3306/mydb

(3) 多环境配置文件

​ 命令规则:application-{profile}.properties 或 application-{profile}.yml。

​ 示例:

​ application-dev.yml (开发环境)

​ application-prod.yml (生产环境)

​ 激活方式:

​ 命令行参数:–spring.profiles.active==dev

​ 环境变量:export SPRING_PROFILES_ACTIVE=prod

对比项.properties.yml多环境配置
格式扁平键值对(key=value层级缩进(YAML 语法)通过 -{profile} 后缀区分
可读性一般(重复前缀多)高(结构化清晰)按环境分离,维护方便
复杂配置支持强(支持数组、对象等)独立文件,避免配置混杂
多环境管理需多个文件支持单文件多环境(---分隔)显式激活指定环境
兼容性所有 Spring 版本Spring Boot 优先推荐通用

8.SpringBoot的核心注解是哪个

@SpringBootApplication 是Spring Boot应用的启动入口注解,标注在主启动类上,整合了以下三个关键注解的功能:

@SpringBootConfiguration  // 标记该类为配置类
@EnableAutoConfiguration  // 启用自动配置
@ComponentScan           // 自动扫描当前包及其子包的组件
子注解功能说明
@SpringBootConfiguration继承自 @Configuration,表示该类是一个 Spring 配置类(定义 Bean 的地方)。
@EnableAutoConfiguration启用 Spring Boot 的自动配置机制(根据依赖自动配置 Bean,如数据源、MVC 等)。
@ComponentScan自动扫描当前类所在包及其子包中的 @Component@Service@Controller 等。

9.SpringBoot的自动装配原理

1.自动装配的核心机制

​ 自动装配的本质是根据项目依赖和配置,动态注册符合条件的 Bean 到 Spring 容器,无需手动编写 @Bean 配置。

2.实现自动装配的关键步骤

​ 触发条件:@EnableAutoConfiguration

​ @SpringBootApplication 组合了 @EnableAutoConfiguration,该注解通过 @Import 加载自动配置逻辑。

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { ... }

3.自动装配的工作流程

1.启动阶段:

​ SpringBoot启动时,AutoConfigurationImportSelector 扫描所有 AutoConfiguration.imports 文件。

​ 2.条件过滤:

​ 根据 @Conditional 注解排除不满足条件的配置类(如缺少依赖类、Bean已存在等)。

​ 3.Bean注册:

​ 剩下的自动配置类 通过 @Bean 方法向容器注册组件 (如DataSource等)

​ 4.属性绑定:

​ 通过@ConfigurationProperties 将 application.yml 中的配置注入到Bean。

10.Spring中Bean的注入方式有几种

Spring支持 3 种核心依赖注入方式(按注入途径分类)。

1.构造器注入(Constructor Injection)

​ 方式:通过类的构造方法注入依赖。

​ 特点:

​ 强依赖:适合必须的依赖项。

​ 不可变:依赖字段可设为 final,保证线程安全。

​ 推荐:Spring 官方推荐方式(尤其Spring 4.3+ 可省略 @Autowired)

@Service
public class UserService {private final OrderService orderService;// Spring 4.3+ 自动识别构造器注入public UserService(OrderService orderService) {this.orderService = orderService;}
}

2.Setter 注入(Setter Injection)

​ 方式:通过Setter 方法注入依赖。

​ 特点:

​ 可选依赖:适合非必须的依赖

​ 灵活性高:可动态重新注入(如测试时 Mock 对象)。

@Service
public class UserService {private OrderService orderService;@Autowired  // 也可用 @Resourcepublic void setOrderService(OrderService orderService) {this.orderService = orderService;}
}

3.字段注入(Field Injection)

​ 方式:直接通过字段注入依赖。

​ 特点:

​ 简洁但隐蔽:代码少,但隐藏依赖关系,不利于测试和维护。

​ 不推荐:破坏封装性,无法注入 final 字段。

注入方式优点缺点适用场景
构造器注入不可变、线程安全、显式声明依赖参数较多时代码略冗长推荐 强制依赖场景
Setter 注入灵活、可选依赖可能因遗漏调用导致 NPE可选依赖或需动态配置
字段注入代码简洁隐藏依赖、难测试快速原型开发(不推荐生产)

11.Spring中Bean的作用域有几种

1.Singleton(单例,默认)

​ 特点:整个Spring容器中只存在一个 Bean 实例。

​ 适用场景:无状态的Bean(如Service、DAO)。

​ 配置方式:

@Scope("singleton")  // 或默认不写
@Service
public class UserService { ... }

2.Prototype(原型/多例)

​ 特点:每次请求(getBean() 或 注入)都会创建新实例。

​ 适用场景:有状态的 Bean(如DTO、Session)

​ 配置方式:

@Scope("prototype")
@Component
public class User { ... }

3.Request(请求域,Web)

​ 特点:每个Http请求创建一个新实例,请求结束后销毁。

​ 适用场景:存储请求相关数据(如用户表单数据)

​ 配置方式:

@Scope("request")
@Component
public class LoginInfo { ... }

4.Session(会话域,Web)

​ 特点:每个用户会话(Session)创建一个实例,会话过期后销毁。

​ 适用场景:用户登录状态,购物车。

​ 配置方式:

@Scope("session")
@Component
public class ShoppingCart { ... }

5.Application(全局Web应用域)

​ 特点:整个Web应用共享一个实例,类似ServletContext。

​ 适用场景:全局配置、缓存。

​ 配置方式:

@Scope("application")
@Component
public class AppConfig { ... }

6.WebSocket(WebSocket会话域)

​ 特点:每个WebSocket 会话一个实例,会话结束后销毁。

​ 适用场景:实时通信(如聊天室)。

12.SpringAOP的通知类型有哪些

SpringAOP提供了 5 种通知类型,用于在目标方法的不同执行阶段插入横切逻辑:

1.@Before(前置通知)

​ 执行时机:目标方法执行前触发。

​ 适用场景:权限校验、日志记录、参数预处理。

@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice(JoinPoint jp) {System.out.println("方法执行前:" + jp.getSignature().getName());
}

2.@AfterReturning(返回后通知)

​ 执行时机:目标方法正常返回后触发(不抛异常时)。

​ 适用场景:记录返回值、结果处理。

@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void afterReturningAdvice(Object result) {System.out.println("方法返回结果:" + result);
}

3.@AfterThrowing(异常通知)

​ 执行时机:目标方法抛出异常后触发。

​ 适用场景:异常捕获、错误日志记录。

@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void afterThrowingAdvice(Exception ex) {System.out.println("方法抛出异常:" + ex.getMessage());
}

4.@After(最终通知)

​ 执行时机:目标方法执行完毕后触发(无论是否抛出异常,类似finally)

​ 适用场景:资源清理、日志记录。

@After("execution(* com.example.service.*.*(..))")
public void afterAdvice() {System.out.println("方法执行结束(无论成功或失败)");
}

5.Around(环绕通知)

​ 执行时机:包裹目标方法,可控制方法执行前后、返回值、异常等。

​ 适用场景:事务管理、性能监控、方法重试。

@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("方法执行前...");Object result = pjp.proceed();  // 调用目标方法System.out.println("方法执行后...");return result;
}

13.@Autowired 和 @Resource的区别

1.@Autowired使用方式

@Service
public class UserService {// 默认按类型注入@Autowired  private OrderService orderService;// 按名称注入(需配合 @Qualifier)@Autowired  @Qualifier("userDaoImpl")  private UserDao userDao;
}

2.@Resource使用方式

@Service
public class UserService {// 默认按名称注入(字段名需与 Bean 名一致)@Resource  private OrderService orderService;// 显式指定名称@Resource(name = "userDaoImpl")  private UserDao userDao;
}
对比项@Autowired (Spring)@Resource (JSR-250, Java标准)
来源Spring 框架提供Java 标准注解(javax.annotation包)
默认注入方式按类型(byType)按名称(byName),找不到则回退到 byType
名称指定需配合 @Qualifier 指定 Bean 名称直接通过 name 属性指定(如 @Resource(name="userService")
适用场景推荐 Spring 生态使用需要兼容非 Spring 环境(如 J2EE)
依赖查找顺序1. 按类型匹配 → 2. 有 @Qualifier 则按名称1. 按名称匹配 → 2. 找不到则按类型
是否支持构造函数注入✔️ 支持(Spring 4.3+ 可省略 @Autowired❌ 不支持

14.@Component 和 @Bean注解的区别?

1.声明方式

​ @Component 是类注解,通过组件扫描(@ComponentScan)自动注册 Bean。

​ @Bean 是方法注解,需在 @Configuration 类中显示定义Bean。

2.控制权

​ @Component 由 Spring自动实例化。

​ @Bean 由开发者完全控制实例化过程(如条件装配@Conditional)

3.适用对象

​ @Component 适用于自定义类。

​ @Bean 适用于无法修改源码的类(如第三方库的 DataSource)。

对比项@Component@Bean
作用目标类级别(标记类为 Spring 组件)方法级别(在配置类中定义 Bean)
使用场景自动扫描并注册 Bean(如 @Service, @Repository手动控制 Bean 的创建逻辑(如第三方库的类)
依赖注入自动完成(通过 @Autowired需在方法中显式构造并返回对象
灵活性适用于标准 Bean更灵活,可自定义初始化/销毁逻辑
示例java @Component public class UserService { ... }java @Configuration public class AppConfig { @Bean public DataSource dataSource() { return new HikariDataSource(); } }

15.常见的HTTP响应状态码

HTTP状态码用于表示服务器对请求的处理结果,分为5类(以首位数字区分):

1xx(信息性状态码)

  • 100 Continue:客户端应继续发送请求(用于大文件上传前确认)。
  • 101 Switching Protocols:服务器同意切换协议(如 WebSocket)。

2xx(成功状态码)

  • 200 OK:请求成功(GET/POST 返回数据正常)。
  • 201 Created:资源创建成功(如 POST 新增数据后返回)。
  • 202 Accepted:请求已接受但未处理完成(异步任务)。
  • 204 No Content:请求成功,但无返回内容(如 DELETE 请求)。

3xx(重定向状态码)

  • 301 Moved Permanently:资源永久重定向(SEO 会更新链接)。
  • 302 Found:资源临时重定向(浏览器会缓存原地址)。
  • 304 Not Modified:资源未修改(缓存生效,用于协商缓存)。

4xx(客户端错误)

  • 400 Bad Request:请求语法错误(如参数格式错误)。
  • 401 Unauthorized:未认证(需登录或 Token 无效)。
  • 403 Forbidden:无权限访问(认证成功但权限不足)。
  • 404 Not Found:资源不存在(路径错误或已删除)。
  • 405 Method Not Allowed:请求方法不被允许(如 GET 接口用 POST 调用)。
  • 429 Too Many Requests:请求过于频繁(限流触发)。

5xx(服务器错误)

  • 500 Internal Server Error:服务器内部错误(代码异常)。
  • 502 Bad Gateway:网关/代理服务器收到无效响应(如上游服务崩溃)。
  • 503 Service Unavailable:服务不可用(如系统维护或过载)。
  • 504 Gateway Timeout:网关超时(上游服务未及时响应)。

16.GET和POST的区别

HTTP协议中,GET 和 POST 是最常用的两种请求方法。核心区别如下:

1. 语义与用途

GETPOST
获取资源(幂等操作,不应修改数据)提交数据(非幂等,可能修改数据)
适用于查询、搜索、分页等场景适用于创建、更新、删除等场景

2. 数据传输方式

GETPOST
数据通过 URL 的 Query 参数 传递(如 ?name=foo&age=20数据通过 请求体(Body) 传递(支持 JSON、Form-Data 等格式)
数据可见(暴露在地址栏,不安全)数据不可见(相对安全)
URL 长度受限(浏览器通常限制为 2KB~8KB)数据大小无严格限制(服务器可配置)

3. 缓存与历史记录

GETPOST
可被浏览器缓存、保留历史记录不会被缓存,重复提交可能触发警告(如“确认重新提交表单”)
书签或链接可直接访问(含参数)书签无法保存请求体数据

4. 安全性

GETPOST
参数在 URL 中,容易被日志或浏览器历史记录泄露数据在 Body 中,适合传输敏感信息(但需配合 HTTPS)
CSRF 攻击风险更高(因 URL 可被恶意构造)相对安全(仍需防护 CSRF)

5. 幂等性与副作用

GETPOST
幂等(多次请求结果相同,无副作用)非幂等(多次请求可能产生不同结果,如重复提交订单)
适用于只读操作适用于写操作

17.Cookie 和 Session 的区别

1.存储机制

​ Cookie:

​ 数据以键值对形式存储在浏览器中(可通过document.cookie 访问)

​ 每次请求会自动附加到HTTP头的 Cookie 字段发送给服务器。

​ Session:

​ 数据存储在服务端,客户端仅保存一个Session ID(通常通过 Cookie 传递)。

​ 服务器根据Session ID 查询对应的客户数据。

2.安全性对比

​ Cookie:

​ 明文存储,容易被篡改(需配合 HttpOnly、Secure 标志提升安全)。

​ Session:

​ 敏感数据(如用户ID)存在服务端,仅暴露Session ID,更安全。

3.生命周期控制

​ Cookie:

​ 通过 Max-Age 或 Expires 设置有效期(如 Max-Age=3600 表示 1小时后过期)

​ 关闭浏览器后,默认的会话Cookie失效。

​ Session:

​ 服务端可设置超时时间(如 Spring Session 的 server.servlet.session.timeout=30m)。

​ 浏览器关闭后,Session ID 丢失导致会话失效(除非持久化Cookie)。

4.跨域与共享

​ Cookie:

​ 受同源策略限制,跨域需设置 SameSite=None + Secure。

​ Session:

​ 多服务器环境下需集中存储(如 Redis),否则默认无法共享。

对比项CookieSession
存储位置客户端(浏览器)服务端(服务器内存、数据库、Redis等)
安全性较低(可被篡改或窃取)较高(敏感信息存在服务端)
数据大小单个 Cookie ≤ 4KB,域名下总数有限理论上无限制(受服务器内存影响)
生命周期可设置过期时间(Expires/Max-Age通常依赖会话(浏览器关闭后默认失效,可设置超时)
跨域支持受同源策略限制天然支持(因为数据在服务端)
性能影响每次请求自动携带,增加带宽需服务端存储和查询,占用服务器资源
典型用途记住登录状态、跟踪用户行为保存用户会话信息(如登录凭证、购物车)

18.redirect 和 forward 的区别

1.工作流程

​ redirect(重定向):

​ 浏览器请求 URL-A。

​ 服务器返回302/307 状态码 + Location:/URL-B。

​ 浏览器自动请求 URL-B。

​ 服务器返回 URL-B 的内容。

​ forward(转发):

​ 浏览器请求 URL-A。

​ 服务器在内部将请求转发给 URL-B 处理。

​ 最终返回 URL-B 的结果,但浏览器仍然显示 URL-A。

2.数据共享

​ redirect:

​ 原始请求的 request 数据丢失(因为是两次独立请求)。

​ 可通过 URL 参数 或 Session 传递数据。

​ foward:

​ 共享同一个 request 对象,可直接传递属性。

对比项redirect(重定向)forward(转发)
工作方式客户端行为(返回 302/307 状态码,浏览器发起新请求)服务器内部跳转(同一请求在服务端传递)
URL 变化会变(显示新地址)不变(浏览器地址栏不变)
请求次数2 次(客户端发起两次 HTTP 请求)1 次(服务端内部处理)
数据传递不能直接共享原始请求数据(需通过 URL 或 Session)可共享 requestresponse 对象
性能较慢(多一次网络往返)较快(无额外网络开销)
适用场景跨应用跳转、防止表单重复提交同一应用内的页面跳转(如 MVC 控制器间)
实现示例response.sendRedirect("/new-url")request.getRequestDispatcher("/path").forward(request, response)

19.Spring事务失效场景

1.方法访问权限问题

​ 非 public 方法:@Transactional 只能用于 public 方法。

​ 原因:SpringAOP 代理机制限制

@Transactional
private void updateOrder() { // 事务失效// ...
}

2.方法自调用问题

​ 同一个类中方法调用:A方法(无事务)调用B方法(有事务)

​ 解决方案:

​ 使用 AopContext.currentProxy()

​ 将方法拆分到不同类中

public void process() {this.updateOrder(); // 事务失效
}@Transactional
public void updateOrder() {// ...
}

3.异常类型不正确

​ 默认只回滚 RuntimeException 和 Error

​ 检查型异常不会触发回滚

@Transactional
public void update() throws Exception { // 抛出检查型异常不会回滚// ...
}

​ 解决方案:

@Transactional(rollbackFor = Exception.class)

4.异常被捕获

​ 异常被 catch 后没有重新抛出

@Transactional
public void update() {try {// ...} catch (Exception e) {e.printStackTrace(); // 事务失效}
}

5.数据库引擎不支持

​ 必须使用 InnoDB,MyISAM引擎只支持表锁,不支持事务。

6.传播行为设置不当

​ PROPAGATION_NOT_SUPPORTED:挂起当前事务

​ PROPAGATION_NEVER:不能有事务

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void update() {// 无事务运行
}

SpringCloud面试题

1.SpringCloud的核心组件有哪些?

SpringCloud为分布式系统开发提供了一套完整的解决方案,以下是其核心组件及其作用:

1.服务注册于发现

① Eureka (Netflix)

​ 作用:服务注册中心,管理所有微服务的地址信息。

​ 关键特性:

​ 服务注册:微服务启动时向Eureka注册

​ 服务发现:通过服务名调用而非IP地址

​ 心跳检测:定期检查服务健康状态

② Nacos (Alibaba)

​ 优势:同时支持服务注册与配置中心,AP/CP模式可切换

​ 对比Eureka:

​ 提供DNS-F功能(支持权重路由)

​ 集成配置管理

​ 支持K8s集成

2.服务调用

Ribbon (客户端负载均衡)

​ 功能:

​ 基于服务名的负载均衡(轮询、随机、权重等策略)

​ 与RestTemplate/OpenFegin集成

​ 示例:

@Bean
@LoadBalanced // 开启负载均衡
public RestTemplate restTemplate() {return new RestTemplate();
}

OpenFeign (声明式HTTP客户端)

​ 特点:

​ 通过接口+注解定义HTTP请求

​ 内置Ribbon负载均衡

​ 支持熔断降级

​ 示例:

@FeignClient(name = "order-service")
public interface OrderClient {@GetMapping("/orders/{id}")Order getOrder(@PathVariable Long id);
}

3.服务容错

① Hystrix (熔断降级)

​ 核心机制:

​ 熔断:当失败率超过阈值时自动切断请求

​ 降级:返回预设的fallback结果

​ 隔离:线程池/信号量隔离资源

② Sentinel (Alibaba替代方案)

​ 优势:

​ 实时监控和控制台

​ 支持流量控制、熔断机制、系统保护

​ 规则可动态配置

4.服务网关

① Zuul (Netflix)

​ 功能:

​ 路由转发:/user-service/** -> 用户服务

​ 过滤器:权限校验、限流

② Spring Cloud Gateway (官方推荐)

​ 优势:

​ 基于WebFlux的非阻塞模型

​ 支持Predicate和Filter链

​ 更好的性能

​ 示例:

spring:cloud:gateway:routes:- id: order-serviceuri: lb://order-servicepredicates:- Path=/order/**

5.配置中心

① Spring Cloud Config

​ 架构:

​ 配置存储在Git/SVN等版本库

​ 客户端通过HTTP获取配置

② Nacos Config

​ 优势:

​ 配置动态刷新(无需重启)

​ 与服务发现共用同一组件

​ 示例:

@RefreshScope // 支持动态刷新
@RestController
public class ConfigController {@Value("${app.config}")private String config;
}

6.消息驱动

Spring Cloud Stream

​ 作用:统一消息中间件接口(Kafka/RabbitMQ)

7.分布式链路追踪

① Sleuth + Zipkin

​ 功能:

​ 为请求添加唯一Trace ID

​ 记录调用链耗时

② SkyWalking (APM工具)

​ 优势:

​ 可视化拓扑图

​ 支持多种语言探针

2.OpenFeign是如何使用的?

OpenFeign 是 Spring Cloud 提供的声明式 HTTP 客户端,用于简化微服务间的 RESTful 调用。

一、基础配置

​ 1.添加依赖

<!-- Spring Cloud OpenFeign -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

​ 2.启用 Feign 客户端

​ 在启动类添加 @EnableFeignClients 注解:

@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}

二、基本使用

​ 1.定义Feign客户端接口

@FeignClient(name = "user-service") // 指定服务名称
public interface UserClient {@GetMapping("/users/{id}") // 映射服务端点User getUserById(@PathVariable("id") Long id);@PostMapping("/users")User createUser(@RequestBody User user);
}

​ 2.注入使用

@RestController
@RequestMapping("/orders")
public class OrderController {@Autowiredprivate UserClient userClient;@GetMapping("/{orderId}/user")public User getUserByOrder(@PathVariable Long orderId) {// 像调用本地方法一样调用远程服务return userClient.getUserById(orderId);}
}

3.RabbitMQ工作模式有哪些?

RabbitMQ 作为流行的消息中间件,支持多种工作模式以满足不同业务场景需求。

​ 1.简单模式(Simple)

​ 架构:生产者 → 队列 → 消费者

​ 特点:

​ 最简单的消息队列模式

​ 单一生产者、单一队列、单一消费者

​ 消息一旦被消费就从队列种删除

​ 适用场景:单对单的简单消息传递

​ 2.工作队列模式(Work Queue)

​ 架构:

生产者 → 队列 → 消费者1→ 消费者2

​ 特点:

​ 一个队列对应多个消费者

​ 消息被竞争消费(轮询分发)

​ 默认采用公平分发(Fair Dispatch)

​ 适用场景:任务分发、负载均衡

​ 3.发布/订阅模式(Publish/Subscribe)

​ 架构:

生产者 → 交换机(Fanout) → 队列1 → 消费者1→ 队列2 → 消费者2

​ 特点:

​ 使用 Fanout 类型交换机

​ 消息广播到所有绑定队列

​ 每个消费者获取全量消息

​ 适用场景:日志广播、事件通知

​ 4.路由模式(Routing)

​ 架构:

生产者 → 交换机(Direct) → 队列1(路由键error)→ 队列2(路由键info/warning)

​ 特点:

​ 使用 Direct 类型交换机

​ 基于路由键(routing key)精确匹配

​ 支持多重条件绑定

​ 适用场景:分类消息处理(如日志级别区分)

​ 5.主题模式(Topics)

​ 架构:

生产者 → 交换机(Topic) → 队列1(user.*)→ 队列2(*.order)

​ 特点:

​ 使用 Topic 类型交换机

​ 支持通配符匹配路由键:* 匹配一个单词,# 匹配零或多个单词

​ 灵活性最高的路由方式

​ 适用场景:多维度消息过滤(如地理位置、设备类型)

​ 6.RPC模式(Remote Procedure Call)

​ 架构:

客户端 → 请求队列 → 服务端↑               ↓└── 回调队列 ←──┘

​ 特点:

​ 实现远程过程调用

​ 需要关联ID(corrlationId)匹配请求响应

​ 使用临时回调队列

4.RabbitMQ的体系结构有哪些内容?

RabbitMQ 是一个基于 AMQP(Advanced Message Queuing Protocol)的开源消息代理系统。

​ 1.核心组件

组件说明
Producer消息生产者,通过Exchange发送消息
Consumer消息消费者,从Queue接收消息
Exchange消息路由中心,决定消息如何分发到Queue
Queue存储消息的缓冲区,FIFO结构
BindingExchange与Queue之间的绑定规则
ConnectionTCP长连接(建议复用)
Channel虚拟连接(复用TCP连接的轻量级通道)
Virtual Host虚拟隔离环境(类似MySQL的database)

​ 2.Exchange 类型与路由机制

类型路由规则典型应用场景
Direct精确匹配routing_key点对点精准投递
Fanout广播到所有绑定队列(忽略routing_key发布/订阅模式
Topic通配符匹配(*匹配单个词,#匹配多个词)多维度消息分类
Headers根据消息headers属性匹配(忽略routing_key复杂条件路由

​ 3.消息生命周期

​ 1.生产者发布消息 -> Exchange

​ 2.Exchange根据类型和Binding规则 -> 路由到Queue

​ 3.Queue存储消息(持久化消息会写入磁盘)

​ 4.消费者订阅Queue -> 获取消息

​ 5.消息确认(ACK) -> 从Queue删除

5.什么场景适合使用RabbitMQ?

RabbitMQ 作为一款功能强大的消息中间件,适合以下核心场景:

​ 1.应用解耦:

​ 当系统模块间需要松耦合通信时,通过消息队列隔离生产者和消费者。

​ 优势:

​ 库存服务升级不影响订单服务。

​ 避免直接HTTP调用导致的级联故障

// 订单服务(生产者)
orderService.createOrder() {saveOrderToDB();rabbitTemplate.convertAndSend("order.created", orderId); // 发送消息
}// 库存服务(消费者)
@RabbitListener(queues = "order.created")
public void deductStock(String orderId) {inventoryService.deduct(orderId);
}

​ 2.异步处理

​ 将非核心流程异步化,提升主流程响应速度。

​ 典型案例:

​ 用户注册后异步发送邮件/短信

​ 支付成功后的积分计算

​ 3.流量削峰

​ 场景描述:应对突发流量,保护下游系统不被压垮。

​ 典型案例:

​ 秒杀系统:将瞬间请求转为队列消费。

​ 日志收集:高峰期的日志写入

​ 实现方案:

// 配置队列最大积压量
@Bean
public Queue spikeQueue() {return new Queue("spike.queue", true, false, false, Map.of("x-max-length", 10000)); // 最大1万条消息
}

​ 4.定时/延迟任务

​ 场景描述:替代轮询数据库,实现精准延时控制。

​ 典型案例:

​ 订单30分钟未支付自动取消。

​ 预约提醒通知

// 发送延迟消息(RabbitMQ插件实现)
rabbitTemplate.convertAndSend("delayed.exchange","order.cancel",orderId,message -> {message.getMessageProperties().setDelay(30 * 60 * 1000); // 30分钟return message;});

​ 5.分布式事务最终一致性

​ 场景描述:替代刚性事务,通过消息队列实现柔性事务。

​ 典型案例:跨系统数据一致性(如订单+库存+积分)

​ Saga模式实现:

​ 1.订单服务创建订单(本地事务)

​ 2.发送OrderCreated 事件

​ 3.库存服务扣减库存(若失败则发送补偿事件)

6.RabbitMQ的运行流程

RabbitMQ的运行流程设计多个组件之间的协作,包括生产者、交换机、队列和消费者。

​ 1.建立连接(Connection)

​ 描述:客户端(生产者或消费者)与 RabbitMQ 服务器建立 TCP 连接。

​ 步骤:

​ 客户端通过指定 RabbitMQ 服务器的地址和端口,建立 TCP 连接。

​ 客户端进行身份验证(如用户名和密码)

​ 连接成功后,客户端可以创建信道(Channel)进行后续操作。

​ 2.创建信道(Channel)

​ 描述:在连接的基础上,客户端创建虚拟的信道用于执行具体的操作。

​ 步骤:

​ 客户端在已建立的连接上创建信道。

​ 信道是轻量级的,可以在一个连接上创建多个信道,支持多线程操作。

​ 3.声明交换机(Declare Exchange)

​ 描述:生产者或消费者声明交换机,指定交换机的类型和属性。

​ 步骤:

​ 客户端通过信道声明交换机,指定交换机的名称、类型(如 Direct、Fanout、Topic)和属性。

​ 如果交换机已存在且属性匹配,则直接使用;否则创建新的交换机。

​ 4.声明队列(Declare Queue)

​ 描述:消费者声明队列,指定队列的名称和属性。

​ 步骤:

​ 客户端通过信道声明队列,指定队列的名称和属性(如持久化、排他性、自动删除)。

​ 如果队列已存在且属性匹配,则直接使用;否则创建新的队列。

​ 5.绑定队列到交换机(Bind Queue to Exchange)

​ 描述:消费者将队列绑定到交换机,并指定绑定规则(如路由键)

​ 步骤:

​ 客户端通过信道将队列绑定到交换机,指定绑定规则(如路由键或模式匹配)。

​ 绑定后,交换机根据规则将消息路由到队列。

​ 6.发送消息(Publish Message)

​ 描述:生产者将消息发送到交换机

​ 步骤:

​ 生产者通过信道将消息发送到指定的交换机。

​ 消息包含消息体和属性(如路由键、持久化标志)。

​ 交换机根据路由规则将消息路由到一个或多个队列。

​ 7.接收消息(Consume Message)

​ 描述:消费者从队列种获取消息并进行处理。

​ 步骤:

​ 消费者通过信道订阅队列,开始接收消息。

​ RabbitMQ 将队列中的消息推送给消费者。

​ 消费者处理消息内容,并根据处理结果发送确认(ACK)或拒绝(NACK)。

​ 8.确认消息(Acknowledage Message)

​ 描述:消费者处理完消息后,向RabbitMQ发送确认,表示消息已成功处理。

​ 步骤:

​ 消费者处理完消息后,通过信道发送确认(ACK)给 RabbitMQ。

​ RabbitMQ 收到确认后,将消息从队列中移除。

​ 如果消费者发送拒绝(NACK)或未发送确认,RabbitMQ 可能会将消息重新入队或丢弃。

​ 9.关闭信道和连接(Close Channel and Connection

​ 描述:客户端在完成操作后,关闭信道和连接。

​ 步骤:

​ 客户端关闭信道,释放资源。

​ 客户端关闭连接,断开与 RabbitMQ 服务器的 TCP 连接。

7.RabbitMQ的消息可靠性如何保证?

RabbitMQ 通过多层次的机制确保消息可靠性传递。

1.生产者确认机制(Publisher Confirm)

​ 作用:确保消息从生产者到达 Broker

​ 三种确认模式:

模式触发时机性能可靠性
单条同步确认每发一条等待Broker确认最高
批量同步确认累积多条后统一确认
异步确认通过回调通知

2.消息持久化

​ 注意事项:

​ 进设置消息持久化无效,必须同时持久化队列

​ 持久化会降低性能(约10倍吞吐量下降)

3.消费者手动ACk

操作命令效果
ACKbasicAck确认处理成功,消息从队列删除
NACKbasicNack处理失败,可设置是否重新入队

4.高可用架构

​ (1) 镜像队列(Mirrored Queues)

​ 特性:

​ 队列数据跨节点复制,主节点故障自动切换,需配合集群使用。

​ (2) 集群部署

​ 数据分布:元数据全节点同步,队列数据仅存于创建节点(除非配置镜像队列)。

5.死信队列(DLX)

​ 触发条件:

​ 消息被消费者 NACK 且不重新入队。

​ 消息 TTL 过期。

​ 队列到达最大长度。

8.RabbitMQ的消息幂等性如何保证?

在分布式系统中,消息重复消费是常见问题,RabbitMQ 本身不提供内置的幂等性保障,需要通过业务逻辑或技术手段实现。

1.幂等性核心原则

​ 定义:同一消息被消费多次与消费一次的效果相同

​ 常见需幂等场景:订单支付处理,库存扣减,账户余额变更,数据同步操作。

2.消息唯一标识(Message ID)

​ 描述:为每条消息分配一个唯一标识(如 UUID),消费者在处理消息时记录已处理的消息 ID,避免重复处理。

​ 实现:

​ 生产者为每条消息设置唯一 ID。

​ 消费者在处理消息时检查消息 ID 是否已经处理过。

3.数据库唯一约束

​ 描述:利用数据库的唯一约束(如唯一索引)来防止重复处理。

​ 实现:

​ 在数据库中为消息 ID 或 业务唯一标识(如订单号)创建唯一索引。

​ 消费者在处理消息时,将消息 ID 或业务唯一标识插入数据库。如果插入失败(唯一约束冲突),则说明消息已处理过。

4.乐观锁(Optimistic Locking)

​ 描述:在业务逻辑中使用乐观锁机制,确保同一笔业务操作不会被重复执行。

​ 实现:

​ 在数据库中为业务数据添加版本号字段。

​ 消费者在处理消息时,检查数据的版本号是否匹配。如果版本号不匹配,则说明数据已被更新,忽略当前消息。

5.幂等性设计

​ 描述:从业务逻辑层面设计幂等性操作,确保多次执行同一操作不会产生副作用。

​ 实现:

​ 查询操作:查询操作天然幂等,多次查询不会更改系统作用。

​ 更新操作:设计更新为覆盖式更新,而不是累加式更新。

​ 插入操作:使用唯一约束来避免重复插入。

6.Redis 分布式锁

​ 描述:利用 Redis 的分布式锁机制,确保同一笔业务操作在同一时间只能被一个消费者处理。

​ 实现:消费者在处理消息前,尝试获取 Redis 锁。

7.RabbitMQ 的消费者确认机制

​ 描述:通过 RabbitMQ 的消费者确认机制 (ACK/NACK),确保消息被正确处理后才从队列中移除,避免消息重复投递。

​ 实现:消费者在处理完消息后手动发送 ACK。

9.RabbitMQ如何实现延迟队列?

RabbitMQ 本身没有直接的延迟队列功能,但可以通过其他方法实现延迟消息投递。

1.RabbitMQ 官方插件(推荐)

​ 使用 rabbitmq_delayed_message_exchange 插件

​ 优点:原生支持,可靠性高。 消息直接进入延迟Exchange,无需额外队列。

​ 限制:需安装插件(生产环境需测试兼容性)

2.TTL + 死信队列(传统方案)

​ 实现原理:

​ 1.消息设置 TTL (Time To Live)

​ 2.过期后通过死信 Exchange 路由到目标队列

​ 优点:无需插件,兼容所有 RabbitMQ 版本。

​ 缺点:消息堆积问题,队列头部消息会阻塞后续消息过期。不精确延迟,只有队列头部消息的TTL会被检查。

3.外部调度 + 定时任务

​ 实现架构:

[数据库] ← 定时任务 → [RabbitMQ]

​ 实现步骤:

​ 消息存入MySQL并记录投递时间,定时任务扫描到期消息,投递到RabbitMQ实际队列。

​ 优点:支持任意延迟时间(天/月级),可结合业务状态灵活控制。

​ 缺点:依赖外部存储,定时任务有处理延迟。

方案延迟精度最大延迟复杂度适用场景
官方插件高(毫秒级)数小时秒级/分钟级延迟
TTL+死信队列低(队列阻塞)数天简单延迟需求
外部调度依赖扫描间隔无限制长延迟(小时/天级)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词