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.测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/210399.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

布隆过滤器,Redis之 bitmap,场景题【如果微博某个大V发了一条消息,怎么统计有多少人看过了】

学习文档 文章目录 一、什么是 Bitmap1-1、Bitmap 相关命令 二、Bitmap 和 Set 对比2-1、数据准备2-2、内存对比2-3、性能对比 三、布隆过滤器3-1、理论3-2、代码实现 四、Java中的 Hash 函数 最近面试时&#xff0c;遇到了一个场景题&#xff0c;面试官问如何统计一条微博大V的…

pandas基础操作2

数据读取 我们想要使用 Pandas 来分析数据&#xff0c;那么首先需要读取数据。大多数情况下&#xff0c;数据都来源于外部的数据文件或者数据库。Pandas 提供了一系列的方法来读取外部数据&#xff0c;非常全面。下面&#xff0c;我们以最常用的 CSV 数据文件为例进行介绍。 …

邮件迁移-邮件同步-批量完成邮件迁移解决方案-imapsync

背景&#xff1a; 公司原来使用的邮箱服务器实现方式是james的cassandra-app&#xff0c;如今要启用新的邮件服务器&#xff0c;架构用的是james的distributed-app,升级后&#xff0c;要求邮件数据不丢失&#xff0c;因此要平滑完成邮件的迁移工作&#xff0c;保障升级后邮件不…

Selenium page object模式Python

目录 概述 优点 示例 项目结构&#xff1a; 基础页面类BasePage 业务页面类BaiduHomePage 测试类test_baidu&#xff1a; 文件工具类file_util 运行日志&#xff1a; 测试结果&#xff1a; 概述 在web应用程序的UI中&#xff0c;有一些区域可以与测试交互。页面对象…

[数据结构]红黑树的定义以及添加原则

红黑树是一种自平衡的二叉查找树&#xff0c;是一种常用的数据结构 1972年出现&#xff0c;在当时被称为平衡二叉B树。后来1978年被修改为如今的“红黑树” 它是一个特殊的二叉查找树&#xff0c;红黑树的每一个节点上都有储存位表示节点的颜色 每一个节点可以是红或者黑&#…

KNN实战-图像识别

数据说明 是在循环0-9的数字一直循环500次所得到的数据&#xff0c;然后以手写照片的形式存在 识别的步骤 加载数据构建目标值构建模型参数调优可视化展示 加载数据 import numpy as np import matplotlib.pyplot as plt # 记载数据 data np.load(./digit.npy) data构建目…

什么是Ros(二)- 结构和通讯概述

目录 1.架构 2.通讯 参考文献 上接&#xff1a;什么是Ros&#xff08;一&#xff09;-CSDN博客 1.架构 共三层&#xff1a;OS 层&#xff0c;中间层&#xff0c;应用层。 OS 层&#xff1a;OS 层是操作系统层也就是我们现在使用的ubuntu&#xff08;linux&#xff09;&…

【Java Web学习笔记】 2 - CSS入门

项目代码 零、 CSS引出 CSS 教程 官方教学文档 1.在没有CSS之前&#xff0c;我们想要修改HTML元素的样式需要为每个HTML元素单独定义样式属性&#xff0c;费心费力。所以CSS就出现了。 2.使用CSS将HTML页面的内容与样式分离提高web开发的工作效率&#xff08;针对前端开发&a…

Spring Cloud笔记 —— 什么是Spring Cloud?

引言&#xff1a; 在写这篇博客之前&#xff0c;其实吧&#xff0c;博主很久之前有过一段时间的Spring Cloud的案例项目开发经验&#xff0c;就是一个案例项目开发而已&#xff0c;也说不上有多高大上&#xff0c;那个时候&#xff0c;我其实也是从众而已罢了&#xff0c;毕竟现…

Java 设计模式系列:代理模式

文章目录 介绍静态代理基本介绍应用实例静态代理优缺点 动态代理基本介绍JDK 中生成代理对象的 API Cglib 代理基本介绍实现步骤 介绍 1&#xff09;代理模式&#xff1a;为一个对象提供一个替身&#xff0c;以控制对这个对象的访问。即通过代理对象访问目标对象 2&#xff09…

JOSEF 快速中间继电器 KZJ-4H-L DC220V 导轨安装

快速中间继电器KZJ-4H-LDC220V导轨安装导轨安装是广泛用于电力系统&#xff0c;能够断货开或开通大负载&#xff0c;并且具有较强的断弧能力&#xff0c;适用于交流50/60Hz。电压24380V,直流电压24280V自动控制电路中以增加保护和控制回路的触点数量与触点容量。 KZJ系列快速中…

leetcode 209. 长度最小的子数组(优质解法)

代码&#xff1a; //时间复杂度 O(N) ,空间复杂度 O(1) class Solution {//采用滑动窗口的方法解决public int minSubArrayLen(int target, int[] nums) {int numsLengthnums.length;int minLengthInteger.MAX_VALUE;int left0;int right0;int sum0;while (right<numsLengt…

详解原生Spring框架下的方法切入点表达式

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

C++/Qt读写xml文件

今天介绍C/Qt如何读写xml文件&#xff0c;xml文件一般用于作为配置文件使用。 C C读写xml文件需要借助第三方来实现&#xff0c;比较好用的有tinyxml2和pugixml&#xff0c;对应的网址链接。 tinyxml2 pugixml 以tinyxml2为例&#xff0c;下载后进行解压可以看到以下文件&…

Python---格式化输出与%百分号----涉及转义符 \ 反斜杠的使用

相关链接Python--格式化输出中的转义符号----\t 制表符&#xff08;空格的&#xff09;和\n&#xff08;换行的&#xff09;_唯元素的博客-CSDN博客 Python---字符串&#xff08;用单、双引号、 三单/双引号定义。反斜杠 \ 转义&#xff0c;单在双内/双在单内 &#xff09;-CS…

【电路笔记】-串联和并联电阻

串联和并联电阻 文章目录 串联和并联电阻1、概述2、串联和并联电阻示例13、串联和并联电阻示例2 电阻器可以无限数量的串联和并联组合连接在一起&#xff0c;形成复杂的电阻电路。 1、概述 在之前的教程中&#xff0c;我们学习了如何将各个电阻器连接在一起以形成串联电阻器网…

混沌系统在图像加密中的应用(基于哈密顿能量函数的混沌系统构造1.5)

混沌系统在图像加密中的应用&#xff08;基于哈密顿能量函数的混沌系统构造1.5&#xff09; 前言一、自治非哈密顿系统的构造、动态特性分析1.相关理论基础2.两个四维自治非哈密顿系统3.数值分析 python代码 前言 续接混沌系统在图像加密中的应用&#xff08;基于哈密顿能量函…

重生奇迹MU再生原石

通过坎特鲁提炼之塔的NPC艾尔菲丝提炼成功就可以可获得再生宝石。 重生奇迹mu里的再生原石的用法&#xff1a; 1、打怪获得再生原石去提炼之塔&#xff08;进入坎特鲁遗址的141188位置的传送台&#xff09;。 2、找到&#xff08;艾儿菲丝&#xff09;把原石提炼成再生宝石。…

MySQL安全相关——TDE和数据脱敏功能介绍

MySQL作为一款广泛使用的开源关系型数据库管理系统(RDBMS)&#xff0c;其安全性一直是开发者和企业关注的重点。在MySQL中&#xff0c;有一些与安全相关的功能&#xff0c;其中包括Transparent Data Encryption(TDE)和数据脱敏。本文将对这些功能进行介绍。 一、Transparent Da…

深度学习实战63-利用自适应混合金字塔网络实现人脸皮肤美颜效果,快速部署与实现一键美颜功能

大家好,我是微学AI,今天给大家介绍一下深度学习实战63-利用自适应混合金字塔网络实现人脸皮肤美颜效果,快速部署与实现一键美颜功能。在本文中,我将介绍一种新颖的自适应混合金字塔网络(ABPN),该网络可以实现对超高分辨率照片的快速局部修饰。该网络主要由两个组件组成:一…