单体项目搭建套路

赖卓成2022年11月9日
大约 4 分钟

项目搭建

每个项目基本都能用上的依赖:

  • 继承父工程、启动器

        <parent>
            <artifactId>spring-boot-starter-parent</artifactId>
            <groupId>org.springframework.boot</groupId>
            <version>2.7.5</version>
        </parent>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
    
  • JSR380

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
            </dependency>
    
  • mysql或sqlserver,其中sqlserver经常连不上,目前稳定使用的版本:8.2.2.jre8

            <dependency>
                <groupId>com.microsoft.sqlserver</groupId>
                <artifactId>mssql-jdbc</artifactId>
                <version>8.2.2.jre8</version>
            </dependency>
    
  • druid连接池,jdbc,mybatis-plus

            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.2.14</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.2</version>
            </dependency>
    
  • swagger

            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-boot-starter</artifactId>
                <version>3.0.0</version>
            </dependency>
    
  • 打包插件和多环境配置

        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
        <profiles>
            <profile>
                <id>dev</id>
                <activation>
                    <activeByDefault>true</activeByDefault>
                </activation>
                <properties>
                    <profile.name>dev</profile.name>
                </properties>
            </profile>
    
            <profile>
                <id>prod</id>
                <properties>
                    <profile.name>prod</profile.name>
                </properties>
            </profile>
        </profiles>
    
  • 多环境配置,一个application.yml,其他的加后缀。

    server:
      port: 8030
    
    spring:
      profiles:
        active: dev
      mvc:
        pathmatch:
          matching-strategy: ant_path_matcher
    
  • 数据源配置:

    spring:
      datasource:
        driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
        url: jdbc:sqlserver://www.iocaop.com:1433;database=sqlserver2017_db_lzc;user=sa;password=lzc911823616.;loginTimeout=30;
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          # 初始化连接池大小
          initial-size: 10
          max-active: 150
          ## 开启基础监控
          stat-view-servlet:
            enabled: true
            # 账号密码
            login-username: admin
            login-password: 123456
            # 访问路径:ip:port/druid/login.html
            url-pattern: /druid/*
            # ip白名单(没有配置为空,则允许所有访问)。deny是设置黑名单
            allow: null
          filter:
            # 开启sql监控
            stat:
              enabled: true
              db-type: sqlserver
              # 慢sql监控,sql监控,最慢会显示红色
              log-slow-sql: true
              slow-sql-millis: 200
            # 开启SQL防火墙监控
            wall:
              enabled: true
              db-type: sqlserver
              config:
                # 是否允许删除
                delete-allow: false
                # 是否允许删除表
                drop-table-allow: false
              # 开启日志打印
            slf4j:
              enabled: true
              statement-prepare-after-log-enabled: false
              statement-close-after-log-enabled: false
          # 开启web应用和URI监控
          web-stat-filter:
            enabled: true
            # 排除的
            exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,/swagger-resources*,/webjars*,/v2*,/doc.html"
            # profileEnable能够监控单个url调用的sql列表
            profileEnable: true
            # sessionStatEnable 开启session监控
          # 开启Spring监控(需要加入aop的依赖)
          aop-patterns: com.hulian.apply.dao
    

配置和规范

  • 允许跨域:

    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                    .maxAge(3600)
                    .allowedHeaders("*")
                    .allowCredentials(false);
        }
        
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
  • 统一返回结果

    /**
     * 统一返回结果
     *
     * @author laizhuocheng
     * @date 2022/11/09
     */
    @Data
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class Result<T> implements Serializable {
    
        private static final long serialVersionUID = 2405172041950251807L;
    
        /**
         * 返回编码
         */
        private Integer code;
    
        /**
         * 编码描述
         */
        private String msg;
    
        /**
         * 业务数据
         */
        private T data;
    
        public static <T> Result<T> success(T data) {
            Result<T> result = new Result<>();
            result.setCode(0);
            result.setMsg("success");
            result.setData(data);
            return result;
        }
    
        public static <T> Result<T> success(Integer code ,String msg,T data) {
            Result<T> result = new Result<>();
            result.setCode(code);
            result.setMsg(msg);
            result.setData(data);
            return result;
        }
    }
    
    

    使用注解,就不用每次return都Result.ok()之类的:

    /**
     * 统一返回结果注解
     *
     * @author laizhuocheng
     * @date 2022/11/09
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ApiResult {
        String value() default "";
    
        int successCode() default 0;
    
        String successMsg() default "success";
    
        Class<? extends Result> resultClass() default Result.class;
    }
    
    /**
     * RestController增强
     *
     * @author laizhuocheng
     * @date 2022/11/09
     */
    
    @Order(0)
    @ControllerAdvice
    public class MyResponseBodyAdvice implements ResponseBodyAdvice {
    
        protected boolean isStringConverter(Class converterType) {
            return converterType.equals(StringHttpMessageConverter.class);
        }
    
        protected boolean isApiResult(MethodParameter returnType) {
            return returnType.hasMethodAnnotation(ApiResult.class);
        }
    
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            return !isStringConverter(converterType) && isApiResult(returnType);
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                      Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            //关键
            return Result.success(body);
        }
    
    }
    
  • 全局异常捕获

    /**
     * 全局异常处理
     *
     * @author laizhuocheng
     * @date 2022/11/09
     */
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
        public Object methodArgumentNotValidHandler(HttpServletRequest request, Exception  e) {
            e.printStackTrace();
            BindingResult bindingResult;
            if (e instanceof MethodArgumentNotValidException) {
                bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            } else {
                bindingResult = ((BindException) e).getBindingResult();
            }
            FieldError fieldError = bindingResult.getFieldError();
            return Result.success(BusinessExceptionEnum.PARAMETERS_ERROR.getCode(), BusinessExceptionEnum.PARAMETERS_ERROR.getMsg()
                    +(null!=fieldError?"[" + fieldError.getField() + "]" + fieldError.getDefaultMessage():""),null);
        }
    
        @ExceptionHandler(value = BusinessException.class)
        public Object businessExceptionHandler(HttpServletRequest request, BusinessException e) {
            e.printStackTrace();
            return Result.success(e.getCode(), e.getMessage(),null);
        }
    
        @ExceptionHandler(value = Exception.class)
        public Object defaultErrorHandler(HttpServletRequest request, Exception e) {
            e.printStackTrace();
            return Result.success(BusinessExceptionEnum.UN_KNOW_ERROR.getCode(), BusinessExceptionEnum.UN_KNOW_ERROR.getMsg(),null);
        }
    }
    
    
    @Data
    @EqualsAndHashCode(callSuper = false)
    public class BusinessException extends RuntimeException{
    
    
        private Integer code;
    
        private  String message;
    
        public BusinessException() {
            super();
        }
    
        public BusinessException(String message) {
            super(message);
            this.message = message;
        }
    
        public BusinessException(Integer code, String message) {
            super(message);
            this.code = code;
            this.message = message;
        }
    
        public BusinessException(BusinessExceptionEnum exceptionEnum) {
            super(exceptionEnum.getMsg());
            this.code = exceptionEnum.getCode();
            this.message = exceptionEnum.getMsg();
        }
    
    }
    
    /**
     * 业务异常枚举
     *
     * @author laizhuocheng
     * @date 2022/10/31
     */
    @Getter
    @AllArgsConstructor
    public enum BusinessExceptionEnum {
    
        /**
         * 未知错误
         */
        UN_KNOW_ERROR(-1,"服务器开小差了~"),
    
        /**
         * 失败
         */
        ERROR(1,"失败"),
    
        /**
         * 参数校验错误
         */
        PARAMETERS_ERROR(0,"参数校验错误"),
    
        /**
         * 未查询到相关信息
         */
        NOT_FOUND(50001,"未查询到相关信息");
    
    
    
        private final Integer code;
        
        private final String msg;
    
    }
    
  • 拦截器校验登录状态

    public class AuthInterceptor implements AsyncHandlerInterceptor {
    
        private ObjectMapper objectMapper = new ObjectMapper();
    
        @Autowired
        private ClientCorpMapper clientCorpMapper;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String token = request.getHeader("token");
            if (StringUtils.isEmpty(token)){
                responseData(response,BusinessExceptionEnum.UN_AUTHORIZATION);
                return false;
            }
    
            UserClaimDTO userClaimDTO = JwtUtil.parseToken(token, UserClaimDTO.class);
            if (null==userClaimDTO){
                responseData(response,BusinessExceptionEnum.UN_AUTHORIZATION);
                return false;
            }
            // 获取用户信息
            ClientCorp clientCorp = clientCorpMapper.selectOne(new LambdaQueryWrapper<ClientCorp>().eq(ClientCorp::getCorpGUID, userClaimDTO.getId()));
            if (null==clientCorp){
                responseData(response,BusinessExceptionEnum.USER_DOES_NOT_EXIST);
                return false;
            }
    
            UserThreadLocalContext.set(clientCorp);
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            AsyncHandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            AsyncHandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    
        private void responseData(HttpServletResponse response, BusinessExceptionEnum businessExceptionEnum)
                throws IOException {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Type", "application/json;charset=utf-8");
            response.getWriter().write(Objects.requireNonNull(objectMapper.writeValueAsString(Result.success(businessExceptionEnum.getCode()
                    ,businessExceptionEnum.getMsg(),null))));
        }
    }
    
    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                    .maxAge(3600)
                    .allowedHeaders("*")
                    .allowCredentials(false);
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // TODO
            InterceptorRegistration registration = registry.addInterceptor(new AuthInterceptor());
            registration.addPathPatterns("/**");
            registration.excludePathPatterns("/**/error","/swagger-ui/**","/corp/sso/**");
        }
    
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
    
Loading...