DEV Community

YYT-0901
YYT-0901

Posted on

Common Dependency Configuration for Spring Boot 3.5.4 + Java 17/21

完整 pom.xml

<?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
         https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <!-- Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.4</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>springboot-demo</artifactId>
    <version>1.0.0</version>

    <name>springboot-demo</name>
    <description>Spring Boot 3.5.4 Demo</description>

    <properties>
        <java.version>17</java.version>

        <!-- MyBatis-Plus -->
        <mybatis-plus.version>3.5.12</mybatis-plus.version>

        <!-- Sa-Token -->
        <sa-token.version>1.44.0</sa-token.version>

        <!-- SpringDoc -->
        <springdoc.version>2.8.9</springdoc.version>

        <!-- RocketMQ -->
        <rocketmq.version>2.3.4</rocketmq.version>

        <!-- Elasticsearch -->
        <elasticsearch.version>8.18.3</elasticsearch.version>

        <!-- MinIO -->
        <minio.version>8.5.17</minio.version>

        <!-- Hutool -->
        <hutool.version>5.8.39</hutool.version>

        <!-- MapStruct -->
        <mapstruct.version>1.6.3</mapstruct.version>

        <!-- Druid -->
        <druid.version>1.2.27</druid.version>
    </properties>


    <dependencies>

        <!-- ================================================= -->
        <!-- Web -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 参数校验 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>


        <!-- ================================================= -->
        <!-- MySQL -->
        <!-- ================================================= -->

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>


        <!-- ================================================= -->
        <!-- MyBatis-Plus -->
        <!-- ================================================= -->

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

        <!-- 分页 / SQL Parser -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-jsqlparser</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- Redis -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- Spring Cache -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>


        <!-- ================================================= -->
        <!-- Sa-Token -->
        <!-- ================================================= -->

        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot3-starter</artifactId>
            <version>${sa-token.version}</version>
        </dependency>

        <!-- Sa-Token Redis -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-redis-template</artifactId>
            <version>${sa-token.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- RocketMQ -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>${rocketmq.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- Elasticsearch -->
        <!-- ================================================= -->

        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- MinIO -->
        <!-- ================================================= -->

        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>${minio.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- Swagger / OpenAPI -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>${springdoc.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- Lombok -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>


        <!-- ================================================= -->
        <!-- MapStruct -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${mapstruct.version}</version>
            <scope>provided</scope>
        </dependency>


        <!-- ================================================= -->
        <!-- Hutool -->
        <!-- ================================================= -->

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- Druid -->
        <!-- ================================================= -->

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-3-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>


        <!-- ================================================= -->
        <!-- Actuator -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>


        <!-- ================================================= -->
        <!-- Test -->
        <!-- ================================================= -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>


    <!-- ===================================================== -->
    <!-- Build -->
    <!-- ===================================================== -->

    <build>

        <plugins>

            <!-- Spring Boot -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

        </plugins>

    </build>

</project>
Enter fullscreen mode Exit fullscreen mode

Spring Boot 3.5.4 + Java 17 + MySQL + MyBatis-Plus + Redis + Sa-Token + RocketMQ + Elasticsearch + MinIO + OpenAPI + Lombok + MapStruct + Hutool + AOP + Validation + Actuator + Test

aplication.yml

server:
  port: 8080

spring:
  application:
    name: springboot-demo

  # ==================== 数据库 ====================
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
    username: root
    password: 123456

    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000

  # ==================== Redis ====================
  data:
    redis:
      host: 127.0.0.1
      port: 6379
      database: 0
      password:

      timeout: 5s

      lettuce:
        pool:
          max-active: 20
          max-idle: 10
          min-idle: 5
          max-wait: 5s

  # ==================== Cache ====================
  cache:
    type: redis


# ==================================================
# MyBatis-Plus
# ==================================================

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml

  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0


# ==================================================
# Sa-Token
# ==================================================

sa-token:

  # Token 名称
  token-name: satoken

  # Token 有效期,单位:秒
  timeout: 2592000

  # Token 最低活跃频率
  active-timeout: 1800

  # 是否允许同一账号多地同时登录
  is-concurrent: true

  # 同一账号最大登录数量
  max-login-count: 2

  # Token 风格
  token-style: uuid-simple

  # 是否打印日志
  is-log: true


# ==================================================
# RocketMQ
# ==================================================

rocketmq:
  name-server: 127.0.0.1:9876

  producer:
    group: ${spring.application.name}-producer
    send-message-timeout: 3000

  consumer:
    group: ${spring.application.name}-consumer


# ==================================================
# Elasticsearch
# ==================================================

elasticsearch:
  url: http://127.0.0.1:9200
  username: elastic
  password: 123456


# ==================================================
# MinIO
# ==================================================

minio:
  endpoint: http://127.0.0.1:9000
  access-key: minioadmin
  secret-key: minioadmin
  bucket-name: demo


# ==================================================
# SpringDoc
# ==================================================

springdoc:
  api-docs:
    path: /v3/api-docs

  swagger-ui:
    path: /swagger-ui.html


# ==================================================
# Actuator
# ==================================================

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics

  endpoint:
    health:
      show-details: always
Enter fullscreen mode Exit fullscreen mode

RedisConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    /**
     * RedisTemplate<String, Object>
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory connectionFactory) {

        RedisTemplate<String, Object> template =
                new RedisTemplate<>();

        template.setConnectionFactory(connectionFactory);

        // Key 使用 String
        StringRedisSerializer stringSerializer =
                new StringRedisSerializer();

        // Value 使用 JSON
        GenericJackson2JsonRedisSerializer jsonSerializer =
                new GenericJackson2JsonRedisSerializer();

        // Key
        template.setKeySerializer(stringSerializer);

        // Value
        template.setValueSerializer(jsonSerializer);

        // Hash Key
        template.setHashKeySerializer(stringSerializer);

        // Hash Value
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();

        return template;
    }
}
Enter fullscreen mode Exit fullscreen mode

RedisService

@Service
@RequiredArgsConstructor
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;


    // =====================================================
    // String
    // =====================================================

    /**
     * 设置值
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue()
                .set(key, value);
    }

    /**
     * 设置值 + 过期时间
     */
    public void set(
            String key,
            Object value,
            long timeout,
            TimeUnit unit) {

        redisTemplate.opsForValue()
                .set(key, value, timeout, unit);
    }

    /**
     * 设置值 + Duration
     */
    public void set(
            String key,
            Object value,
            Duration timeout) {

        redisTemplate.opsForValue()
                .set(key, value, timeout);
    }

    /**
     * 获取值
     */
    public Object get(String key) {
        return redisTemplate.opsForValue()
                .get(key);
    }

    /**
     * 获取指定类型
     */
    public <T> T get(
            String key,
            Class<T> clazz) {

        Object value = get(key);

        if (value == null) {
            return null;
        }

        return clazz.cast(value);
    }

    /**
     * 删除
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 批量删除
     */
    public Long delete(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }

    /**
     * 判断 Key 是否存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     */
    public Boolean expire(
            String key,
            long timeout,
            TimeUnit unit) {

        return redisTemplate.expire(
                key,
                timeout,
                unit
        );
    }

    /**
     * 获取剩余过期时间
     */
    public Long getExpire(
            String key,
            TimeUnit unit) {

        return redisTemplate.getExpire(
                key,
                unit
        );
    }


    // =====================================================
    // Hash
    // =====================================================

    /**
     * Hash 设置
     */
    public void hSet(
            String key,
            String hashKey,
            Object value) {

        redisTemplate.opsForHash()
                .put(key, hashKey, value);
    }

    /**
     * Hash 获取
     */
    public Object hGet(
            String key,
            String hashKey) {

        return redisTemplate.opsForHash()
                .get(key, hashKey);
    }

    /**
     * Hash 删除
     */
    public Long hDelete(
            String key,
            Object... hashKeys) {

        return redisTemplate.opsForHash()
                .delete(key, hashKeys);
    }

    /**
     * Hash 是否存在
     */
    public Boolean hHasKey(
            String key,
            String hashKey) {

        return redisTemplate.opsForHash()
                .hasKey(key, hashKey);
    }


    // =====================================================
    // List
    // =====================================================

    /**
     * 左侧添加
     */
    public Long leftPush(
            String key,
            Object value) {

        return redisTemplate.opsForList()
                .leftPush(key, value);
    }

    /**
     * 右侧添加
     */
    public Long rightPush(
            String key,
            Object value) {

        return redisTemplate.opsForList()
                .rightPush(key, value);
    }

    /**
     * 获取 List
     */
    public List<Object> range(
            String key,
            long start,
            long end) {

        return redisTemplate.opsForList()
                .range(key, start, end);
    }


    // =====================================================
    // Set
    // =====================================================

    public Long sAdd(
            String key,
            Object... values) {

        return redisTemplate.opsForSet()
                .add(key, values);
    }

    public Set<Object> sMembers(String key) {

        return redisTemplate.opsForSet()
                .members(key);
    }

    public Boolean sIsMember(
            String key,
            Object value) {

        return redisTemplate.opsForSet()
                .isMember(key, value);
    }


    // =====================================================
    // Increment
    // =====================================================

    /**
     * 自增
     */
    public Long increment(String key) {

        return redisTemplate.opsForValue()
                .increment(key);
    }

    /**
     * 自增指定数值
     */
    public Long increment(
            String key,
            long delta) {

        return redisTemplate.opsForValue()
                .increment(key, delta);
    }

    /**
     * 自减
     */
    public Long decrement(String key) {

        return redisTemplate.opsForValue()
                .decrement(key);
    }
}
Enter fullscreen mode Exit fullscreen mode

ElasticsearchConfig.java

@Configuration
public class ElasticsearchConfig {

    @Value("${elasticsearch.url}")
    private String url;

    @Value("${elasticsearch.username}")
    private String username;

    @Value("${elasticsearch.password}")
    private String password;

    @Bean
    public ElasticsearchClient elasticsearchClient() {

        RestClient restClient = RestClient.builder(
                HttpHost.create(url)
        )
        .setDefaultHeaders(new Header[]{
                new BasicHeader(
                        HttpHeaders.AUTHORIZATION,
                        Credentials.basic(
                                username,
                                password
                        )
                )
        })
        .build();

        ElasticsearchTransport transport =
                new RestClientTransport(
                        restClient,
                        new JacksonJsonpMapper()
                );

        return new ElasticsearchClient(transport);
    }
}
Enter fullscreen mode Exit fullscreen mode

MinioProperties.java

@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {

    private String endpoint;

    private String accessKey;

    private String secretKey;

    private String bucketName;
}
Enter fullscreen mode Exit fullscreen mode

MinioConfig.java

@Configuration
@RequiredArgsConstructor
public class MinioConfig {

    private final MinioProperties properties;

    @Bean
    public MinioClient minioClient() {

        return MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(
                        properties.getAccessKey(),
                        properties.getSecretKey()
                )
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

MinioService.java

@Service
@RequiredArgsConstructor
public class MinioService {

    private final MinioClient minioClient;

    private final MinioProperties properties;

    public String upload(MultipartFile file)
            throws Exception {

        String filename =
                UUID.randomUUID()
                        + "-" + file.getOriginalFilename();

        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(properties.getBucketName())
                        .object(filename)
                        .stream(
                                file.getInputStream(),
                                file.getSize(),
                                -1
                        )
                        .contentType(file.getContentType())
                        .build()
        );

        return filename;
    }
}
Enter fullscreen mode Exit fullscreen mode

全局

ResultFailEnum.java

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ResultFailEnum {
    NOT_FOUND(404, "请求地址不存在"),
    MESSAGE_DATA_DUPLICATE(601, "信息已经存在"),
    SERVER_ERROR(500, "服务器返回错误"),
    PARAMETER_ERROR(600, "请求参数错误");

    private final Integer code;
    private final String message;
}

Enter fullscreen mode Exit fullscreen mode

Result.java

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private static final String SUCCESS = "success";
    private static final String SUCCESS_MESSAGE = "请求成功";
    private static final String FAIL = "error";

    private Integer code;

    private String status;

    private String message;

    private T data;

    public static <T> Result<T> ok() {

        return Result.<T>builder()
                .code(200)
                .status(SUCCESS)
                .message(SUCCESS_MESSAGE)
                .build();
    }

    public static <T> Result<T> ok(T data) {

        return Result.<T>builder()
                .code(200)
                .status(SUCCESS)
                .message(SUCCESS_MESSAGE)
                .data(data)
                .build();
    }

    public static <T> Result<T> fail(
            String message) {

        return Result.<T>builder()
                .code(500)
                .status(FAIL)
                .message(message)
                .build();
    }

    public static <T> Result<T> fail(
            ResultFailEnum resultFailEnum) {

        return Result.<T>builder()
                .code(resultFailEnum.getCode())
                .status(FAIL)
                .message(resultFailEnum.getMessage())
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

BusinessException

@Getter
public class BusinessException extends RuntimeException {
    private ResultFailEnum codeEnum;
    private Integer code;
    private String message;

    public BusinessException(String message, Throwable e) {
        super(message, e);
        this.message = message;
    }

    public BusinessException(String message) {
        super(message);
        this.message = message;
    }

    public BusinessException(Throwable e) {
        super(e);
    }

    public BusinessException(ResultFailEnum codeEnum) {
        super(codeEnum.getMessage());
        this.codeEnum = codeEnum;
        this.code = codeEnum.getCode();
        this.message = codeEnum.getMessage();
    }

    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
        this.message = message;
    }

    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}
Enter fullscreen mode Exit fullscreen mode

GlobalExceptionHandler

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    Object handleException(Exception e, HttpServletRequest request) {
        log.error("请求错误,请求地址{},错误信息:", request.getRequestURL(), e);
        Result ajaxResponse = new Result();
        //404
        String FAIL = "error";
        if (e instanceof NoHandlerFoundException) {
            ajaxResponse.setCode(ResultFailEnum.NOT_FOUND.getCode());
            ajaxResponse.setMessage(ResultFailEnum.NOT_FOUND.getMessage());
            ajaxResponse.setStatus(FAIL);
        } else if (e instanceof BusinessException) {
            //业务错误
            BusinessException biz = (BusinessException) e;
            ajaxResponse.setCode(biz.getCode() == null ? ResultFailEnum.PARAMETER_ERROR.getCode() : biz.getCode());
            ajaxResponse.setMessage(biz.getMessage());
            ajaxResponse.setStatus(FAIL);
        } else if (e instanceof BindException || e instanceof MethodArgumentTypeMismatchException) {
            //参数类型错误
            ajaxResponse.setCode(ResultFailEnum.PARAMETER_ERROR.getCode());
            ajaxResponse.setMessage(ResultFailEnum.PARAMETER_ERROR.getMessage());
            ajaxResponse.setStatus(FAIL);
        } else if (e instanceof DuplicateKeyException) {
            //主键冲突
            ajaxResponse.setCode(ResultFailEnum.MESSAGE_DATA_DUPLICATE.getCode());
            ajaxResponse.setMessage(ResultFailEnum.MESSAGE_DATA_DUPLICATE.getMessage());
            ajaxResponse.setStatus(FAIL);
        } else if (e instanceof ConstraintViolationException || e instanceof BindException) {
            //请求参数错误
            ConstraintViolationException ce = (ConstraintViolationException) e;
            ajaxResponse.setCode(ResultFailEnum.PARAMETER_ERROR.getCode());
            ajaxResponse.setMessage(ResultFailEnum.PARAMETER_ERROR.getMessage());
            ajaxResponse.setStatus(FAIL);
        } else {
            ajaxResponse.setCode(ResultFailEnum.SERVER_ERROR.getCode());
            ajaxResponse.setMessage(ResultFailEnum.SERVER_ERROR.getMessage());
            ajaxResponse.setStatus(FAIL);
        }
        return ajaxResponse;
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)