Spring——Spring開發實戰經驗(1)

摘要

文章主要介紹了 Swagger 作為 API 文檔生成和測試工具的功能,包括自動生成 API 文檔、提供可視化調試界面、促進前后端協作、支持 OpenAPI 規范等。同時,還提及了 Spring Boot 與 Swagger3 的實戰應用,以及 Spring 開發中其他相關技術內容,如 @Resource 與 @Autowired 的區別、Druid 監控配置、切面日志示例等。

1. Swagger-API文檔生成和測試工具

Swagger 是一種 API 文檔生成和測試工具,用于描述、生成、測試和管理 RESTful API。它的主要作用包括以下幾個方面:

1.1. 生成 API 文檔

Swagger 可以自動掃描代碼中的 ControllerAPI 方法,并生成一份可視化的 API 文檔,無需手動維護。
示例: 在 Spring Boot 項目中,添加 @ApiOperation 注解:

@ApiOperation("獲取用戶信息")
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {return "用戶 ID:" + id;
}

1.2. 提供可視化的 API 調試界面

Swagger 自帶交互式 UI,允許開發者直接在瀏覽器中進行 API 測試,而不需要使用 Postman、cURL 等工具。

訪問 Swagger UI:

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 描述:

{"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 文檔中同時管理多個版本,例如:

@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 文件中添加以下依賴:

<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 配置類:

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 {@Beanpublic 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 兼容性問題*/@Beanpublic static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {return new BeanPostProcessor() {@Overridepublic 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

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

  1. 啟動 Spring Boot 項目后,訪問:
http://localhost:8080/swagger-ui/

如果 swagger-ui/ 訪問不到,嘗試:

http://localhost:8080/swagger-ui/index.html
  1. 訪問 OpenAPI JSON 文檔
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 的相關依賴:

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>

請根據項目的需求,選擇適合的版本。

4.2. 配置 Druid 數據源

在 Spring Boot 項目的 application.ymlapplication.properties 文件中,配置 Druid 數據源。

4.2.1. application.yml 示例配置:

spring:datasource:url: jdbc:mysql://localhost:3306/your_databaseusername: your_usernamepassword: your_passworddriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid: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 示例配置:

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 的 StatViewServletWebStatFilter 來啟用 Web 監控界面和統計功能。

4.3.1. 配置 StatViewServlet

@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

@Bean
public FilterRegistrationBean<WebStatFilter> druidWebStatFilter() {FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>();filterRegistrationBean.setFilter(new WebStatFilter());// 配置 URL 過濾規則filterRegistrationBean.addUrlPatterns("/*");filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.ico,/error");  // 排除的 URLreturn filterRegistrationBean;
}

4.4. 訪問 Druid 監控頁面

一旦完成了上述配置,你可以通過訪問 http://localhost:8080/druid 來查看 Druid 的監控界面。

監控內容

  • 連接池狀態:顯示當前連接池的基本信息,如最大連接數、活躍連接數、空閑連接數等。
  • SQL 統計:展示最近執行的 SQL 語句,包括執行次數、執行時間、影響行數等。
  • 慢 SQL 統計:展示執行時間超過指定閾值的 SQL。

4.5. 配置慢 SQL 日志

Druid 支持記錄慢 SQL,幫助開發人員優化性能。在 application.ymlapplication.properties 中,你可以配置慢 SQL 的閾值。

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

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 示例配置:

spring:datasource:url: jdbc:mysql://localhost:3306/your_databaseusername: your_usernamepassword: your_passworddriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000filters: stat,wall,log4j# 配置監控頁面stat-view-servlet:enabled: truelogin-username: adminlogin-password: adminreset-enable: falseweb-stat-filter:enabled: trueexclusions: /druid/*,*.ico,/errorlog-slow-sql: trueslow-sql-millis: 5000  # 慢 SQL 閾值:超過 5 秒的 SQL 會被記錄

4.8.2. DruidConfig.java 配置類:

@Configuration
public class DruidConfig {@Beanpublic 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;}@Beanpublic 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配置完成示例

/*** Created by libinsong on 2017/4/19.*/
@EnableScheduling。// 表示開啟spring中的定時任務功能。
@Configuration
public class WebConfig implements WebMvcConfigurer {@Value("${filter.slow.reqmillis:3000}")private String slowReqMillis;@Autowiredprivate DispatcherServlet dispatcherServlet;/*** 去掉JSON返回的Null屬性,配置HTTP消息轉換器,去掉JSON返回的Null屬性并設置UTF-8編碼。** @return*/@Beanpublic 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*/@Beanpublic 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*/@Beanpublic FilterRegistrationBean druidWebStatFilterRegistrationBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(new WebStatFilter());filterRegistrationBean.addUrlPatterns("/*");filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.ico,/error");return filterRegistrationBean;}/*** {@link LogFilter}* 注冊日志過濾器,記錄慢請求日志。* @return*/@Beanpublic FilterRegistrationBean logFilterRegistrationBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(new LogFilter());filterRegistrationBean.addUrlPatterns("/*");filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.ico,/error");filterRegistrationBean.addInitParameter("slowReqMillis", slowReqMillis);return filterRegistrationBean;}@Beanpublic HandlerInterceptor getSystemInterceptor() {return new SystemInterceptor();}/*** {@link addInterceptors}* 注冊系統攔截器,攔截特定路徑的請求* @return*/@Overridepublic 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*/@Beanpublic 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;}@Overridepublic 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 中添加以下依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 創建日志切面類 在該類中,我們可以定義切面(Aspect)來記錄日志。我們會為 Controller 層和 DAO 層的方法添加切面。

5.2. Controller 層和 DAO 層的日志記錄示例

5.2.1. 日志切面(Aspect)配置

首先,我們需要定義一個日志切面類,這個類將定義切點(即要執行切面的地方)和通知(切面觸發時執行的操作)。

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.propertiesapplication.yml 中配置了日志級別。

# application.properties
logging.level.com.example.demo.aspect=INFO

或者:

# application.yml
logging:level:com.example.demo.aspect: INFO

5.2.4. 測試 Controller 層和 DAO 層

假設我們有以下的 Controller 和 DAO 層:

Controller 示例:

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 {@Autowiredprivate UserService userService;@GetMapping("/getUser")public String getUserInfo() {return userService.getUserInfo();}
}

DAO 示例:

package com.example.demo.dao;import org.springframework.stereotype.Repository;@Repository
public class UserDao {public String getUser() {// Simulating DB interactionreturn "User Data from Database";}
}

Service 示例:

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 {@Autowiredprivate UserDao userDao;// 查詢用戶信息public String getUserInfo() {return userDao.getUser();}// 創建用戶信息public String createUser(String username) {// 假設我們會在這里做一些業務處理return "Created user: " + username;}
}

執行和查看日志

當你訪問 GET /getUser 接口時,控制臺日志將顯示如下信息:

Entering Controller method: getUserInfo
Entering DAO method: getUser
Exiting DAO method: getUser
Exiting Controller method: getUserInfo

5.3. Aspect切面日志總結

  1. Controller 層日志:通過 @Pointcut("execution(* com.example.demo.controller..*(..))") 配置切點,結合 @Before@After 注解記錄進入和退出的方法信息。
  2. DAO 層日志:類似地,使用 @Pointcut("execution(* com.example.demo.dao..*(..))") 配置切點來記錄數據庫操作方法的日志。

通過 AOP 技術,你可以在不改變原有業務邏輯的情況下,靈活地添加日志記錄,便于調試、監控和審計。

6. SpringBoot項目中RPC遠程調用監控日志記錄

/*** @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);@Overridepublic 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 注解

@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 框架自動管理的,具體生命周期包括:

  1. 注冊階段DubboFilter 會在服務啟動時被注冊到 Dubbo 框架中。
  2. 調用攔截:每次 Dubbo 服務接收到請求時,都會經過 DubboFilter 進行處理,按照過濾器的鏈式調用順序執行。
  3. 過濾器順序:如果有多個過濾器,DubboFilter 會按順序執行,執行完畢后,invoker.invoke(invocation) 會調用實際的服務方法。
  4. 結果處理:在服務方法調用結束后,DubboFilter 會對結果進行處理,如記錄日志、采集監控數據等。

6.5. 示例執行流程

假設有一個 Dubbo 服務提供者,客戶端調用服務時:

  1. 客戶端請求:消費者向服務提供者發送 RPC 請求。
  2. DubboFilter 起作用
    • DubboFilter 攔截到請求。
    • 記錄請求的開始時間。
    • 執行 invoker.invoke(invocation),即繼續執行服務邏輯。
  1. 服務方法執行invoker.invoke() 執行實際的服務方法。
  2. 執行結束DubboFilter 在服務方法執行完成后,記錄調用的時間、方法名、接口名等信息,并通過 MeterRegistryUtil 發送監控數據。
  3. 返回結果:最后,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 來進行全局的異常處理。

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 字段返回。


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 Setterspublic 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;}}
}

這樣,當應用中發生異常時,返回給客戶端的錯誤信息會包含在 errormessage 字段中,保持了接口返回的統一性。

7.2.3. 全局模型屬性

你還可以通過 @RestControllerAdvice 來定義全局的模型屬性,使得所有的 Controller 方法都能共享一些公共數據。

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 方法提供一些全局的數據綁定配置,例如設置格式化器、攔截器等。

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 中的配置@InitBinderpublic 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

@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@ResponseBodypublic 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 封裝錯誤信息,但它更側重于捕獲和處理業務相關的異常,并返回詳細的錯誤信息(如錯誤碼、業務消息等)。
  • 異常類型:它針對各種業務異常(如 BizExceptionServiceException)以及文件上傳、請求參數校驗等進行處理。
  • 方法
    • 它有針對性地處理不同類型的業務異常,并返回對應的錯誤信息。

8.2. GlobalExceptionHandler

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";@Resourceprivate MultipartProperties multipartProperties;@ExceptionHandler(value = Exception.class)@ResponseBodypublic 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)@ResponseBodypublic 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)@ResponseBodypublic 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. 詳細功能:

  1. 捕獲并處理不同類型的異常
    • 業務異常 (BizExceptionServiceExceptionSystemException):這些異常通常表示應用邏輯中出現的錯誤。根據不同的業務需求,會返回不同的錯誤信息。
    • 參數校驗異常:處理參數錯誤,如 MethodArgumentNotValidExceptionBindException,主要用于校驗請求參數是否正確。
    • 上傳文件大小超過限制的異常:如 MaxUploadSizeExceededException,當上傳的文件大小超過預設的限制時,返回相應的錯誤信息。
    • 系統異常和未捕獲的異常:對于不在上述處理范圍內的異常,返回通用的錯誤信息,并記錄錯誤日志。
  1. 返回統一的錯誤響應格式
    • 使用 Response 對象來包裝錯誤響應信息,返回統一的錯誤碼、錯誤消息和額外的錯誤詳情(如上傳文件大小等)。
  1. 日志記錄
    • 每次捕獲異常時,都會記錄錯誤日志,方便問題的追蹤和排查。
  1. 異常信息自定義
    • 針對不同類型的異常,可以自定義錯誤消息。例如,業務異常可以返回具體的業務錯誤信息,上傳異常可以返回文件上傳限制的詳細信息。

8.2.3. 異常處理方法:

  1. defaultErrorHandler
    • 捕獲所有未被專門處理的異常。它會根據異常類型分別進行處理:
      • BusException:業務異常,返回業務相關的錯誤信息。
      • ServiceException:服務層異常,返回服務相關的錯誤信息。
      • SystemException:系統異常,返回系統錯誤信息。
      • 其他異常(如 ServletRequestBindingExceptionIllegalArgumentException):一般參數錯誤或非法參數。
      • 未知異常:記錄并返回通用的錯誤信息。
  1. handleException
    • 專門捕獲 BizException 類型的異常,處理業務相關的異常。根據異常中設置的消息和參數,返回相應的錯誤信息。
  1. handleUploadException
    • 專門處理文件上傳時發生的異常,如文件大小超過了服務器設置的限制(MaxUploadSizeExceededException)。
  1. handleFieldException
    • 處理來自 cn.fraudmetrix 模塊的 BizException,并根據其 code 字段設置相應的錯誤代碼。
  1. validationException
    • 捕獲參數驗證異常(如 MethodArgumentNotValidExceptionBindException),返回字段錯誤信息。

8.3. 關鍵區別總結:

特性

GlobalErrorController

GlobalExceptionHandler

作用

主要處理系統錯誤(如404、500等)

主要處理業務層異常、請求參數錯誤等

異常類型

捕獲通用的 HTTP 錯誤

捕獲應用層的異常(如 BizException

ServiceException

MaxUploadSizeExceededException

錯誤響應格式

統一的錯誤響應,通常返回錯誤碼和錯誤消息

統一的錯誤響應,詳細描述業務錯誤、上傳限制等

異常處理

基于 HTTP 錯誤,處理頁面訪問錯誤

基于業務異常,處理文件上傳、參數校驗等

  • GlobalErrorController 主要處理系統級別的錯誤,捕獲 HTTP 錯誤并返回統一的錯誤響應。
  • GlobalExceptionHandler 主要處理應用層的異常,尤其是業務異常、上傳異常、參數校驗等。

博文參考

  • [二] Spring 程序入口和xml解析 | 葉良辰の學習筆記

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895639.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895639.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895639.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SAP-ABAP:SAP的Screen Layout Designer屏幕布局設計器詳解及示例

在SAP中&#xff0c;Screen Layout Designer&#xff08;屏幕布局設計器&#xff09;是用于設計和維護屏幕&#xff08;Dynpro&#xff09;布局的工具。通過Screen Layout Designer&#xff0c;您可以創建和修改屏幕元素&#xff08;如輸入字段、按鈕、文本、表格控件等&#x…

安全筑基,智能賦能:BeeWorks IM引領企業協同新紀元

在數字經濟高速發展的今天&#xff0c;企業通訊系統已從單純的信息傳遞工具演變為支撐業務創新的核心平臺。傳統通訊工具在安全性、智能化、協同性等方面的不足&#xff0c;嚴重制約著企業的數字化轉型進程。BeeWorks IM系統以其創新的技術架構和智能化功能&#xff0c;正在重新…

SpringBoot實戰:高效獲取視頻資源

文章目錄 前言技術實現SpringBoot項目構建產品選取配置數據采集 號外號外 前言 在短視頻行業高速發展的背景下&#xff0c;海量內容數據日益增長&#xff0c;每天都有新的視頻、評論、點贊、分享等數據涌現。如何高效、精準地獲取并處理這些龐大的數據&#xff0c;已成為各大平…

【IoTDB 線上小課 11】為什么 DeepSeek 要選擇開源?

新年新氣象&#xff0c;【IoTDB 視頻小課】第十一期全新來臨&#xff01; 關于 IoTDB&#xff0c;關于物聯網&#xff0c;關于時序數據庫&#xff0c;關于開源... 一個問題重點&#xff0c;3-5 分鐘&#xff0c;我們講給你聽&#xff1a; 開源“加成”再次展現&#xff01; 現在…

宏任務和微任務

在前端開發中&#xff0c;**宏任務&#xff08;Macro Task&#xff09;**和**微任務&#xff08;Micro Task&#xff09;**是 JavaScript 事件循環&#xff08;Event Loop&#xff09;中的兩個重要概念。它們決定了異步代碼的執行順序。 --- ### 1. **事件循環&#xff08;Ev…

人工智能 - 機器學習、深度學習、強化學習是人工智能領域的理論基礎和方法論

機器學習、深度學習、強化學習是人工智能領域的三大核心方向,各自具有獨特的理論基礎和方法論。以下是它們的核心理論知識總結: 一、機器學習(Machine Learning, ML) 1. 基礎概念 目標:通過數據驅動的方式,讓機器從經驗中學習規律,完成預測、分類或決策任務。 核心范式…

java處理pgsql的text[]類型數據問題

背景 公司要求使用磐維數據庫&#xff0c;于是去了解了這個是基于PostgreSQL構建的&#xff0c;在使用時有場景一條圖片數據中可以投放到不同的頁面&#xff0c;由于簡化設計就放在數組中&#xff0c;于是使用了text[]類型存儲&#xff1b;表結構 #這是一個簡化版表結構&…

. Unable to find a @SpringBootConfiguration(默認軟件包中的 Spring Boot 應用程序)

解決&#xff1a; 新建一個包即可 問題&#xff1a; 默認軟件包中的 Spring Boot 應用程序。 原因&#xff1a; 默認包的定義 &#xff1a; 如果一個 Java 類沒有使用 package 聲明包名&#xff0c;則該類會被放置在默認包中。Spring Boot 遵循 Java 的包管理約定&#xff…

C語言——排序(冒泡,選擇,插入)

基本概念 排序是對數據進行處理的常見操作&#xff0c;即將數據按某字段規律排列。字段是數據節點的一個屬性&#xff0c;比如學生信息中的學號、分數等&#xff0c;可針對這些字段進行排序。同時&#xff0c;排序算法有穩定性之分&#xff0c;若兩個待排序字段一致的數據在排序…

滲透利器:YAKIT 工具-基礎實戰教程.

YAKIT 工具-基礎實戰教程. YAKIT&#xff08;Yak Integrated Toolkit&#xff09;是一款基于Yak語言開發的集成化網絡安全單兵工具&#xff0c;旨在覆蓋滲透測試全流程&#xff0c;提供從信息收集、漏洞掃描到攻擊實施的自動化支持。其核心目標是通過GUI界面降低Yak語言的使用…

CRISPR spacers數據庫;CRT和PILER-CR用于MAGs的spacers搜索

iPHoP&#xff1a;病毒宿主預測-CSDN博客 之前介紹了這個方法來預測病毒宿主&#xff0c;今天來介紹另一種比較用的多的方法CRISPR比對 CRISPR spacers數據庫 Dash 在這可以下載作者搜集的spacers用于后期比對 CRT和PILER-CR 使用 CRT 和 PILERCR 識別 CRISPR 間隔區&#x…

模糊聚類分析方法:從模糊等價矩陣到動態分類

一、模糊聚類分析的核心思想 在實際工程技術和經濟管理問題中&#xff0c;我們常常需要對對象進行分類。例如&#xff0c;根據生物特征對物種分類、根據氣候特征對城市分類、根據用戶行為對客戶群體分類等。傳統的聚類分析基于清晰的分類邊界&#xff0c;但現實中許多分類問題…

DeepSeek從入門到精通:提示詞設計的系統化指南

目錄 引言&#xff1a;AIGC時代的核心競爭力 第一部分 基礎篇&#xff1a;提示詞的本質與核心結構 1.1 什么是提示詞&#xff1f; 1.2 提示詞的黃金三角結構 第二部分 類型篇&#xff1a;提示詞的六大范式 2.1 提示語的本質特征 2.2 提示語的類型 2.2.1 指令型提示詞 …

【EDA學習】嘉立創題庫

一、多選題 1.嘉立創題庫的作用是什么&#xff0c;以下描述正確的是&#xff1f; A.提供學習平臺&#xff0c;幫助客戶了解嘉立創工藝 B.可成為嘉立創客戶所在企業的內部培訓資料&#xff0c;打通設計與制造&#xff0c;提高產品研發效率&#xff0c;降本增效 C.可成為嘉立創客…

Python PyCharm DeepSeek接入

Python PyCharm DeepSeek接入 創建API key 首先進入DeepSeek官網&#xff0c;https://www.deepseek.com/ 點擊左側“API Keys”&#xff0c;創建API key&#xff0c;輸出名稱為“AI” 點擊“創建"&#xff0c;將API key保存&#xff0c;復制在其它地方。 在PyCharm中下…

對界面簡單易用封裝SDK

1.三大接口 1.CheckTuple package com.x.globalcommonservice.model.permissioncontrolservice.openfga.service;import com.x.globalcommonservice.global.exception.CodeException; import com.x.globalcommonservice.model.permissioncontrolservice.openfga.dto.tuple.Op…

【Pico】使用Pico進行無線串流搜索不到電腦

使用Pico進行無線串流搜索不到電腦 官串方式&#xff1a;使用Pico互聯連接電腦。 故障排查 以下來自官方文檔 請按照以下步騾排除故障&#xff1a; 確認電腦和一體機連接了相同的路由器WiFi網絡(相同網段) IP地址通常為192.168.XX&#xff0c;若兩設備的IP地址前三段相同&…

[免費]Springboot+Vue醫療(醫院)掛號管理系統【論文+源碼+SQL腳本】

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;看到一個不錯的SpringbootVue醫療(醫院)掛號管理系統&#xff0c;分享下哈。 項目視頻演示 【免費】SpringBootVue醫療(醫院)掛號管理系統 Java畢業設計_嗶哩嗶哩_bilibili 項目介紹 在如今社會上&#xff0c;關于信息上…

【一文讀懂】WebRTC協議

WebRTC&#xff08;Web Real-Time Communication&#xff09;協議 WebRTC&#xff08;Web Real-Time Communication&#xff09;是一種支持瀏覽器和移動應用程序之間進行 實時音頻、視頻和數據通信 的協議。它使得開發者能夠在瀏覽器中實現高質量的 P2P&#xff08;點對點&…

沃德校園助手系統php+uniapp

一款基于FastAdminThinkPHPUniapp開發的為校園團隊提供全套的技術系統及運營的方案&#xff08;目前僅適配微信小程序&#xff09;&#xff0c;可以更好的幫助你打造自己的線上助手平臺。成本低&#xff0c;見效快。各種場景都可以自主選擇服務。 更新日志 V1.2.1小程序需要更…