摘要
文章主要介绍了 Swagger 作为 API 文档生成和测试工具的功能,包括自动生成 API 文档、提供可视化调试界面、促进前后端协作、支持 OpenAPI 规范等。同时,还提及了 Spring Boot 与 Swagger3 的实战应用,以及 Spring 开发中其他相关技术内容,如 @Resource 与 @Autowired 的区别、Druid 监控配置、切面日志示例等。

1. Swagger-API文档生成和测试工具
Swagger 是一种 API 文档生成和测试工具,用于描述、生成、测试和管理 RESTful API。它的主要作用包括以下几个方面:
1.1. 生成 API 文档
Swagger 可以自动扫描代码中的 Controller 和 API 方法,并生成一份可视化的 API 文档,无需手动维护。
示例: 在 Spring Boot 项目中,添加 @ApiOperation 注解:
less
代码解读
复制代码
@ApiOperation("获取用户信息") @GetMapping("/user/{id}") public String getUser(@PathVariable Long id) { return "用户 ID:" + id; }
1.2. 提供可视化的 API 调试界面
Swagger 自带交互式 UI,允许开发者直接在浏览器中进行 API 测试,而不需要使用 Postman、cURL 等工具。
访问 Swagger UI:
bash
代码解读
复制代码
http://localhost:8080/swagger-ui/
你可以:
- 选择 API
- 输入参数
- 直接点击 "Try it out" 测试 API
- 查看 API 的 请求/响应数据
1.3. 让前后端开发协作更高效
- 后端开发 可以专注于编写 API 代码,Swagger 自动生成 API 文档。
- 前端开发 可以直接查看 Swagger 文档,了解 API 的调用方式,而不需要等待后端手写文档。
- 测试人员 也可以使用 Swagger UI 进行 API 测试,提高测试效率。
1.4. 支持 OpenAPI 规范,便于 API 管理
Swagger 基于 OpenAPI 规范(OAS,OpenAPI Specification),可以生成标准的 API 描述文件(JSON 或 YAML 格式)。这些文件可以用于:
- 生成客户端 SDK(如 Java、Python、JavaScript)
- 生成 API 服务器代码
- 自动化 API 测试
示例: 访问 http://localhost:8080/v3/api-docs,Swagger 会返回 JSON 格式的 API 描述:
json
代码解读
复制代码
{ "openapi": "3.0.1", "info": { "title": "API 文档", "version": "1.0" }, "paths": { "/user/{id}": { "get": { "summary": "获取用户信息", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "成功" } } } } } }
这个 JSON 文档可以用于自动生成 API 客户端代码。
1.5. 提供 API 版本管理
Swagger 支持 API 版本控制,可以在 API 文档中同时管理多个版本,例如:
less
代码解读
复制代码
@Api(tags = "用户管理 API V1") @RequestMapping("/api/v1") public class UserControllerV1 { } @Api(tags = "用户管理 API V2") @RequestMapping("/api/v2") public class UserControllerV2 { }
这样,前端可以根据不同版本调用相应的 API。
1.6. 支持多种编程语言
Swagger 不仅支持 Java,还支持多种编程语言,包括:
- Python(FastAPI, Flask)
- Node.js(Express)
- Go
- .NET(C#)
- PHP
这使得 Swagger 成为跨语言的 API 文档标准。
1.7. Swagger总结
| 作用 | 详细描述 |
|---|---|
| 自动生成 API 文档 | 通过注解解析 API,并生成详细的文档 |
| 提供 UI 界面 | 让开发者可以直接在网页上查看和测试 API |
| 提高前后端协作效率 | 无需手写 API 文档,前端可直接查看接口说明 |
| 支持 OpenAPI 规范 | 生成标准的 JSON/YAML API 文档,可用于 SDK 生成 |
| 支持 API 版本管理 | 可以管理多个 API 版本 |
| 兼容多种编程语言 | 适用于 Java、Python、Node.js、Go、C# 等 |
Swagger 让 API 开发更直观、高效、易维护,是现代微服务开发的重要工具。 🚀
2. SpringBoot + Swagger3实战
下面是一个完整的 Spring Boot + Swagger3(Springfox 3.0.0) 示例,适用于 Spring Boot 2.6+ 版本,并解决了常见的兼容性问题。
2.1. 引入 Swagger 依赖
在 pom.xml 文件中添加以下依赖:
xml
代码解读
复制代码
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency>
Spring Boot 2.6+ 之后,Springfox 和 PathPatternParser 可能不兼容,后面会介绍如何解决。
2.2. 配置 Swagger
创建 SwaggerConfig.java 配置类:
java
代码解读
复制代码
package com.example.swagger.config; import io.swagger.annotations.ApiOperation; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.oas.annotations.EnableOpenApi; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; @Configuration @EnableOpenApi public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.OAS_30) // 使用 OpenAPI 3.0 .apiInfo(apiInfo()) // 设置 API 文档的基本信息 .select() .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 仅扫描 @ApiOperation 注解的方法 .paths(PathSelectors.any()) // 允许所有路径 .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Spring Boot + Swagger3 API 文档") .description("示例项目 API 文档") .version("1.0") .build(); } /** * 解决 Spring Boot 2.6 以上版本和 Springfox 兼容性问题 */ @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings( List<T> mappings) { List<T> copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List<RequestMappingInfoHandlerMapping>) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } }
2.3. 创建 Controller 并使用 Swagger 注解
创建 UserController.java:
kotlin
代码解读
复制代码
package com.example.swagger.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; @Api(tags = "用户管理") @RestController @RequestMapping("/users") public class UserController { @ApiOperation("获取用户信息") @GetMapping("/{id}") public String getUser(@PathVariable("id") Long id) { return "用户ID:" + id; } @ApiOperation("创建用户") @PostMapping("/") public String createUser(@RequestParam String name) { return "用户 " + name + " 创建成功"; } @ApiOperation("删除用户") @DeleteMapping("/{id}") public String deleteUser(@PathVariable("id") Long id) { return "用户 " + id + " 已删除"; } }
2.4. 启动项目并访问 Swagger UI
- 启动 Spring Boot 项目后,访问:
bash
代码解读
复制代码
http://localhost:8080/swagger-ui/
如果 swagger-ui/ 访问不到,尝试:
bash
代码解读
复制代码
http://localhost:8080/swagger-ui/index.html
2. 访问 OpenAPI JSON 文档
bash
代码解读
复制代码
http://localhost:8080/v3/api-docs
2.5. Swagger主要注解
| 注解 | 作用 |
|---|---|
@Api(tags = "说明") | 给 Controller 加标签(分组) |
@ApiOperation("说明") | 给 API 方法添加描述 |
@ApiParam("参数说明") | 说明请求参数 |
@ApiModel("实体说明") | 说明实体类 |
@ApiModelProperty("属性说明") | 说明实体类字段 |
2.6. Swagger可能遇到的问题
2.6.1. Swagger 页面打不开
- 访问
http://localhost:8080/swagger-ui/时,页面 404?
-
- 可能路径变了,尝试
http://localhost:8080/swagger-ui/index.html
- 可能路径变了,尝试
2.6.2. 启动时报 NoSuchFieldException: handlerMappings
- 这个错误是 Spring Boot 2.6+ 版本与 Springfox 兼容性问题,已经在
SwaggerConfig.java里用BeanPostProcessor解决。
2.6.3. API 没有出现在 Swagger 页面
- 确保 Controller 里的方法 有
@ApiOperation注解,否则不会显示。
3. @Resource 与 @Autowired 区别
注解来源:
- @Resource 是 Java 标准注解,属于 JSR-250 规范的一部分,位于 javax.annotation 包中。
- @Autowired 是 Spring 框架提供的注解,位于 org.springframework.beans.factory.annotation 包中。
默认行为:
- @Resource 默认按名称(byName)注入,如果没有找到匹配的名称,则按类型(byType)注入。
- @Autowired 默认按类型(byType)注入,如果需要按名称注入,可以配合 @Qualifier 注解使用。
使用场景:
- @Resource 更适合在 Java EE 环境中使用,因为它不依赖于 Spring 框架。
- @Autowired 更适合在 Spring 框架中使用,提供了更丰富的功能,如自动装配集合类型的属性。
4. Springboot+Druid 监控配置
Druid 是一个开源的高性能数据库连接池,它提供了内置的监控功能,可以帮助开发者监控数据库连接池的状态、执行的 SQL、慢 SQL 等信息。下面是如何在 Spring 项目中配置和使用 Druid 监控的详细步骤。
4.1. 引入 Druid 依赖
首先,确保你已经在 pom.xml 文件中引入了 Druid 的相关依赖:
xml
代码解读
复制代码
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency>
请根据项目的需求,选择适合的版本。
4.2. 配置 Druid 数据源
在 Spring Boot 项目的 application.yml 或 application.properties 文件中,配置 Druid 数据源。
4.2.1. application.yml 示例配置:
yaml
代码解读
复制代码
spring: datasource: url: jdbc:mysql://localhost:3306/your_database username: your_username password: your_password driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 5 # 初始化连接数 min-idle: 5 # 最小空闲连接数 max-active: 20 # 最大连接数 max-wait: 60000 # 获取连接的最大等待时间 filters: stat,wall # 开启 SQL 监控和防火墙 # 配置监控页面 stat-view-servlet: enabled: true # 启用监控 login-username: admin # 设置访问监控页面的用户名 login-password: admin # 设置密码 reset-enable: false # 禁止重置 web-stat-filter: enabled: true # 启用 Web 统计 exclusions: /druid/*,*.ico,/error # 排除不需要监控的路径
4.2.2. application.properties 示例配置:
ini
代码解读
复制代码
spring.datasource.url=jdbc:mysql://localhost:3306/your_database spring.datasource.username=your_username spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.initial-size=5 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-active=20 spring.datasource.druid.max-wait=60000 spring.datasource.druid.filters=stat,wall spring.datasource.druid.stat-view-servlet.enabled=true spring.datasource.druid.stat-view-servlet.login-username=admin spring.datasource.druid.stat-view-servlet.login-password=admin spring.datasource.druid.stat-view-servlet.reset-enable=false spring.datasource.druid.web-stat-filter.enabled=true spring.datasource.druid.web-stat-filter.exclusions=/druid/*,*.ico,/error
4.3. 配置 Druid 监控 Servlet 和 Web Stat Filter
你还需要手动配置 Druid 的 StatViewServlet 和 WebStatFilter 来启用 Web 监控界面和统计功能。
4.3.1. 配置 StatViewServlet
typescript
代码解读
复制代码
@Bean public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() { ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); // 配置登录用户名和密码 bean.addInitParameter("loginUsername", "admin"); bean.addInitParameter("loginPassword", "admin"); // 禁用重置功能 bean.addInitParameter("resetEnable", "false"); return bean; }
4.3.2. 配置 WebStatFilter
typescript
代码解读
复制代码
@Bean public FilterRegistrationBean<WebStatFilter> druidWebStatFilter() { FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new WebStatFilter()); // 配置 URL 过滤规则 filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.ico,/error"); // 排除的 URL return filterRegistrationBean; }
4.4. 访问 Druid 监控页面
一旦完成了上述配置,你可以通过访问 http://localhost:8080/druid 来查看 Druid 的监控界面。
监控内容:
- 连接池状态:显示当前连接池的基本信息,如最大连接数、活跃连接数、空闲连接数等。
- SQL 统计:展示最近执行的 SQL 语句,包括执行次数、执行时间、影响行数等。
- 慢 SQL 统计:展示执行时间超过指定阈值的 SQL。
4.5. 配置慢 SQL 日志
Druid 支持记录慢 SQL,帮助开发人员优化性能。在 application.yml 或 application.properties 中,你可以配置慢 SQL 的阈值。
yaml
代码解读
复制代码
spring: datasource: druid: filters: stat,wall,log4j # 启用慢 SQL 记录 # 配置慢 SQL 阈值 log-slow-sql: true # 启用慢 SQL 日志 slow-sql-millis: 5000 # 记录超过 5 秒的慢 SQL
4.6. 配置 SQL 防火墙(WallFilter)
Druid 提供了 SQL 防火墙(WallFilter),可以防止恶意的 SQL 注入攻击。你可以在 filters 中启用 wall。
yaml
代码解读
复制代码
spring: datasource: druid: filters: stat,wall
WallFilter 是 Druid 的一个过滤器,用于检测潜在的 SQL 注入和不安全的 SQL 语句。你可以通过配置来进行 SQL 校验。
4.7. 配置 Druid 监控 API
Druid 还提供了一个监控 API,用于查看连接池的状态等信息。你可以通过访问以下 URL 来获取 Druid 的监控数据:
- 连接池监控 API:
/druid/dataSource - SQL 执行监控 API:
/druid/sql - 慢 SQL 监控 API:
/druid/slowSql
这些 API 提供了可以集成到第三方监控平台(如 Prometheus 或 Grafana)的接口。
4.8. 配置文件完整示例:
4.8.1. application.yml 示例配置:
yaml
代码解读
复制代码
spring: datasource: url: jdbc:mysql://localhost:3306/your_database username: your_username password: your_password driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 filters: stat,wall,log4j # 配置监控页面 stat-view-servlet: enabled: true login-username: admin login-password: admin reset-enable: false web-stat-filter: enabled: true exclusions: /druid/*,*.ico,/error log-slow-sql: true slow-sql-millis: 5000 # 慢 SQL 阈值:超过 5 秒的 SQL 会被记录
4.8.2. DruidConfig.java 配置类:
typescript
代码解读
复制代码
@Configuration public class DruidConfig { @Bean public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() { ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); bean.addInitParameter("loginUsername", "admin"); bean.addInitParameter("loginPassword", "admin"); bean.addInitParameter("resetEnable", "false"); return bean; } @Bean public FilterRegistrationBean<WebStatFilter> druidWebStatFilter() { FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.ico,/error"); return filterRegistrationBean; } }
通过以上配置,你可以在 Spring Boot 项目中轻松集成 Druid 数据库连接池,并启用 Druid 的监控功能,查看 SQL 执行情况、慢 SQL、连接池的状态等。配置了监控页面后,可以通过浏览器访问 http://localhost:8080/druid 查看实时的数据库连接池信息,同时配合日志系统记录慢 SQL,帮助开发者优化数据库性能。
4.9. webConfig配置完成示例
java
代码解读
复制代码
/** * Created by libinsong on 2017/4/19. */ @EnableScheduling。// 表示开启spring中的定时任务功能。 @Configuration public class WebConfig implements WebMvcConfigurer { @Value("${filter.slow.reqmillis:3000}") private String slowReqMillis; @Autowired private DispatcherServlet dispatcherServlet; /** * 去掉JSON返回的Null属性,配置HTTP消息转换器,去掉JSON返回的Null属性并设置UTF-8编码。 * * @return */ @Bean public HttpMessageConverters customConverters() { MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); //设置日期格式 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper); //设置中文编码格式 List<MediaType> list = new ArrayList<>(); list.add(MediaType.APPLICATION_JSON_UTF8); list.add(MediaType.ALL); mappingJackson2HttpMessageConverter.setSupportedMediaTypes(list); StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8); stringHttpMessageConverter.setWriteAcceptCharset(false); return new HttpMessageConverters(false, Arrays.asList(stringHttpMessageConverter, mappingJackson2HttpMessageConverter)); } /** * {@link StatViewServlet} * 注册Druid监控Servlet和过滤器,用于数据库连接池监控。 * @return */ @Bean public ServletRegistrationBean druidServlet() { ServletRegistrationBean reg = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); reg.addInitParameter("resetEnable", "false"); reg.addInitParameter("loginUsername", "kraken"); reg.addInitParameter("loginPassword", "krakenAdmin"); return reg; } /** * {@link WebStatFilter} * * @return */ @Bean public FilterRegistrationBean druidWebStatFilterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.ico,/error"); return filterRegistrationBean; } /** * {@link LogFilter} * 注册日志过滤器,记录慢请求日志。 * @return */ @Bean public FilterRegistrationBean logFilterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new LogFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.ico,/error"); filterRegistrationBean.addInitParameter("slowReqMillis", slowReqMillis); return filterRegistrationBean; } @Bean public HandlerInterceptor getSystemInterceptor() { return new SystemInterceptor(); } /** * {@link addInterceptors} * 注册系统拦截器,拦截特定路径的请求 * @return */ @Override public void addInterceptors(InterceptorRegistry registry) { String[] patterns = new String[]{"/ok", "/ok.htm", "/actuator/**", "/user/getAuthCode", "/user/auth", "/user/auth/janus", "/user/checkToken", "/user/authCode", "/regedit/menu", "/solution", "/solution/updateSolutionName", "/license/**", "/auth/**", "/openAPI/**", "/other/auth/**", "/solution/import", "/gitInfo", "/swagger-ui.html", "/doc.html", "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/version", "/version/compatible/**"}; registry.addInterceptor(getSystemInterceptor()).addPathPatterns("/**").excludePathPatterns(patterns); } /** * {@link addInterceptors} * 配置Swagger资源映射和静态资源处理。 * @return */ @Bean public ServletRegistrationBean<Servlet> swaggerServlet() { ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(dispatcherServlet); bean.addUrlMappings("/swagger-resources", "/swagger-resources/configuration/ui", "/v2/api-docs"); bean.setName("swaggerServlet"); return bean; } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } }
5. SpringBoot项目中Controller层和Dao层切面日志示例
在 Spring Boot 项目中,可以使用 AOP(面向切面编程)来为 Controller 层和 DAO 层添加日志记录功能。通过 AOP,我们可以在方法调用前后自动记录日志,而不需要在每个方法中手动编写日志代码。
5.1. 实战步骤
- 添加 AOP 依赖(如果没有添加) Spring Boot 默认已经包含了 AOP 相关的依赖,但如果没有的话,可以在
pom.xml中添加以下依赖:
xml
代码解读
复制代码
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
- 创建日志切面类 在该类中,我们可以定义切面(Aspect)来记录日志。我们会为 Controller 层和 DAO 层的方法添加切面。
5.2. Controller 层和 DAO 层的日志记录示例
5.2.1. 日志切面(Aspect)配置
首先,我们需要定义一个日志切面类,这个类将定义切点(即要执行切面的地方)和通知(切面触发时执行的操作)。
java
代码解读
复制代码
package com.example.demo.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); // 定义一个切点:用于匹配所有Controller包下的方法 @Pointcut("execution(* com.example.demo.controller..*(..))") public void controllerLayer() {} // 定义一个切点:用于匹配所有Service包下的方法 @Pointcut("execution(* com.example.demo.service..*(..))") public void serviceLayer() {} // 定义一个切点:用于匹配所有DAO包下的方法 @Pointcut("execution(* com.example.demo.dao..*(..))") public void daoLayer() {} // Controller 层前置通知 @Before("controllerLayer()") public void logControllerMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); logger.info("Entering Controller method: {}", methodName); } // Controller 层后置通知 @After("controllerLayer()") public void logControllerMethodEnd(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); logger.info("Exiting Controller method: {}", methodName); } // Service 层前置通知 @Before("serviceLayer()") public void logServiceMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); logger.info("Entering Service method: {}", methodName); } // Service 层后置通知 @After("serviceLayer()") public void logServiceMethodEnd(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); logger.info("Exiting Service method: {}", methodName); } // DAO 层前置通知 @Before("daoLayer()") public void logDaoMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); logger.info("Entering DAO method: {}", methodName); } // DAO 层后置通知 @After("daoLayer()") public void logDaoMethodEnd(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); logger.info("Exiting DAO method: {}", methodName); } }
5.2.2. 解析切面代码
@Pointcut:定义切点,用于匹配指定的类和方法。
-
execution(* com.example.demo.controller..*(..)):匹配controller包下所有类的方法。execution(* com.example.demo.dao..*(..)):匹配dao包下所有类的方法。
@Before:在目标方法执行之前执行,记录进入 Controller 或 DAO 层的方法信息。@After:在目标方法执行之后执行,记录退出 Controller 或 DAO 层的方法信息。JoinPoint:提供了对目标方法签名、参数等信息的访问。
5.2.3. 配置日志(如果没有)
为了查看日志输出,确保你在 application.properties 或 application.yml 中配置了日志级别。
ini
代码解读
复制代码
# application.properties logging.level.com.example.demo.aspect=INFO
或者:
yaml
代码解读
复制代码
# application.yml logging: level: com.example.demo.aspect: INFO
5.2.4. 测试 Controller 层和 DAO 层
假设我们有以下的 Controller 和 DAO 层:
Controller 示例:
kotlin
代码解读
复制代码
package com.example.demo.controller; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/getUser") public String getUserInfo() { return userService.getUserInfo(); } }
DAO 示例:
kotlin
代码解读
复制代码
package com.example.demo.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { public String getUser() { // Simulating DB interaction return "User Data from Database"; } }
Service 示例:
typescript
代码解读
复制代码
package com.example.demo.service; import com.example.demo.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserDao userDao; // 查询用户信息 public String getUserInfo() { return userDao.getUser(); } // 创建用户信息 public String createUser(String username) { // 假设我们会在这里做一些业务处理 return "Created user: " + username; } }
执行和查看日志
当你访问 GET /getUser 接口时,控制台日志将显示如下信息:
sql
代码解读
复制代码
Entering Controller method: getUserInfo Entering DAO method: getUser Exiting DAO method: getUser Exiting Controller method: getUserInfo
5.3. Aspect切面日志总结
- Controller 层日志:通过
@Pointcut("execution(* com.example.demo.controller..*(..))")配置切点,结合@Before和@After注解记录进入和退出的方法信息。 - DAO 层日志:类似地,使用
@Pointcut("execution(* com.example.demo.dao..*(..))")配置切点来记录数据库操作方法的日志。
通过 AOP 技术,你可以在不改变原有业务逻辑的情况下,灵活地添加日志记录,便于调试、监控和审计。
6. SpringBoot项目中RPC远程调用监控日志记录
java
代码解读
复制代码
/** * @Author maweijie * @Date 2020/7/22 3:13 PM * @Version 1.0 */ @Activate(group = Constants.PROVIDER) public class DubboFilter implements Filter { private static Logger logger = LoggerFactory.getLogger(DubboFilter.class); @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { long start = System.currentTimeMillis(); MeterRegistryUtil meterRegistryUtil = SpringContextUtil.getBean("meterRegistryUtil"); Timer.Sample sample = meterRegistryUtil.timerStart(); Result result = invoker.invoke(invocation); long cost = System.currentTimeMillis() - start; try { String interfaceName = invoker.getInterface().getSimpleName(); String method = invocation.getMethodName(); logger.info("interfaceName : {} method : {}, cost :{}", interfaceName, method, cost); if (result != null) { meterRegistryUtil.timerStop(sample, "bifrost_dubbo_provider", "method", method, "interface", interfaceName, "success", String.valueOf(!result.hasException()), "code", String.valueOf(cn.tongdun.bifrost.biz.service.rpc.result.Result.SUCCESS)); } else { meterRegistryUtil.timerStop(sample, "bifrost_dubbo_provider", "method", method, "interface", interfaceName, "success", "false", "code", "-1"); } } catch (Exception e) { logger.error("dubbo Service monitoring exception", e); } return result; } }
这个 DubboFilter 类是一个 Dubbo Filter,它的作用是在 Dubbo 服务调用过程中,拦截并对请求进行额外的处理。它通常用于记录日志、统计调用次数、处理监控、性能分析等。
6.1. 何时起作用?
这个类的起作用时间是在 Dubbo 服务提供者 被调用时。当一个客户端请求通过 Dubbo 进行调用时,DubboFilter 会作为请求的拦截器被触发,执行它定义的逻辑。
具体来说,它会在以下时机起作用:
- 当服务提供者 接收到一个来自 消费者 的 Dubbo RPC 请求时,
DubboFilter会首先被触发。 - 它会拦截当前的 RPC 调用,记录一些信息,比如调用的方法、接口名、执行时间等。
- 然后它会调用
invoker.invoke(invocation)继续执行后续的调用(即真正的服务处理)。 - 最后,它会在调用结束后,记录调用的时间、结果等监控信息。
6.2. @Activate 注解
ini
代码解读
复制代码
@Activate(group = Constants.PROVIDER)
这个注解标记了该 DubboFilter 的作用范围。具体解释:
group = Constants.PROVIDER 表明该 DubboFilter 只会在 Dubbo 服务提供者(Provider)端生效。@Activate 注解的作用是用来激活该过滤器,在某个特定的环境中自动启用。在这里,Constants.PROVIDER 表示过滤器只会在 服务端 启动并生效,而不会影响到消费者端。
6.3. Filter 的工作原理
Dubbo 的 Filter 是一种类似于拦截器的机制,它可以在服务调用的不同阶段进行切入。invoke 方法就是 Filter 在处理请求时被调用的核心方法:
- 开始时间:记录开始时间,表示请求的开始。
- 调用目标服务:通过
invoker.invoke(invocation)执行目标服务的调用。 - 请求结束时间:记录请求的结束时间,计算服务调用的时间耗时。
- 监控:利用
MeterRegistryUtil进行监控数据的采集,记录调用成功与否、耗时等数据。通过timerStart()开始计时,timerStop()停止计时并将监控数据发送到指标系统中。
6.4. Filter 的生命周期
在 Dubbo 中,Filter 是通过 Dubbo 框架自动管理的,具体生命周期包括:
- 注册阶段:
DubboFilter会在服务启动时被注册到 Dubbo 框架中。 - 调用拦截:每次 Dubbo 服务接收到请求时,都会经过
DubboFilter进行处理,按照过滤器的链式调用顺序执行。 - 过滤器顺序:如果有多个过滤器,
DubboFilter会按顺序执行,执行完毕后,invoker.invoke(invocation)会调用实际的服务方法。 - 结果处理:在服务方法调用结束后,
DubboFilter会对结果进行处理,如记录日志、采集监控数据等。
6.5. 示例执行流程
假设有一个 Dubbo 服务提供者,客户端调用服务时:
- 客户端请求:消费者向服务提供者发送 RPC 请求。
- DubboFilter 起作用:
-
DubboFilter拦截到请求。- 记录请求的开始时间。
- 执行
invoker.invoke(invocation),即继续执行服务逻辑。
- 服务方法执行:
invoker.invoke()执行实际的服务方法。 - 执行结束:
DubboFilter在服务方法执行完成后,记录调用的时间、方法名、接口名等信息,并通过MeterRegistryUtil发送监控数据。 - 返回结果:最后,
DubboFilter返回结果给消费者。
6.6. 关键功能
- 日志记录:
logger.info记录了调用的接口名、方法名和耗时信息。 - 性能监控:使用
MeterRegistryUtil采集服务调用的相关指标,如成功与否、耗时等信息,发送到监控系统。 - 异常处理:在捕获异常时,
DubboFilter会记录异常信息,确保不会导致服务中断。
6.7. RPC远程调用监控日志总结
DubboFilter 类的作用是作为 Dubbo 服务提供者端的过滤器,用于:
- 监控服务调用的性能(如执行时间、成功与否等)。
- 记录调用日志。
- 在请求的生命周期中进行切入,处理一些公共的逻辑(如监控、日志等)。
通过 @Activate(group = Constants.PROVIDER) 注解,它只会在 服务提供者端 被触发和执行,而不会影响到消费者端。
7. @RestControllerAdvice注解
@RestControllerAdvice 是 Spring 5 引入的一个注解,它结合了 @ControllerAdvice 和 @ResponseBody 的功能。它用于处理全局异常、全局数据绑定和全局模型属性等,但与 @ControllerAdvice 不同的是,@RestControllerAdvice 自动将返回值序列化为 JSON 格式,适用于 RESTful 风格的 API。
7.1. @RestControllerAdvice 的作用:
- 全局异常处理:统一处理应用中的异常并返回统一的错误响应。
- 全局数据绑定:可以在所有的 Controller 方法中共享一些公共的数据。
- 全局响应体处理:返回的对象会自动进行 JSON 序列化处理,返回给客户端。
@RestControllerAdvice 结合了 @ControllerAdvice(提供全局配置功能)和 @ResponseBody(将返回的对象转换为 JSON 或 XML)注解,因此它可以简化 RESTful 风格应用中的异常处理、数据绑定和响应体处理。
7.2. @RestControllerAdvice示例:
7.2.1. 全局异常处理
假设你的项目中可能会抛出一些常见的异常,比如业务异常(BusinessException)或者系统异常,你可以通过 @RestControllerAdvice 来进行全局的异常处理。
typescript
代码解读
复制代码
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { // 处理业务异常 @ExceptionHandler(BusinessException.class) public ResponseEntity<String> handleBusinessException(BusinessException e) { return new ResponseEntity<>("Business error: " + e.getMessage(), HttpStatus.BAD_REQUEST); } // 处理通用异常 @ExceptionHandler(Exception.class) public ResponseEntity<String> handleException(Exception e) { return new ResponseEntity<>("Internal server error: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } }
@ExceptionHandler:指定处理哪些类型的异常。- 返回类型
ResponseEntity<String>:用于构建返回的 HTTP 响应。
7.2.2. 统一响应封装
你可以在 @RestControllerAdvice 中处理统一的响应结构,使得所有的返回结果都采用统一格式。例如,所有的成功响应都采用 data 字段返回,所有的错误响应都采用 message 字段返回。
typescript
代码解读
复制代码
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { // 统一的错误响应格式 @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleException(Exception e) { ErrorResponse errorResponse = new ErrorResponse("Internal Server Error", e.getMessage()); return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } // 自定义的错误响应类 public static class ErrorResponse { private String error; private String message; public ErrorResponse(String error, String message) { this.error = error; this.message = message; } // Getters and Setters public String getError() { return error; } public void setError(String error) { this.error = error; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } }
这样,当应用中发生异常时,返回给客户端的错误信息会包含在 error 和 message 字段中,保持了接口返回的统一性。
7.2.3. 全局模型属性
你还可以通过 @RestControllerAdvice 来定义全局的模型属性,使得所有的 Controller 方法都能共享一些公共数据。
kotlin
代码解读
复制代码
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalModelAttribute { // 定义全局模型属性 @ModelAttribute("globalInfo") public String addGlobalInfo() { return "This is global info!"; } }
在所有的 Controller 中,你都可以通过 @ModelAttribute 获取 globalInfo 的值。
7.2.4. 全局数据绑定
你还可以为所有的 Controller 方法提供一些全局的数据绑定配置,例如设置格式化器、拦截器等。
kotlin
代码解读
复制代码
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalDataBinder { // 设置全局数据绑定,类似于在 @InitBinder 中的配置 @InitBinder public void initBinder(WebDataBinder binder) { // 自定义数据绑定器配置 binder.setDisallowedFields("password"); } }
7.2.5. @RestControllerAdvice 结合 @ResponseBody 和 @ControllerAdvice 的优势:
- 简化代码:
@RestControllerAdvice自动为每个方法的返回值添加了@ResponseBody,你不需要再额外配置 JSON 序列化。 - 集中管理:通过
@RestControllerAdvice可以集中管理应用的异常处理、模型属性、数据绑定等。 - 统一响应格式:可以在
@RestControllerAdvice中对返回的结果进行统一封装,如统一错误响应格式或成功响应格式。
7.2.6. @RestControllerAdvice总结
@RestControllerAdvice是 Spring 5 引入的用于增强@ControllerAdvice的功能,自动将响应对象转换为 JSON 格式,适用于 RESTful API 项目。- 它提供了全局的异常处理、模型属性共享、数据绑定等功能,可以用于简化开发过程,提升代码的可维护性。
8. Spring中错误处方案
8.1. GlobalErrorController
typescript
代码解读
复制代码
@Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class GlobalErrorController implements ErrorController { private static final Logger logger = LoggerFactory.getLogger(GlobalErrorController.class); private final ErrorAttributes errorAttributes; private final ErrorProperties errorProperties; private final ServerProperties serverProperties; public GlobalErrorController(ServerProperties serverProperties, ErrorAttributes errorAttributes) { this.serverProperties = serverProperties; this.errorAttributes = errorAttributes; this.errorProperties = serverProperties.getError(); } @RequestMapping @ResponseBody public Response<String> error(HttpServletRequest request, HttpServletResponse response) { response.reset(); // 设置状态码 response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setHeader("Cache-Control", "no-cache"); Response<String> res = new Response<String>(); res.setCode(StatusCodeEnum.SYSTEM_ERROR.getStatus()); res.setMessage(StatusCodeEnum.SYSTEM_ERROR.getMsg()); Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); logger.error("Request {}Exception Information{}", request.getRequestURL(), JSON.toJSONString(body)); return res; } protected Map<String, Object> getErrorAttributes(HttpServletRequest request, ErrorAttributeOptions includeStackTrace) { ServletWebRequest servletWebRequest = new ServletWebRequest(request); return this.errorAttributes.getErrorAttributes(servletWebRequest, includeStackTrace); } protected boolean getTraceParameter(HttpServletRequest request) { String parameter = request.getParameter("trace"); if (parameter == null) { return false; } return !"false".equalsIgnoreCase(parameter); } protected ErrorAttributeOptions isIncludeStackTrace(HttpServletRequest request, MediaType produces) { Set<ErrorAttributeOptions.Include> setInclude = new HashSet<>(); ErrorProperties error = this.serverProperties.getError(); if (error.getIncludeStacktrace() == ErrorProperties.IncludeAttribute.ALWAYS) { setInclude.add(ErrorAttributeOptions.Include.STACK_TRACE); } if(error.getIncludeMessage() == ErrorProperties.IncludeAttribute.ALWAYS){ setInclude.add(ErrorAttributeOptions.Include.MESSAGE); } if(error.isIncludeException()){ setInclude.add(ErrorAttributeOptions.Include.EXCEPTION); } if(error.getIncludeBindingErrors() == ErrorProperties.IncludeAttribute.ALWAYS){ setInclude.add(ErrorAttributeOptions.Include.BINDING_ERRORS); } return ErrorAttributeOptions.of(setInclude); } protected ErrorProperties getErrorProperties() { return this.errorProperties; } }
8.1.1. GlobalErrorController:
- 用途:
GlobalErrorController主要用于处理 HTTP 错误页面(如 404、500 错误等),并返回统一的错误响应。它的目的是捕获和处理 Spring Boot 中的系统层错误(例如页面未找到、服务器错误等)。 - 错误响应格式:
GlobalErrorController返回的是一个标准的错误响应,通常包含错误码、错误消息等信息。 - 异常类型:主要捕获应用层的系统错误(如404、500等 HTTP 错误),不特定于业务异常。
- 方法:
-
- 它通过
ErrorAttributes来获取错误详情。 getErrorAttributes方法用于获取错误的详细信息。
- 它通过
8.1.2. GlobalExceptionHandler:
- 用途:
GlobalExceptionHandler主要用于处理应用层(业务层)以及一些常见的请求错误(如参数校验错误、上传文件错误等)。它的作用是在应用层捕获和处理特定的业务异常、上传文件异常、参数错误等。 - 错误响应格式:同样使用
Response封装错误信息,但它更侧重于捕获和处理业务相关的异常,并返回详细的错误信息(如错误码、业务消息等)。 - 异常类型:它针对各种业务异常(如
BizException、ServiceException)以及文件上传、请求参数校验等进行处理。 - 方法:
-
- 它有针对性地处理不同类型的业务异常,并返回对应的错误信息。
8.2. GlobalExceptionHandler
scss
代码解读
复制代码
package ********; @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); private static final String LOG_ERROR_FORMAT = "Request {} exception information"; private static final String NO_CACHE = "no-cache"; private static final String CACHE_CONTROL = "Cache-Control"; @Resource private MultipartProperties multipartProperties; @ExceptionHandler(value = Exception.class) @ResponseBody public Response<String> defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) { response.reset(); // 设置状态码 response.setStatus(HttpStatus.OK.value()); response.setHeader(CACHE_CONTROL, NO_CACHE); Response<String> res = new Response<>(); if (ex instanceof BusException) { EnumStatus enumStatus = ((BusException) ex).getStatus(); res.setCode(enumStatus.getStatus()); res.setMessage(enumStatus.getMsg()); logger.warn(LOG_ERROR_FORMAT, request.getRequestURL(), ex); } else if (ex instanceof ServiceException) { response.setStatus(HttpStatus.OK.value()); ServiceException serviceException = ((ServiceException) ex); MSG msg = serviceException.getMsg(); if (msg == null) { res.setCode(-1); res.setSuccess(false); res.setMessage(serviceException.getMessage()); } else { res = Response.error(msg); } logger.warn(LOG_ERROR_FORMAT, request.getRequestURL(), ex); } else if (ex instanceof SystemException) { SystemException systemException = (SystemException) ex; logger.warn(LOG_ERROR_FORMAT, request.getRequestURL(), ex); return Response.error(systemException.getMessage()); } else if (ex instanceof ServletRequestBindingException || ex instanceof IllegalArgumentException) { res.setCode(StatusCodeEnum.PARAM_ERROR.getStatus()); res.setMessage(StatusCodeEnum.PARAM_ERROR.getMsg()); logger.warn(LOG_ERROR_FORMAT, request.getRequestURL(), ex); } else { res.setCode(StatusCodeEnum.UNKNOW_ERROR.getStatus()); res.setMessage(StatusCodeEnum.UNKNOW_ERROR.getMsg()); logger.error(LOG_ERROR_FORMAT, request.getRequestURL(), ex); } return res; } /** * 业务异常捕捉 * * @param e 业务异常类 * @param request * @param response * @return 返回报文 * @author xuluquan * @date 2019-08-01 10:32 */ @ExceptionHandler(BizException.class) @ResponseBody public Response<Object> handleException(BizException e, HttpServletRequest request, HttpServletResponse response) { response.reset(); // 设置状态码 response.setStatus(HttpStatus.OK.value()); response.setHeader(CACHE_CONTROL, NO_CACHE); logger.error("BizException:", e); if (e.getMsg() != null && e.getReplacements() != null) { return Response.error(e.getMsg(), e.getReplacements()); } if (e.getCommonMsg() != null && e.getReplacements() != null) { return Response.error(e.getCommonMsg(), e.getReplacements()); } if (e.getMsg() != null) { return Response.error(e.getMsg()); } if (e.getCommonMsg() != null) { return Response.error(e.getCommonMsg()); } return Response.error(e.getMessage()); } @ExceptionHandler(MaxUploadSizeExceededException.class) public Response<Object> handleUploadException(MaxUploadSizeExceededException e, HttpServletResponse response) { response.reset(); // 设置状态码 response.setStatus(HttpStatus.OK.value()); response.setHeader(CACHE_CONTROL, NO_CACHE); logger.error("MaxUploadSizeExceededException:", e); long singleSize = multipartProperties.getMaxFileSize().toMegabytes(); long reqSize = multipartProperties.getMaxRequestSize().toMegabytes(); return Response.error(MSG.UPLOAD_ERROR_SPRING, singleSize, reqSize); } @ExceptionHandler(cn.fraudmetrix.module.ent.ddd.common.base.BizException.class) @ResponseBody public Response<Object> handleFieldException(cn.fraudmetrix.module.ent.ddd.common.base.BizException e, HttpServletRequest request, HttpServletResponse response) { response.reset(); // 设置状态码 response.setStatus(HttpStatus.OK.value()); response.setHeader(CACHE_CONTROL, NO_CACHE); Response<Object> result = Response.error(e.getMessage()); if (e.getCode() != null) { result.setCode(e.getCode().getCode()); } return result; } @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class}) public Response<Object> validationException(Exception e, HttpServletResponse response) { response.reset(); response.setStatus(HttpStatus.OK.value()); response.setHeader(CACHE_CONTROL, NO_CACHE); BindingResult bindResult = null; if (e instanceof MethodArgumentNotValidException) { bindResult = ((MethodArgumentNotValidException) e).getBindingResult(); } else if (e instanceof BindException) { bindResult = ((BindException) e).getBindingResult(); } Map<String, String> errorMap = new HashMap<>(16); if (bindResult != null) { bindResult.getFieldErrors().forEach((fieldError) -> errorMap.put(fieldError.getField(), fieldError.getDefaultMessage())); } return Response.error(MSG.REQ_ERROR_PARAM_ERR, errorMap); } }
8.2.1. GlobalExceptionHandler 类的作用:
这个类是 全局异常处理器,它的主要作用是处理 Spring Boot 应用中所有未被捕获的异常,并根据不同的异常类型返回适当的错误信息。它使用了 Spring 的 @RestControllerAdvice 注解来进行全局异常捕获和处理。该类能够捕捉不同类型的异常,并返回一致的错误响应格式给客户端。
8.2.2. 详细功能:
- 捕获并处理不同类型的异常:
-
- 业务异常 (
BizException、ServiceException、SystemException):这些异常通常表示应用逻辑中出现的错误。根据不同的业务需求,会返回不同的错误信息。 - 参数校验异常:处理参数错误,如
MethodArgumentNotValidException、BindException,主要用于校验请求参数是否正确。 - 上传文件大小超过限制的异常:如
MaxUploadSizeExceededException,当上传的文件大小超过预设的限制时,返回相应的错误信息。 - 系统异常和未捕获的异常:对于不在上述处理范围内的异常,返回通用的错误信息,并记录错误日志。
- 业务异常 (
- 返回统一的错误响应格式:
-
- 使用
Response对象来包装错误响应信息,返回统一的错误码、错误消息和额外的错误详情(如上传文件大小等)。
- 使用
- 日志记录:
-
- 每次捕获异常时,都会记录错误日志,方便问题的追踪和排查。
- 异常信息自定义:
-
- 针对不同类型的异常,可以自定义错误消息。例如,业务异常可以返回具体的业务错误信息,上传异常可以返回文件上传限制的详细信息。
8.2.3. 异常处理方法:
defaultErrorHandler:
-
- 捕获所有未被专门处理的异常。它会根据异常类型分别进行处理:
-
-
BusException:业务异常,返回业务相关的错误信息。ServiceException:服务层异常,返回服务相关的错误信息。SystemException:系统异常,返回系统错误信息。- 其他异常(如
ServletRequestBindingException和IllegalArgumentException):一般参数错误或非法参数。 - 未知异常:记录并返回通用的错误信息。
-
handleException:
-
- 专门捕获
BizException类型的异常,处理业务相关的异常。根据异常中设置的消息和参数,返回相应的错误信息。
- 专门捕获
handleUploadException:
-
- 专门处理文件上传时发生的异常,如文件大小超过了服务器设置的限制(
MaxUploadSizeExceededException)。
- 专门处理文件上传时发生的异常,如文件大小超过了服务器设置的限制(
handleFieldException:
-
- 处理来自
cn.fraudmetrix模块的BizException,并根据其code字段设置相应的错误代码。
- 处理来自
validationException:
-
- 捕获参数验证异常(如
MethodArgumentNotValidException和BindException),返回字段错误信息。
- 捕获参数验证异常(如
8.3. 关键区别总结:
| 特性 | GlobalErrorController | GlobalExceptionHandler |
|---|---|---|
| 作用 | 主要处理系统错误(如404、500等) | 主要处理业务层异常、请求参数错误等 |
| 异常类型 | 捕获通用的 HTTP 错误 | 捕获应用层的异常(如 BizException、ServiceException、MaxUploadSizeExceededException) |
| 错误响应格式 | 统一的错误响应,通常返回错误码和错误消息 | 统一的错误响应,详细描述业务错误、上传限制等 |
| 异常处理 | 基于 HTTP 错误,处理页面访问错误 | 基于业务异常,处理文件上传、参数校验等 |
GlobalErrorController主要处理系统级别的错误,捕获 HTTP 错误并返回统一的错误响应。GlobalExceptionHandler主要处理应用层的异常,尤其是业务异常、上传异常、参数校验等。
&spm=1001.2101.3001.5002&articleId=145665160&d=1&t=3&u=5d6dda025abf4d42978733b2e501a886)
5644

被折叠的 条评论
为什么被折叠?



