聊一聊SpringBoot中的自定义Starter
前言
自己动手简单的封装、应用一个starter
该starter的作用是被引入后,会对项目中Controller出现的异常做统一的处理及反馈
starter的思想在实际开发过程被大量的应用
一、新建starter项目
使用IDE创建一个maven构建方式的空白项目
1.1pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.lazy.snail</groupId><artifactId>exception-handler-spring-boot-starter</artifactId><version>1.0.0</version><packaging>jar</packaging><properties><java.version>17</java.version><encoding>UTF-8</encoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.12</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>17</source><target>17</target></configuration></plugin></plugins></build></project>
- 自定义的starter命名为xxx-spring-boot-starter
- spring-boot-starter-web依赖的引入是由于开发starter过程中会使用到相关的类或注解,如:@RestControllerAdvice、@ExceptionHandler等
- 构建安装时(clean install)可以去掉spring-boot-starter-web依赖
- exception-handler-spring-boot-starter的应用场景就是基于springboot的web项目,所以相关的依赖在应用项目中都会有
1.2自定义一个Api异常
package com.lazy.snail.exceptionhandler;/*** @ClassName ApiException* @Description TODO* @Author lazysnail* @Date 2024/11/7 15:06* @Version 1.0*/
public class ApiException extends RuntimeException {private int code;private String message;public ApiException(int code, String message) {super(message);this.code = code;this.message = message;}public int getCode() {return code;}@Overridepublic String getMessage() {return message;}
}
- 引入项目中可以根据实际业务抛出该异常
1.3自定义反馈结果
package com.lazy.snail.exceptionhandler;/*** @ClassName ApiResponse* @Description TODO* @Author lazysnail* @Date 2024/11/7 15:06* @Version 1.0*/
public class ApiResponse<T> {private int status; // 状态码private String message; // 错误信息或成功提示private T data; // 业务数据public ApiResponse(int status, String message, T data) {this.status = status;this.message = message;this.data = data;}public int getStatus() {return status;}public void setStatus(int status) {this.status = status;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}
出现异常情况时,简单的封装一个反馈对象
1.4异常处理配置类
package com.lazy.snail.exceptionhandler;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @ClassName ExceptionHandlerAutoConfiguration* @Description TODO* @Author lazysnail* @Date 2024/11/7 15:32* @Version 1.0*/
@Configuration
public class ExceptionHandlerAutoConfiguration {@Bean@ConditionalOnMissingBean(GlobalExceptionHandler.class)public GlobalExceptionHandler globalExceptionHandler() {return new GlobalExceptionHandler();}
}
普通的配置类,当springboot启动过程中,容器中没有GlobalExceptionHandler类型的bean时,将创建一个
1.5异常处理类
package com.lazy.snail.exceptionhandler;import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;/*** @ClassName GlobalExceptionHandler* @Description TODO* @Author lazysnail* @Date 2024/11/7 15:06* @Version 1.0*/
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(ApiException.class)public ResponseEntity<ApiResponse<Object>> handleApiException(ApiException ex) {// 捕获 ApiException,返回指定的状态码和信息ApiResponse<Object> response = new ApiResponse<>(ex.getCode(), ex.getMessage(), null);return new ResponseEntity<>(response, HttpStatus.valueOf(ex.getCode()));}@ExceptionHandler(Exception.class)public ResponseEntity<ApiResponse<Object>> handleException(Exception ex) {// 捕获其他未处理的异常,返回500状态码ApiResponse<Object> response = new ApiResponse<>(500, "Internal Server Error", null);return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);}
}
@RestControllerAdvice
:
- Spring 提供的注解,用于定义全局的异常处理器、数据绑定和数据格式化。它是
@ControllerAdvice
和@ResponseBody
的组合,表示该类中的方法返回的对象会自动被序列化为 JSON 或 XML 格式,直接返回给客户端。 @RestControllerAdvice
的作用范围是整个应用的控制层,它会捕获所有标注了@RequestMapping
的控制器中的异常。
@ExceptionHandler(ApiException.class)
:
- 该注解标注的方法用于捕获
ApiException
类型的异常。通过将异常类型作为参数传入,Spring 可以在控制器中抛出ApiException
时自动调用此方法。 @ExceptionHandler
注解的作用是捕获指定类型的异常,使得不同的异常处理逻辑可以分开,实现精细化处理。
@ExceptionHandler(Exception.class)
:
- 该注解标注的方法用于捕获
Exception
类型的异常,这意味着它将处理所有未被其他@ExceptionHandler
方法处理的异常,起到“兜底”作用。 - 当出现任何未预料的异常时,Spring 会调用这个方法,为这些异常提供一个默认的错误响应,避免未处理的异常暴露到前端。
1.6spring.factories
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lazy.snail.exceptionhandler.ExceptionHandlerAutoConfiguration
实现自动配置机制:
- Spring Boot 会在启动时扫描
META-INF/spring.factories
文件中的EnableAutoConfiguration
配置,加载所有指定的自动配置类。这里指定了ExceptionHandlerAutoConfiguration
作为自动配置类,因此 Spring Boot 会在启动时自动加载com.lazy.snail.exceptionhandler.ExceptionHandlerAutoConfiguration
。
简化项目中的配置:
- 通过将异常处理逻辑配置为自动配置类,可以在不同项目中只需要引入该 starter,就可以自动获得其中定义的异常处理逻辑,而不需要手动配置。模块化的设计方便了复用和维护。
按需加载组件:
- 在
ExceptionHandlerAutoConfiguration
中,通常会定义与异常处理相关的@Bean
,如GlobalExceptionHandler
或其他配置项。通过这种自动配置机制,Spring Boot 会自动加载这些 Bean,确保全局异常处理器在项目启动时生效。 ExceptionHandlerAutoConfiguration
还可以通过条件注解(例如@ConditionalOnMissingBean
)判断是否需要创建某些 Bean,可以在主项目中进行定制,避免重复配置或冲突。
支持条件加载:
- Spring Boot 的自动配置类通常可以配置成启用或禁用。通过在
ExceptionHandlerAutoConfiguration
中使用条件注解(如@ConditionalOnProperty
),可以控制自动配置的加载,比如通过属性开关来启用或禁用异常处理功能。
二、构建打包
2.1命令行
mvn clean install
2.2IDE
2.3说明
clean
:执行清理操作
mvn clean
命令会删除项目的target
目录,这个目录包含了之前构建生成的所有文件(如.class
文件、JAR 包、WAR 包等)。这一步的作用是确保项目在全新的环境下进行构建,避免使用上一次构建中遗留的文件。
install
:构建并安装项目
mvn install
命令会触发 Maven 的完整构建流程(编译、测试、打包等),并将生成的构件(例如 JAR、WAR 文件)安装到本地 Maven 仓库(默认在~/.m2/repository
目录下,也可自定义)。安装到本地仓库后,其他 Maven 项目可以通过依赖管理使用该构件。- 具体来说,install 命令会顺序执行以下几个阶段:
- validate:检查项目是否正确,所有必要的信息是否完整。
- compile:编译源代码。
- test:运行测试代码。
- package:将编译后的代码打包成 JAR、WAR 等文件。
- install:将打包后的文件安装到本地 Maven 仓库,以便其他项目可以使用。
2.4结果
本地仓库中打包好了exception-handler-spring-boot-starter
三、应用
3.1新建一个springboot项目
3.1.1pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lazy.snail</groupId><artifactId>SpringBoot</artifactId><version>0.0.1-SNAPSHOT</version><name>SpringBoot</name><description>SpringBoot</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.lazy.snail</groupId><artifactId>exception-handler-spring-boot-starter</artifactId><version>1.0.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
- pom中引入了exception-handler-spring-boot-starter
3.1.2测试controller
package com.lazy.snail.controller;import com.lazy.snail.exceptionhandler.ApiException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** @ClassName TestController* @Description TODO* @Author lazysnail* @Date 2024/11/7 15:44* @Version 1.0*/
@RestController
public class TestController {@GetMapping("/test")public String test() {// 手动抛出一个自定义的异常throw new ApiException(400, "Bad Request");}@GetMapping("/error")public String error() {int a = 1/0;return "error";}}
- "/test"模拟ApiException
- "/error"模拟不可预见的异常
3.2postman测试结果
- /test
- /error
四、总结
4.1Starter的作用
-
简化配置:
通过自动配置,减少了手动配置的工作量,特别是在常见功能(如数据库连接、消息队列等)的配置上。
-
模块化功能:
可以将某些通用功能封装到独立的模块中(例如日志、缓存、事务、消息队列等),使得其他项目可以通过引入该 starter 直接使用。
-
增强可复用性:
将项目中的一些通用代码和配置抽象成 starter,方便在多个项目中复用,提升开发效率。
4.2Starter的简要开发流程
-
定义功能模块:
明确 starter 要解决的具体功能,例如统一的异常处理、日志管理、缓存配置等。
-
创建自动配置类:
在starter中编写自动配置类(通常使用 @Configuration 注解)。这个类包含自动配置所需的 bean 和逻辑,比如 @Bean 定义一些默认的配置或功能。
使用 @Conditional 注解来控制某些配置是否生效,例如 @ConditionalOnProperty 可根据配置文件中的属性来启用某些功能。
-
提供依赖项:
确定starter所需的依赖项,并将其添加到 pom.xml 中。例如,如果starter需要连接数据库,就需要引入相关的数据库驱动。
-
包装和发布:
将 starter 打包为一个可发布的 JAR 文件,并发布到本地或远程 Maven 仓库。
在META-INF/spring.factories中注册自动配置类,使得Spring Boot能够自动识别和加载该starter。
4.3Starter的应用场景
-
统一配置和功能封装:
统一的异常处理:将异常处理封装到starter中,其他项目只需引入即可自动获得一致的异常处理机制。
统一的日志配置:将日志配置封装为starter,让不同的项目能够使用统一的日志配置和日志格式。
统一的响应格式:提供统一的 API 响应封装类,确保所有项目的返回格式一致。
统一的安全配置:例如身份认证、授权、权限管理等功能的配置封装。
-
通用功能模块:
缓存管理:封装Spring Cache的配置,统一管理缓存的实现,减少每个项目单独配置的工作量。
数据库连接配置:封装常见的数据源配置,如 HikariCP 或 DBCP2,简化数据源的初始化和管理。
消息队列配置:封装对消息队列(如 RabbitMQ、Kafka)的配置,简化消息队列的使用和管理。
-
跨项目共享功能:
在多个项目中有相同的需求时,使用 starter 可以将功能抽象出来,减少每个项目重复编写相同代码的工作。比如,团队内部有多个微服务,可能都需要统一的异常处理和响应格式,这时可以开发一个starter让每个项目都能复用这部分功能。
-
支持不同的模块或服务:
适用于微服务架构中,每个微服务通过引入不同的starter来简化配置和功能实现。例如,可以为每个服务创建一个starter,统一管理日志、异常处理、监控等功能。
-
简化常见配置的开发和维护:
健康检查、审计日志、事务管理、定时任务 等常见功能可以通过starter提供自动化配置,无需重复编写。