系统脚手架
一、基础环境
1.开发环境
JAVA版本: JDK1.8
Maven版本: 3.3.9
MySql版本: 8.0.2
Spring版本: 2.7.5
2.基础依赖
spring-boot-starter作用: 提供 Spring Boot 核心功能和自动配置(日志、基础组件等)
maven 坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
spring-boot-starter-web作用: 提供 Web 开发功能,如 Spring MVC、内嵌 Tomcat,用于构建 RESTful 接口和 Web 应用
maven 坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
spring-boot-devtools作用: 提供开发时的热部署、自动重启等功能,提升开发效率
maven坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency>
lombok作用: 通过注解自动生成 getter/setter、构造器等,减少样板代码
maven坐标
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>annotationProcessor</scope> </dependency>
二、集成Mybatis Plus插件
1.导入依赖
mybatis-plus-boot-starter作用: 整合 MyBatis-Plus 与 Spring Boot,提供增强的 CRUD、条件构造器、分页等功能
版本: 3.5.2
maven坐标
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency
mybatis-plus-generator作用: MyBatis-Plus 代码生成器,用于根据数据库表快速生成实体、Mapper、Service 等代码
版本: 3.5.2
maven坐标
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.2</version> </dependency>
mysql-connector-java作用: 提供连接 MySQL 数据库所需的 JDBC 驱动,实现数据源连接和 SQL 通讯
版本: 由 Spring Boot 2.7.5 的依赖管理自动指定(MySQL JDBC 驱动)
maven坐标
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
freemarker作用: 模板引擎,配合代码生成器使用,根据模板生成 Java 源码等文件
版本: 2.3.31
maven坐标
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency>
2.配置文件编辑
数据库文件配置
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/vueshop?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: ****mybatis配置
mybatis-plus: global-config: db-config: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
3.集成分页与全表更新代码
类名c.v.c.MybatisPlusConfig
具体代码
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 防止全表更新和删除 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 分页 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
三、集成Sql语句打印分析
1.导入依赖
p6spy作用: 拦截并输出实际执行的 SQL 及参数,便于调试和分析数据库访问性能
版本: 3.9.1
maven坐标:
<dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>
2.配置文件编辑
p6spy配置
# ===================== P6Spy 配置(用于输出 SQL 日志) ===================== # 指定启用的 P6Spy 模块: # - com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory: # 使用 MyBatis-Plus 提供的日志工厂,输出格式更友好,支持格式化 SQL。 # - com.p6spy.engine.outage.P6OutageFactory: # 开启“长时间执行 SQL 检测”(outage detection)功能,用来发现执行很慢的 SQL。 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory # 备用配置示例(P6Spy 原生日志工厂),当前被注释掉: # 如果想使用 P6Spy 自带的普通日志格式,可以改用这一行。 #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义 SQL 日志输出格式类: # 使用 MyBatis-Plus 内置的 P6Spy 日志格式化器,会输出执行耗时、SQL 语句等信息,并对 SQL 进行格式化。 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger # 日志输出目标: # - StdoutLogger:直接输出到控制台(System.out),开发环境查看最方便。 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 如果希望用日志框架(如 Logback / Log4j2)记录 SQL,而不是直接打印到控制台, # 可以改用 Slf4JLogger(当前被注释掉): #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 是否在初始化时注销已注册的 JDBC Driver: # true 表示先注销已有 Driver,再由 P6Spy 代理接管,避免多个 Driver 冲突。 deregisterdrivers=true # 是否使用 URL 前缀区分是否走 P6Spy: # true 表示 JDBC URL 需要使用前缀(如 jdbc:p6spy:mysql://...)。 useprefix=true # 需要排除、不记录的日志类别(逗号分隔): # 可选类别包括:error, info, batch, debug, statement, commit, rollback, result, resultset 等。 # 这里表示不记录 info、debug、result、commit、resultset,减少日志噪音。 excludecategories=info,debug,result,commit,resultset # 日志中时间戳的格式: dateformat=yyyy-MM-dd HH:mm:ss # 示例:指定真正使用的数据库 Driver(当前被注释): # 一般在 Spring Boot 中不需要手动配置,由数据源配置决定。 #driverlist=org.h2.Driver # 是否开启“慢 SQL”检测(Outage Detection): # true 表示开启,当 SQL 执行时间超过阈值时会输出慢 SQL 提示。 outagedetection=true # 慢 SQL 判定阈值(单位:秒): # 当某条 SQL 执行时间 >= 2 秒时,会被记录为慢 SQL。 outagedetectioninterval=2
四、统一结果集封装
1.业务目标
核心作用: 用于在接口层统一返回状态码、提示信息和数据结构,方便前后端约定与处理。
状态码约定: 200代表正常,非200(如默认失败状态码为400)表示异常。
2.结果集数据结构
包含序列化: 实现
Serializable接口以便于网络传输。code: 整型,表示状态码。msg: 字符串,表示返回的提示信息。data: Object类型,表示接口实际返回的业务负载数据。
3.具体代码实现
类路径:
com.vueshop.lang.Result具体代码:
package com.vueshop.lang; import lombok.Data; import java.io.Serializable; /** * 统一的接口返回结果封装 */ @Data public class Result implements Serializable { // 200是正常,非200表示异常 private int code; // 返回信息 private String msg; // 结果数据 private Object data; /** * 成功 - 无数据返回 */ public static Result success() { return success(null); } /** * 成功 - 带数据返回 */ public static Result success(Object data) { return success(200, "操作成功", data); } /** * 成功 - 自定义状态码、信息和数据 */ public static Result success(int code, String msg, Object data) { Result r = new Result(); r.setCode(code); r.setMsg(msg); r.setData(data); return r; } /** * 失败 - 默认400状态码,不带数据 */ public static Result fail(String msg) { return fail(400, msg, null); } /** * 失败 - 默认400状态码,带数据 */ public static Result fail(String msg, Object data) { return fail(400, msg, data); } /** * 失败 - 自定义状态码、信息和数据 */ public static Result fail(int code, String msg, Object data) { Result r = new Result(); r.setCode(code); r.setMsg(msg); r.setData(data); return r; } }
五、全局异常处理
1.业务目标
统一捕获业务层抛出的异常,返回标准的接口结果结构,避免向前端暴露服务端代码异常详情。
集中记录异常日志,避免在各个 Controller 中重复编写冗余的
try-catch代码块。通过自定义异常类区分系统异常和业务异常,并携带业务错误码和错误信息,方便接口统一返回和日志排查。
2.自定义业务异常封装
核心作用: 在业务处理过程中统一抛出特定业务错误,继承自
RuntimeException异常类。包含属性: 包含异常提示信息
msg和默认值为 500 的异常码code。类路径:
com.vueshop.common.exception.HubException具体代码:
package com.vueshop.common.exception; /** * 自定义业务异常 */ public class HubException extends RuntimeException{ // 异常提示信息 private String msg; // 异常码 private int code = 500; /** * 仅自定义异常信息的构造方法 */ public HubException(String msg) { super(msg); this.msg = msg; } /** * 自定义异常信息与异常码的构造方法 */ public HubException(String msg, int code) { super(msg); this.msg = msg; this.code = code; } }
3.全局异常拦截器实现
核心作用: 使用
@RestControllerAdvice注解拦截 Controller 层的异常,配合@ExceptionHandler和@ResponseStatus实现异常分类处理,并将结果封装为之前定义的Result结构返回。拦截类型:
HubException: 拦截自定义业务异常,固定返回 HTTP 400 状态码。RuntimeException: 全局兜底的运行时异常处理,统一返回 HTTP 400 状态码。NoHandlerFoundException: 处理 404 资源未找到异常,返回 HTTP 404 状态码及 “404 页面不存在!” 提示。MethodArgumentNotValidException: 处理@Valid/@Validated参数校验失败异常,返回 HTTP 400 状态码,并提取首个参数校验错误信息作为提示信息返回给前端。
类路径:
com.vueshop.common.exception.GlobalExceptionHandler具体代码:
package com.vueshop.common.exception; import com.vueshop.lang.Result; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.NoHandlerFoundException; /** * 全局异常处理 */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * 处理自定义业务异常 */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = HubException.class) public Result handler(HubException e) { log.error("自定义异常 ----{}", e.getMessage()); return Result.fail(e.getMessage()); } /** * 全局兜底的运行时异常处理 */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = RuntimeException.class) public Result handler(RuntimeException e) { log.error("运行时异常 ----{}", e.getMessage()); return Result.fail(e.getMessage()); } /** * 处理 404 资源未找到异常 */ @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler(value = NoHandlerFoundException.class) public Result handler(NoHandlerFoundException e) { log.error("页面不存在异常 ---- {}",e.getMessage()); return Result.fail("404 页面不存在!"); } /** * 处理参数校验失败异常 */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = MethodArgumentNotValidException.class) public Result handler(MethodArgumentNotValidException e) { // 获取绑定的所有字段异常 BindingResult bindingResult = e.getBindingResult(); // 获取出错误中第一个错误信息[N个就取第一个] ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get(); // 输出并返回实体类校验异常的默认信息 log.error("运行时异常 ----{}", objectError.getDefaultMessage()); return Result.fail(objectError.getDefaultMessage()); } }
六、腾讯云对象存储
1.业务目标
核心作用: 封装与腾讯云 COS (Cloud Object Storage) 对象存储相关的上传逻辑,对外提供统一的文件上传接口。
功能约定: 支持批量上传图片,并在上传前按指定比例和质量对图片流进行压缩处理。
2.导入依赖
cos_api作用: 接入腾讯云对象存储服务(COS),用于文件/图片上传、下载与管理。
版本: 5.6.89
maven坐标:
<dependency> <groupId>com.qcloud</groupId> <artifactId>cos_api</artifactId> <version>5.6.89</version> </dependency>
hutool-all作用: 提供丰富的 Java 工具类(如图片处理、IO 工具等),简化日常开发。
版本: 5.8.9
maven坐标:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.9</version> </dependency>
3.配置文件编辑
存储桶参数配置(
application.yml)cos: secretId: AKIDmUI2c.... secretKey: yOiro..... region: ap-guangzhou bucketName: photo-1320933175
4.腾讯云上传图片实现
接口定义:
com.vueshop.service.AppUploadService核心方法:
List<String> upload(MultipartFile[] files, float scale, double quality) throws IOException;参数说明:
files: 待上传的图片文件数组scale: 缩放比例(例如 0.5 表示宽高压缩为原来的一半)quality: 压缩质量(0–1 之间的小数,值越小图片越模糊、体积越小)
返回值:上传成功后每张图片在 COS 上的访问 URL 列表
实现类:
com.vueshop.service.impl.AppUploadServiceImpl类注解:使用
@Service注解标记为 Spring 服务类配置属性注入(
@Value注解):@Value("${cos.secretId}") private String secretId; @Value("${cos.secretKey}") private String secretKey; @Value("${cos.region}") private String region; @Value("${cos.bucketName}") private String bucketName;实现流程:
校验图片格式
检查参数是否为空或长度为 0,若为空则返回空列表
使用
FileTypeUtil.getType()校验文件流类型只支持
png、jpg、jpeg三种图片格式(不区分大小写)使用
Assert.isTrue()断言验证文件非空和类型合法性,不符合则抛出异常
压缩图片
创建
ByteArrayOutputStream输出流使用
Img.from(pic.getInputStream())将输入流转换为图片对象调用
.scale(scale)进行比例缩放调用
.setQuality(quality)设置压缩质量调用
.write(outputStream)将压缩后的图片写入输出流
上传到腾讯云 COS
生成唯一的文件名(使用
UUID.randomUUID().toString())拼接存储路径:
"image/" + uuid + "." + type创建
PutObjectRequest对象:调用
getClient().putObject(putObjectRequest)上传文件拼接图片访问 URL:
"https://" + bucketName + ".cos." + region + ".myqcloud.com/" + path将 URL 添加到结果列表并返回
public List<String> upload(MultipartFile[] pics, float scale, double quality) throws IOException { // 1.检查图片是否符合要求 if (pics == null || pics.length == 0){ return new ArrayList<>(); } List<String> paths = new ArrayList<>() ; for (MultipartFile pic : pics){ // 通过hutool工具校验传入的输入流的类型 String type = FileTypeUtil.getType(pic.getInputStream()); // 断言验证是否为空 Assert.isTrue(!pic.isEmpty(),"图片错误"); // 断言验证类型[本质是对集合每一条图片格式进行尾缀格式校验] Assert.isTrue(Arrays.asList("png","jpg","jpeg").contains(type.toLowerCase()),"不支持图片类型"); // 2.压缩图片 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 设置压缩图片质量并写入到输出流中 Img.from(pic.getInputStream()) .scale(scale) .setQuality(quality) .write(outputStream) ; // 3.上传到腾讯云 String key = UUID.randomUUID().toString(); String path = "image/" + key + "." + type; PutObjectRequest putObjectRequest = new PutObjectRequest(this.bucketName,path,new ByteArrayInputStream(outputStream.toByteArray()),null); getClient().putObject(putObjectRequest); String picPath = "https://" + this.bucketName + ".cos." + this.region + ".myqcloud.com/" +path; paths.add(picPath); } return paths; }COS 客户端构建(
getClient()方法):private COSClient getClient() { // 1. 设置认证凭证 COSCredentials cred = new BasicCOSCredentials(secretId, secretKey); // 2. 配置客户端参数 ClientConfig clientConfig = new ClientConfig(); clientConfig.setRegion(new Region(this.region)); // 设置地域 clientConfig.setHttpProtocol(HttpProtocol.https); // 使用 HTTPS 协议 // 3. 创建并返回 COS 客户端 return new COSClient(cred, clientConfig); }
七、解决跨域问题
1.业务目标
核心作用:统一配置后端接口的 CORS 策略,解决前后端分离下的跨域访问问题。
约定:
对所有路径(
/**)生效允许指定来源、请求头和请求方法
暴露
Authorization响应头,方便前端获取 Token 等信息
2.配置类实现
类路径:
com.vueshop.config.CorsConfig核心注解:
@Configuration:标记为 Spring 配置类implements WebMvcConfigurer:实现 Web MVC 配置接口
具体代码:
package com.vueshop.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig implements WebMvcConfigurer { /** * 构建通用的跨域配置。 * * 约定: * - 允许所有来源发起跨域请求("*") * - 允许所有请求头和请求方法 * - 暴露 "Authorization" 响应头,方便前端获取 Token 等信息 * * @return 配置完成的 CorsConfiguration 实例 */ private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.addExposedHeader("Authorization"); return corsConfiguration; } /** * 注册全局 CORS 过滤器。 * * 对所有请求路径(/**)应用 buildConfig() 中定义的跨域策略。 * * @return 全局跨域过滤器实例 */ @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); return new CorsFilter(source); } /** * 使用 Spring MVC 提供的方式配置跨域映射。 * * 约定: * - 对所有路径(/**)开放跨域访问 * - 允许所有来源,允许 GET、POST、DELETE、PUT 请求方法 * - 预检请求的缓存时间为 3600 秒 * * @param registry CORS 映射注册器 */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") // .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT") .maxAge(3600); } }
3.配置说明
项目开发之脚手架
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法