实战指南:基于SpringBoot与Mybatis-Plus构建微信小程序后端服务
1. 为什么选择SpringBoot+Mybatis-Plus组合?
在开始动手搭建微信小程序后端之前,我们先聊聊技术选型。SpringBoot作为Java领域最流行的微服务框架,最大的优势就是"开箱即用"。记得我第一次用SpringBoot时,被它自动配置的特性惊艳到了——以前需要折腾半天的Tomcat配置、XML文件,现在只需要一个main方法就能启动服务。
Mybatis-Plus则是Mybatis的增强工具,它最实用的功能就是自动生成CRUD代码。去年我做用户管理系统时,原本需要写几十行的增删改查代码,用Mybatis-Plus后只需要继承BaseMapper接口就自动获得了这些方法。特别是它的Wrapper条件构造器,写复杂查询时比传统XML方式直观多了。
这个组合特别适合微信小程序后端开发,因为:
- 微信小程序通常需要快速迭代,SpringBoot的快速启动特性完美匹配
- 小程序后端的数据库操作以CRUD为主,正是Mybatis-Plus的强项
- 两者都有完善的文档和社区支持,遇到问题容易找到解决方案
2. 环境准备与项目搭建
2.1 开发环境配置
工欲善其事必先利其器,我们先准备好这些工具:
- JDK 1.8+(推荐JDK11,LTS版本更稳定)
- IntelliJ IDEA(社区版就够用,比Eclipse对SpringBoot支持更好)
- MySQL 5.7+(8.0版本性能更好,但注意时区配置)
- Postman(测试API必备)
安装MySQL时有个小坑要注意:记得设置serverTimezone=Asia/Shanghai,否则运行时会报时区错误。我遇到过好几次团队新人因为这个配置漏掉导致项目跑不起来的情况。
2.2 初始化SpringBoot项目
打开IDEA,用Spring Initializr创建项目时重点选择这几个依赖:
- Spring Web(提供RESTful支持)
- MySQL Driver(数据库连接)
- MyBatis Framework(基础ORM)
- Lombok(简化实体类代码)
pom.xml里还需要手动添加Mybatis-Plus依赖:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency>建议使用3.5.x版本,这是目前最稳定的release版。之前用过3.3.1.tmp这种临时版本,结果分页查询时有奇怪的bug。
3. 数据库设计与实体映射
3.1 用户表设计
我们先设计一个简单的用户表,包含基础字段:
CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) DEFAULT NULL COMMENT '用户名', `password` varchar(100) DEFAULT NULL COMMENT '密码', `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意使用utf8mb4字符集,这样才能支持emoji表情存储。去年有个项目因为用了utf8,结果用户昵称里的emoji全部变成问号,不得不做数据迁移。
3.2 实体类编写
使用Lombok简化代码:
@Data @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String username; private String password; private String avatar; @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; }这里有几个实用技巧:
@TableField(fill = FieldFill.INSERT)实现自动填充创建时间- 使用LocalDateTime代替Date,处理时间更友好
- 建议所有表都包含create_time和update_time字段
4. 核心功能实现
4.1 基础CRUD接口
Mybatis-Plus的强大之处在于,我们甚至不用写SQL就能实现基础CRUD。先创建Mapper接口:
@Repository public interface UserMapper extends BaseMapper<User> { }然后编写Service层:
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Override public IPage<User> pageQuery(int pageNum, int pageSize) { Page<User> page = new Page<>(pageNum, pageSize); return this.page(page); } }最后是Controller示例:
@RestController @RequestMapping("/api/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public Result<User> getById(@PathVariable Long id) { return Result.success(userService.getById(id)); } @GetMapping("/page") public Result<IPage<User>> pageQuery( @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize) { return Result.success(userService.pageQuery(pageNum, pageSize)); } }4.2 微信小程序登录对接
小程序登录流程需要特别注意安全验证。这里给出一个简化版实现:
@PostMapping("/login") public Result<String> wxLogin(@RequestBody WxLoginDTO dto) { // 1. 调用微信接口验证code String openid = wxService.getOpenid(dto.getCode()); // 2. 查询或创建用户 User user = userService.lambdaQuery() .eq(User::getOpenid, openid) .one(); if (user == null) { user = new User(); user.setOpenid(openid); user.setAvatar(dto.getAvatarUrl()); userService.save(user); } // 3. 生成JWT token String token = JwtUtil.generateToken(user.getId()); return Result.success(token); }小程序端需要先调用wx.login获取code,然后传给后端。注意code有效期只有5分钟,不能缓存。
5. 接口安全与性能优化
5.1 接口鉴权设计
小程序接口需要防止未授权访问,推荐使用JWT方案:
- 登录成功后返回token
- 前端将token放在header中(Authorization: Bearer xxx)
- 后端通过拦截器验证token
实现一个简单的JWT工具类:
public class JwtUtil { private static final String SECRET = "your-secret-key"; public static String generateToken(Long userId) { return Jwts.builder() .setSubject(userId.toString()) .setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } public static Long parseToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); return Long.parseLong(claims.getSubject()); } }5.2 缓存优化策略
对于用户信息这类读多写少的数据,建议加入Redis缓存:
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Autowired private RedisTemplate<String, User> redisTemplate; @Override public User getByIdWithCache(Long id) { String key = "user:" + id; User user = redisTemplate.opsForValue().get(key); if (user == null) { user = this.getById(id); if (user != null) { redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS); } } return user; } }缓存更新策略建议:
- 读操作:先查缓存,没有再查DB
- 写操作:先更新DB,再删除缓存(避免双写不一致)
6. 小程序端对接实战
6.1 网络请求封装
小程序端建议封装统一的request方法:
const request = (url, method, data) => { return new Promise((resolve, reject) => { wx.request({ url: `https://yourdomain.com${url}`, method, data, header: { 'Authorization': `Bearer ${wx.getStorageSync('token')}` }, success: (res) => { if (res.data.code === 200) { resolve(res.data.data) } else { wx.showToast({ title: res.data.msg, icon: 'none' }) reject(res.data) } }, fail: (err) => { wx.showToast({ title: '网络错误', icon: 'none' }) reject(err) } }) }) }6.2 分页列表实现
小程序端实现分页加载:
Page({ data: { list: [], pageNum: 1, loading: false }, onLoad() { this.loadData() }, loadData() { if (this.data.loading) return this.setData({ loading: true }) request('/api/user/page?pageNum=' + this.data.pageNum, 'GET').then(res => { this.setData({ list: [...this.data.list, ...res.records], pageNum: this.data.pageNum + 1 }) }).finally(() => { this.setData({ loading: false }) }) }, onReachBottom() { this.loadData() } })下拉刷新和上拉加载更多是小程序列表的标配功能,记得在json文件中开启enablePullDownRefresh。
7. 常见问题排查
7.1 跨域问题解决
开发时常见的跨域问题,可以通过配置解决:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*") .allowedHeaders("*"); } }生产环境建议配置具体的域名而不是通配符,更安全。
7.2 接口响应慢分析
如果发现接口响应慢,可以从这几个方面排查:
- 检查SQL是否走了索引(用EXPLAIN分析)
- 查看网络延迟(特别是小程序端到服务器的网络)
- 检查是否有N+1查询问题(Mybatis-Plus的selectList容易引发)
建议在application.yml中开启SQL日志:
logging: level: com.yourpackage.mapper: debug8. 项目部署上线
8.1 打包与运行
使用Maven打包:
mvn clean package -DskipTests生成的jar包可以直接运行:
java -jar your-project.jar --spring.profiles.active=prod建议配置不同的profile区分开发和生产环境。
8.2 微信小程序域名配置
在小程序后台需要配置服务器域名:
- request合法域名:你们的API域名
- uploadFile合法域名:文件上传域名
- downloadFile合法域名:文件下载域名
注意域名必须备案,且使用HTTPS协议。测试阶段可以在开发者工具中勾选"不校验合法域名"。
9. 进一步优化建议
9.1 接口文档生成
推荐使用Knife4j生成API文档:
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency>配置Swagger:
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.yourpackage.controller")) .paths(PathSelectors.any()) .build(); } }9.2 监控与告警
生产环境建议接入监控:
- SpringBoot Actuator:提供健康检查端点
- Prometheus + Grafana:监控系统指标
- ELK:日志收集与分析
可以在application.yml中配置:
management: endpoints: web: exposure: include: health,info,metrics10. 项目结构最佳实践
经过多个项目实践,我总结出这样的包结构比较合理:
src/main/java ├── com.yourpackage │ ├── config // 配置类 │ ├── controller // 控制器 │ ├── service // 服务接口 │ ├── service/impl // 服务实现 │ ├── mapper // Mybatis Mapper │ ├── entity // 数据库实体 │ ├── dto // 数据传输对象 │ ├── vo // 视图对象 │ ├── util // 工具类 │ └── exception // 异常处理这种结构层次清晰,适合中小型项目。对于更复杂的项目,可以考虑按业务模块划分。
