当前位置: 首页 > article >正文

Springboot自定义starter

一、start背景和简介

1.背景

工作中经常需要将多个springboot项目共同的非业务模块抽取出来,比如访问日志、维护请求上下文中的用户信息或者链路id等等。此次模拟的是请求中用户信息维护,方便整个请求中用户信息的取用。

2.作用

根据项目组的实际需求,封装starter,可以简化开发,统一规范的效果。

3.规范

官方的starter包规范:spring-boot-starter-xxx

自定义starter包规范:xxx-spring-boot-starter

二、stater定义

1.创建maven项目(trace-spring-boot-stater),并添加依赖

<?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.su</groupId>
    <artifactId>trace-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 引入 Spring Boot 统一版本父项目管理依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>

    <dependencies>
       
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.13.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

</project>

2.UserInfo  定义请求上下文内容的类

@Data
public class UserInfo {
    private String traceId;

    private String spanId;

    private String userNo;
}

 

3.UserContext 实现同一个或父子线程中上下文用户信息的取用

public class UserContext {
    public static final TransmittableThreadLocal<UserInfo> userInfo = new TransmittableThreadLocal<>();

    private UserContext() {
    }

    public static void set(UserInfo user) {
        userInfo.set(user);
    }

    public static UserInfo get() {
        return (UserInfo) userInfo.get();
    }

    public static void remove() {
        userInfo.remove();
    }


    public static String userNo() {
        UserInfo userInfo = (UserInfo) UserContext.userInfo.get();
        return Objects.isNull(userInfo) ? null : userInfo.getUserNo();
    }
}

4.UserContextInterceptor 拦截器,拦截请求中的信息并存储

@Slf4j
@Component
public class UserContextInterceptor implements HandlerInterceptor {
    private static final String TRACE_ID = "traceId";
    private static final String SPAN_ID = "spanId";
    private static final String APP_ID = "appId";
    private static final String USER_NO = "userNo";

    @Resource
    private AppIdsConfig appIdsConfig;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 验证请求方的 AppId
        String appId = request.getHeader(APP_ID);
        Set<String> appIdsSet = appIdsConfig.getIdsSet();
        if (StringUtils.isBlank(appId) || CollectionUtils.isEmpty(appIdsSet) || !appIdsSet.contains(appId)) {
            returnJson(response, ComResponse.fail(ResponseCodeEnums.AUTHOR_ERROR_CODE));
            return false;
        }

        String spanId = UUIDGenerator.getUUID();
        String traceId = request.getHeader(TRACE_ID);
        String userNo = request.getHeader(USER_NO);
        if (StringUtils.isEmpty(traceId)) {
            traceId = UUIDGenerator.getUUID();
        }

        UserInfo userInfo = new UserInfo();
        userInfo.setUserNo(userNo);
        userInfo.setTraceId(traceId);
        userInfo.setSpanId(spanId);
        UserContext.set(userInfo);

        MDC.put(TRACE_ID, traceId);
        MDC.put(SPAN_ID, spanId);

        log.info("{requestServer:{},traceId:{},spanId:{}}", appId, traceId, spanId);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        UserContext.remove();
        MDC.remove(TRACE_ID);
        MDC.remove(SPAN_ID);
    }


    private void returnJson(HttpServletResponse response, ComResponse<Object> result) {
        response.setCharacterEncoding("UTF-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType("application/json;charset=UTF-8");
        try (PrintWriter writer = response.getWriter()) {
            writer.print(JacksonUtil.toJsonString(result));
        } catch (IOException e) {
            log.error("response error", e);
        }
    }
}

5.AppIdsConfig配置类,读取配置文件中的信息

注意:使用@ConfigurationProperties的好处是懒加载,如果配置文件中没有配置也不会报错。

如果使用@Value,如果配置文件中找不到,则会报错

@ConfigurationProperties(prefix = "app")
@Component
public class AppIdsConfig {
    private String ids;
    private Set<String> idsSet;

    public String getIds() {
        return ids;
    }

    public void setIds(String ids) {
        this.ids = ids;
        if(StringUtils.isNotEmpty(ids)){
            this.idsSet = org.springframework.util.StringUtils.commaDelimitedListToSet(ids);
        }else{
            this.idsSet = Collections.emptySet();
        }
    }

    public Set<String> getIdsSet() {
        return idsSet;
    }

}

6.配置拦截器

@Configuration
public class TraceConfiguration implements WebMvcConfigurer {
    @Resource
    private UserContextInterceptor userContextInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userContextInterceptor).addPathPatterns("/**")
                .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/doc.html/**");
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations(
                "classpath:/static/");
        registry.addResourceHandler("doc.html").addResourceLocations(
                "classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations(
                "classpath:/META-INF/resources/webjars/");
    }
}

7.最后重点,创建配置文件,把需要自动装载的类配置上

我用的springboot3以下版本,所以在resource/META-INF下新建一个spring.factories文件,添加配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.su.trace.configure.TraceConfiguration

8.maven打包 clean,install/deploy

三、引用stater

在其他项目是使用上边自定义的starter

1.添加依赖

 <dependency>
            <groupId>com.su</groupId>
            <artifactId>trace-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

2.添加配置文件内容(如果多个项目的配置内容一致,可以考虑将配置信息放到公共配置里)

app.ids=activity,crm,order,product,customer

3.测试类

@Slf4j
@RestController
@RequestMapping("test")
public class TestController {
    @GetMapping("/trace")
    public void testTrace(){
        log.info("userID:{}", UserContext.userNo());
    }

}

4.测试


http://www.kler.cn/a/154330.html

相关文章:

  • MySQL技巧之跨服务器数据查询:基础篇-A数据库与B数据库查询合并--封装到存储过程中
  • Tomcat启动过程中cmd窗口(控制台)中文乱码的问题
  • 脑机接口、嵌入式 AI 、工业级 MR、空间视频和下一代 XR 浏览器丨RTE2024 空间计算和新硬件专场回顾
  • 几何合理的分片段感知的3D分子生成 FragGen - 评测
  • <项目代码>YOLOv8 番茄识别<目标检测>
  • 3步实现贪吃蛇
  • dockerfile文件:copy和add 异同
  • 第九节HarmonyOS 常用基础组件1-Text
  • 基于Spring、SpringMVC、MyBatis的网上服装销售系统
  • 【android开发-10】android中四种布局详细介绍
  • Docker部署Plik临时文件上传系统并且实现远程访问
  • 7 种 JVM 垃圾收集器详解
  • Asp.net core WebApi 配置自定义swaggerUI和中文注释,Jwt Bearer配置
  • (CS61A)Homework 1: Variables Functions, Control
  • 快速了解Spring AOP的概念及使用
  • LangChain 18 LangSmith监控评估Agent并创建对应的数据库
  • 生成带依赖Jar 包的两种常用方式:IDEA打包工具:Artifacts 和 maven-shade-plugin
  • Effective C++(四): 资源管理
  • 将不同时间点的登录状态记录转化为不同时间段的相同登录状态SQL求解
  • 【数据结构】树与二叉树(廿三):树和森林的遍历——层次遍历(LevelOrder)
  • 康托展开(Cantor Expansion)
  • 使用trigger-forward跨流水线传递参数
  • 腾讯云优惠券领取入口及使用指南
  • 运筹学-使用python建模基本操作
  • vscode中使用luaide-lite插件断点调试cocos2dx-lua
  • [传智杯 #2 决赛] 补刀