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

要在自定义Spring Boot Starter 项目中使用 MyBatis-Plus,可以按照以下步骤进行配置: 1. 添加 MyBatis-Plus 依赖:在你的自定义 Starter 的 `pom.xml` 文件中,添加 MyBatis-Plus 的依赖。可以使用以下 Maven 坐标添加 MyBatis-Plus: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本号</version> </dependency> ``` 请将 `最新版本号` 替换为你想要使用的 MyBatis-Plus 版本。 2. 配置数据源:在你的自定义 Starter 中,配置数据源的连接信息。可以使用 Spring Boot 提供的 `application.properties` 或 `application.yml` 文件进行配置,或者创建一个自定义的配置类。将数据库连接信息配置到对应的属性中。 3. 创建 Mapper 接口和实体类:在你的自定义 Starter 中,创建与数据库表对应的 Mapper 接口和实体类。Mapper 接口可以使用 MyBatis-Plus 提供的 `BaseMapper` 接口,实体类需要使用 `@TableName` 注解指定表名以及与数据库字段的映射关系。 4. 配置 MyBatis-Plus:在你的自定义 Starter 中,创建 MyBatis-Plus 的配置类,用于配置 MyBatis-Plus 的相关属性。可以使用 `@Configuration` 注解标记该类为配置类,并使用 `@MapperScan` 注解指定 Mapper 接口所在的包。 ```java @Configuration @MapperScan("com.example.yourpackage.mapper") public class MyBatisPlusConfig { } ``` 请将 `com.example.yourpackage.mapper` 替换为你的 Mapper 接口所在的包路径。 5. 使用 MyBatis-Plus:在你的自定义 Starter 中,可以通过注入 Mapper 接口来使用 MyBatis-Plus 提供的 CRUD 和查询功能。 ```java @Service public class UserService { @Autowired private UserMapper userMapper; public User getUserById(Long id) { return userMapper.selectById(id); } // 其他对用户表的操作方法 } ``` 通过以上步骤,你就可以在自定义Spring Boot Starter 项目中使用 MyBatis-Plus 进行数据库操作了。当其他开发人员使用你的 Starter 时,可以直接注入 Mapper 接口并调用相应的方法来进行数据库操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值