侧边栏壁纸
博主头像
小小猫儿鱼的菜园

夜晚,影子也离我而去

  • 累计撰写 5 篇文章
  • 累计创建 6 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

优雅构建SpringBoot Start

小小猫儿鱼
2025-01-12 / 0 评论 / 0 点赞 / 7 阅读 / 0 字 / 正在检测是否收录...

最近在维护公司内部的二方库,有时候对SpringBootstart理解不到位,也有部分同事感觉使用二方库麻烦,不如自己配置.但是这样各个项目会有差异化,导致后面的工作会有挑战,因此还是大力推二方库(不知道原因...)

今天简单整理一下SpringBootstart构建方式,记录一下,以免以后自己忘记了.

创建项目

创建一个日志打印的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

项目代码

demo-log-spring-boot-starter.7z

0

评论区