最近在维护公司内部的二方库,有时候对SpringBoot
的start
理解不到位,也有部分同事感觉使用二方库麻烦,不如自己配置.但是这样各个项目会有差异化,导致后面的工作会有挑战,因此还是大力推二方库(不知道原因...)
今天简单整理一下SpringBoot
的start
构建方式,记录一下,以免以后自己忘记了.
创建项目
创建一个日志打印的start
这个就一笔带过,使用IDEA
创建一个maven
项目选择,Archetypes
选择org.apache.maven.archetypes:maven-archetype-quickstart
即可.
配置信息
异常信息
自定义异常对象
自定义的异常对象,主要是方便系统抛出异常时能够更加方便识别出异常类型,更容易定位问题
@Getter
public class LogException extends RuntimeException{
private final int code;
private final String message;
public LogException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
}
全局异常拦截器
统一全局捕获异常处理来减少try…catch代码编写以及异常转义等等,以此减少代码量以及提高代码整洁程度,统一规范开发避免重复造轮子
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(LogException.class)
public ResponseEntity<Map<Object, Object>> handleApiException(LogException ex) {
// 捕获 LogException,返回指定的状态码和信息
Map<Object, Object> response = Map.of("code", ex.getCode(), "message", ex.getMessage());
return new ResponseEntity<>(response, HttpStatus.valueOf(ex.getCode()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<Object, Object>> handleException(Exception ex) {
// 捕获其他未处理的异常,返回500状态码
Map<Object, Object> response = Map.of("code", 500, "message", "系统异常:" + ex.getMessage());
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
异常拦截器配置
ConditionalOnMissingBean
系统缺少GlobalExceptionHandler
时,配置相关Bean
@Configuration
public class ExceptionHandlerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(GlobalExceptionHandler.class)
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
}
日志配置
日志拦截器
这里面的内容是抄的别人的,在请求前,和请求后打印部分日志信息
@Slf4j
@Component
public class RequestLogInterceptor implements HandlerInterceptor {
public static final String ATTRIBUTE_HANDLER_METHOD = "HANDLER_METHOD";
private static final String ATTRIBUTE_STOP_WATCH = "ApiAccessLogInterceptor.StopWatch";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 记录 HandlerMethod,提供给 ApiAccessLogFilter 使用
HandlerMethod handlerMethod = handler instanceof HandlerMethod ? (HandlerMethod) handler : null;
if (handlerMethod != null) {
request.setAttribute(ATTRIBUTE_HANDLER_METHOD, handlerMethod);
}
// 打印 request 日志
Map<String, String> queryString = ServletUtils.getParamMap(request);
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;
if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) {
log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI());
} else {
log.info("[preHandle][开始请求 URL({}) 参数({})]", request.getRequestURI(),
StrUtil.blankToDefault(requestBody, queryString.toString()));
}
// 计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 打印 response 日志
StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH);
stopWatch.stop();
log.info("[afterCompletion][完成请求 URL({}) 耗时({} ms)]",
request.getRequestURI(), stopWatch.getTotalTimeMillis());
}
}
日志配置文件
获取配置文件的信息
ConditionalOnProperty
配置文件可设置是否开启日志打印,默认是开启的
@Data
@NoArgsConstructor
@AllArgsConstructor
@AutoConfiguration
@ConditionalOnProperty(prefix = "demo.log", value = "enable", matchIfMissing = true)
@ConfigurationProperties(prefix = "demo.log")
public class LogConfig {
/**
* 应用名称
*/
private String appName="";
/**
* 日志前缀
*/
private String prefix = "";
/**
* 排除的路径
*/
private Set<String> excludes;
/**
* 包含的路径
*/
private Set<String> includes;
}
配置拦截器
配置日志拦截器
@AutoConfiguration(after = LogConfig.class)
public class LogAutoInterceptorConfig implements WebMvcConfigurer {
@Resource
private LogConfig logConfig;
@Resource
private RequestLogInterceptor requestLogInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration interceptor = registry.addInterceptor(requestLogInterceptor);
if (!ObjectUtils.isEmpty(logConfig.getExcludes())) {
interceptor.excludePathPatterns(new ArrayList<>(logConfig.getExcludes()));
}
if (!ObjectUtils.isEmpty(logConfig.getIncludes())) {
interceptor.addPathPatterns(new ArrayList<>(logConfig.getIncludes()));
} else {
interceptor.addPathPatterns("/**");
}
}
}
配置文件
demo:
log:
enable: true
app-name: demo
prefix: 打印
includes:
- /**
自动装配
在resource目录下,创建对应的文件夹,写入对应的内容即可
该部分内容是自动装配的配置类,SpringBoot会自动扫描它们,完成自动装配
com.liu.demo.config.ExceptionHandlerAutoConfiguration
com.liu.demo.config.LogConfig
com.liu.demo.config.LogAutoInterceptorConfig
请求接口打印日志
请求接口/api/test1
或者浏览器打开127.0.0.1:8080/api/test1
2025-01-11T20:47:26.785+08:00 INFO 4176 --- [nio-8080-exec-2] c.l.d.interceptor.RequestLogInterceptor : [preHandle][开始请求 URL(/api/test1) 无参数]
2025-01-11T20:47:26.788+08:00 INFO 4176 --- [nio-8080-exec-2] c.l.d.interceptor.RequestLogInterceptor : [afterCompletion][完成请求 URL(/api/test1) 耗时(2 ms)]
完整pom
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
<!-- lookup parent from repository -->
<relativePath/>
</parent>
<groupId>com.liu.demo</groupId>
<artifactId>demo-log-spring-boot-starter</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>demo-log-spring-boot-starter</name>
<url>http://maven.apache.org</url>
<description>自定义日志模块-DEMO</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hutool.version>5.8.32</hutool.version>
<skipTests>true</skipTests>
</properties>
<developers>
<developer>
<name>gdLiu</name>
<email>2283771565@qq.com</email>
<organization>https://xiaoyver.top</organization>
<timezone>+8</timezone>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- 私有MAVEN仓库 打包上传到该目录 本地maven配置文件配置有密码-->
<distributionManagement>
<repository>
<id>liu-releases</id>
<name>java-release</name>
<url>http://192.168.11.250/repository/java-release/</url>
</repository>
<snapshotRepository>
<id>liu-snapshot</id>
<name>java-snapshot</name>
<url>http://192.168.11.250/repository/java-snapshot/</url>
</snapshotRepository>
</distributionManagement>
<build>
<plugins>
<!-- 打包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<!-- 打包跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
<!-- 打包source.jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<!-- 绑定source插件到Maven的生命周期,并在生命周期后执行绑定的source的goal -->
<executions>
<execution>
<!-- 绑定source插件到Maven的生命周期 -->
<phase>compile</phase>
<!--在生命周期后执行绑定的source插件的goals -->
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
其他
有需要的可以设定一下生产环境不打印日志,需要配置多环境,请看下一篇 TODO
评论区