Springboot development, этого пакета достаточно!

Java задняя часть
Springboot development, этого пакета достаточно!

2021-12-25 Обновление

  1. Этот проект был загружен на центральный склад, добро пожаловать в использование
<dependency>
    <groupId>io.github.chenxuancode</groupId>
    <artifactId>base</artifactId>
    <version>1.0.0-Release</version>
</dependency>
  1. Быстро создать проект: выполните следующую команду, чтобы быстро создать проект Springboot, содержащий функции этого проекта.
mvn archetype:generate -DarchetypeGroupId=io.github.chenxuancode -DarchetypeArtifactId=archetype -DarchetypeVersion=1.0.0-Release -DgroupId=io.github.chenxuancode -DartifactId=demo -Dport=8888

Tip:

  • DgroupId修改为你的groupId,DartifactId修改为你项目的artifactId
  • 命令控制台会确认你项目的groupId、artifactId等信息,如无需修改回车确认即可
  1. Код этого проекта обновлен доGitHub.com/Chen Xuan код…

last: Отзывы и предложения приветствуются!!!Нелегко кодировать слова, пожалуйста, поставьте лайк и подпишитесь на звездочку~~~

-------------------------------------------------- -----------------------сегментация

текст

При разработке зрелого проекта базовый пакет будет предоставлять некоторые общие функциональные компоненты проекта, чтобы избежать повторения колес для каждого проекта. Этот проект включает в себя основные функции, обычно используемые при разработке проектов Springboot.Единое управление зависимостями,Обработка исключений,Упаковка ответного сообщения,Единое управление журналами,Шифрование и дешифрование конфиденциальных данныхи другие функции. Поддерживайте подключаемый режим, пока вводятся зависимости, доступны вышеуказанные функции.

Вот как это сделать~~~

Единое управление зависимостями

Объединение некоторых часто используемых зависимостей в базовый пакет может облегчить последующее управление компонентами (обновление или исправление ошибок и т.д.) Принцип зависимостей компонентов в базовом пакете - стабильность и минимум зависимостей. В настоящее время в базу входят следующие компоненты, в основном отвечающие основным функциям, разработанным проектом springboot. Зависимости базовых пакетов каждого проекта управляются базой, и нужно вводить только базовый модуль, а пакеты функций вводятся самим каждым проектом. В текущий базовый пакет интегрированы общие базовые компоненты, такие как mybatis-plus swagger.

имя компонента Версия
spring-boot-starter-validation 2.3.12.RELEASE
spring-boot-starter-web 2.3.12.RELEASE
spring-boot-starter-test 2.3.12.RELEASE
spring-boot-starter-aop 2.3.12.RELEASE
mysql-connector-java 8.0.16
mybatis-plus 3.4.0
springfox-swagger2 2.8.0
springfox-swagger-ui 2.8.0
swagger-bootstrap-ui 1.8.5
lombok 1.18.20
hutool-all 5.7.14

Обработка исключений

Определяется унифицированный глобальный обработчик исключений, который не поощряет захват исключений в бизнес-коде и выбрасывает все исключения на уровнях dao, службы и контроллера на верхний уровень.try-catch对业务代码的侵入性Если вам нужно вернуть указанное сообщение об ошибке интерфейса, вы можете напрямую создать пользовательское исключение.AiException

throw new ApiException("两次密码输入不一致");

Принцип реализации

использовать@RestControllerAdviceВключите захват глобальных исключений и настройте метод для использованияExceptionHandlerАннотируйте, а затем определите тип захваченных исключений для единообразной обработки этих захваченных исключений.

@Slf4j
@RestControllerAdvice
public class ExceptionControllerAdvice {

    @ExceptionHandler(ApiException.class)
    public ResultVO<String> apiExceptionHandler(ApiException e) {
        log.error("接口请求异常:{}{}",e.getResultCode(),e.getMsg());
        return new ResultVO<>(e.getResultCode(), e.getMsg());
    }

    @ExceptionHandler
    public ResultVO unknownException(Exception e) {
        log.error("发生了未知异常", e);
        return new ResultVO<>(ResultCode.ERROR, "系统出现错误, 请联系网站管理员!");
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<String> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        return new ResultVO<>(ResultCode.VALIDATE_FAILED, objectError.getDefaultMessage());
    }

}

Другие исключения, не созданные явно, автоматически распознаются внешним обработчиком исключений как неизвестные ошибки и возвращаются во внешний интерфейс.

обработка журнала

В проектах Springboot компоненты журнала обычно используются для управления журналом. Тогда, если каждый сервис будет писать свой конфигурационный файл логбэка, это неизбежно приведет к различным форматам логов и путям логов, которыми сложно управлять, поэтому обработка логов передана в базовый пакет для унифицированной обработки. Обычно при обработке журналов необходимо учитывать несколько моментов: как распечатывать журналы, как разделять журналы и управлять ими, а также как собирать журналы.

Печать входящего и исходящего журнала

Используйте @WebLog в методе контроллера, чтобы распечатать сообщение ответа на запрос.

@PostMapping("/register")
@ApiOperation(value = "注册")
@WebLog
public String register(@RequestBody @Validated RegisterParam param) {
    userService.register(param);
    return "操作成功";
}

Принцип реализации

Определение аспекта журналаLogAspect,

public class LogAspect {

    @Pointcut("@annotation(com.sleeper.common.base.annotate.WebLog)")
    public void webLog() {}


    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        log.info("IP:{}  Class Method:{}.{}   Request Args: {}",request.getRemoteAddr(),joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), new Gson().toJson(joinPoint.getArgs()));
    }

 
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        log.info("Response Args  : {} Time-Consuming : {} ms", new Gson().toJson(result),System.currentTimeMillis() - startTime);
        return result;
    }

}

WebLogаннотация

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface WebLog {

}

сохранение журнала разделения

Разделение и сохранение журнала настраивается с помощью logback. Текущий журнал разделен на журналы ошибок и обычные журналы. Каждый тип файла разделен по дням. Файлы журнала, превышающие 200M в этот день, разделяются путем увеличения числа в имени файла. правила следующие${LOG_ERROR_HOME}/${springAppName}-%d{yyyy-MM-dd}.%i.log ${LOG_INFO_HOME}/${springAppName}-%d{yyyy-MM-dd}.%i.log |

использоватьAsyncAppenderЖурнал выводится в виде асинхронного вывода. Полный журнал журналов см.:

отслеживание ссылок

В настоящее время отслеживание ссылок реализовано через MDC. MDC является важным инструментом для реализации распределенной многопоточной передачи данных журнала в системах журнала, подобных Slf4J. MDC можно использовать для печати некоторых данных контекста времени выполнения. Для ознакомления с MDC см.nuggets.capable/post/690122…

Принцип реализацииПерехватите запрос через перехватчик, сгенерируйте traceId и установите его в THreadLocalMap через интерфейс ввода MDC.

public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String traceId = request.getHeader("traceId");
        if (traceId == null) {
            traceId = IdUtil.getSnowflake().nextIdStr();
        }
        MDC.put("traceId", traceId);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
    }

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

Добавьте %X{traceId} в logback-spring.xml.

<property name="PATTERN" value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %X{traceId} %yellow(%-5level) %highlight([%t]) %boldMagenta([%C]).%green(%method[%L]): %m%n"/>

Автоматическая инкапсуляция ответного сообщения

Обычно интерфейс нужно возвращать по определенной структуре, включая кодировку результата обработки сервиса, текстовую информацию, соответствующую кодировке, возвращаемое значение и т.д.@RestControllerAdviceEnhance Controller для реализации автоматической инкапсуляции ответных сообщений

@RestControllerAdvice("com.sleeper")
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
        // 如果接口返回的类型本身就是ResultVO那就没有必要进行额外的操作,返回false
        return !returnType.getParameterType().equals(ResultVO.class) || returnType.hasMethodAnnotation(NotResponseWrap.class);
    }

    @Override
    public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
        // String类型不能直接包装,所以要进行些特别的处理
        if (returnType.getGenericParameterType().equals(String.class)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 将数据包装在ResultVO里后,再转换为json字符串响应给前端
                return objectMapper.writeValueAsString(new ResultVO<>(data));
            } catch (JsonProcessingException e) {
                throw new ApiException("返回String类型错误");
            }
        }
        // 将原本的数据包装在ResultVO里
        return new ResultVO<>(data);
    }
}

Для интерфейсов, которые не хотят автоматически инкапсулировать результат, используйте аннотации@NotResponseWrapпоставить отметку на методе

Шифрование и дешифрование конфиденциальных данных

Иногда в процессе разработки необходимо зашифровать определенные данные, такие как номера мобильных телефонов, идентификационные номера и другие данные, когда они хранятся в базе данных, чтобы предотвратить утечку данных. Этот базовый пакет обеспечивает@SensitiveData(действуя на КЛАССЕ)@SensitiveField(Он действует на FEILD для реализации операций шифрования и дешифрования, просто добавьте объект объекта данных@SensitiveDataПримечания в конфиденциальных полях плюс@SensitiveFieldМогут быть реализованы операции шифрования и дешифрования конфиденциальных данных.

@Data
@SensitiveData
public class SysUser implements Serializable {
    
 private static final long serialVersionUID = 1L;
 
    @TableId(value = "id", type = IdType.AUTO)
    private String id;
  
    @SensitiveField
    private String mobile;

}

Принцип реализации

Переписать перехватчик mybatisResultSetHandler.handleResultSets ParameterHandler.setParametersметод, который идентифицирует аннотации и шифрует поля с помощью AES при настройке параметров запроса, а также идентифицирует аннотации и расшифровывает поля с помощью AES при получении наборов результатов.Подробнее см.GitHub.com/Chen Xuan код…

изящное завершение работы

Если нет корректного завершения работы, сервер в это время отключается напрямую (kill -9), что приведет к прямому сбою бизнеса, работающего в данный момент в контейнере, и созданию грязных данных в некоторых особых сценариях. При включенном изящном завершении работы, когда веб-контейнер завершает работу, веб-сервер больше не будет получать запросы и будет ожидать завершения буфера активных запросов.

Принцип реализацииВерсия Spring Boot 2.3, используемая базовым пакетом, имеет эту встроенную функцию, и нет необходимости расширять пул потоков контейнера для ее обработки. все приложения поддерживают функцию плавного выключения, нужно только настроитьserver.shutdown=graceful. Этот базовый пакет по умолчанию включает функцию плавного завершения работы.SPIМеханизм выполняет внедрение конфигурации в службу и объединяет сценарии последующего развертывания для унифицированного управления корректным завершением работы службы.

@Configuration
public class ShutDownConfig {
    @Autowired
    ServerProperties serverProperties;
    @Autowired
    LifecycleProperties lifecycleProperties;
    @Bean
    public void setShutDownConfig() {
        serverProperties.setShutdown(Shutdown.GRACEFUL);
        lifecycleProperties.setTimeoutPerShutdownPhase(Duration.ofSeconds(20));
    }