单体项目搭建套路
2022年11月9日
项目搭建
每个项目基本都能用上的依赖:
继承父工程、启动器
<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...