ssm web项目的整体开发流程 技术栈 springboot
+mybatis
后端api的测试:knife4j
,knife4j参考资料
整理一套开发规范
文件目录
项目背景 创建一个简单数据库表user
执行简单的增删改查业务逻辑
开发过程 domain层开发 定义一个Java
实体类com.example.domain.User
与数据库表user
相对应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.example.domain;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String userName; private String passWord; }
dao层开发 dao层我们采用mybatis
框架
step1 在application.yml
配置文件中配置mysql
数据库和mybatis mapper
扫描路径 1 2 3 4 5 6 7 8 9 10 11 spring: application: name: ssm-demo datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/my-test?useUnicode=true&characterEncoding=utf8&useSSL=false password: 123456 username: root mybatis: mapper-locations: classpath:mapper/*.xml
step2 编写dao层接口 创建com.example.mapper.UserMapper
接口,里面定义争对数据库表user
的dao
层业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.example.mapper;import com.example.domain.User;import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper public interface UserMapper { User findById (int id) ; List<User> findAll () ; int updateUser (User user) ; int insert (User user) ; int delete (User user) ; }
step3 编写具体SQL语句的Mybatis映射文件 文件路劲src/main/resources/mapper/UserMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.example.mapper.UserMapper" > <select id ="findById" resultType ="com.example.domain.User" > select * from user where id=#{id}; </select > <select id ="findAll" resultType ="com.example.domain.User" > select * from user; </select > <update id ="updateUser" parameterType ="com.example.domain.User" > update user set username=#{userName},password=#{passWord} where id=#{id}; </update > <insert id ="insert" parameterType ="com.example.domain.User" > insert into user values(#{id},#{userName},#{passWord}); </insert > <delete id ="delete" parameterType ="com.example.domain.User" > delete from user where id=#{id}; </delete > </mapper >
service层开发 service往往是业务中最繁琐最终的地方,大对数代码都在这一层,这一层需要先声明一个接口,然后是一个接口实现类,又因为需要根据业务逻辑做异常处理,所以需要首先自定义异常
自定义异常 我们自己定义一个异常类,继承自RuntimeException
,各种不太的业务异常我们用一个枚举类来进行描述
枚举类异常类型申明 com.example.exception.UserExceptionEnum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.example.exception;public enum UserExceptionEnum { TYPE_NOTtMATCH_EXCEPTION("请求参数错误" ), PERMISSION_DENIED_EXCEPTION("无请求权限" ), UNKNOWN_EXCEPTION("未知类型错误" ); private String desc; UserExceptionEnum(String desc) { this .desc = desc; } public String getDesc () { return desc; } public void setDesc (String desc) { this .desc = desc; } @Override public String toString () { return "UserExceptionEnum{" + "desc='" + desc + '\'' + "} " + super .toString(); } }
自定义异常类 com.example.exception.UserException
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.example.exception;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class UserException extends RuntimeException { private UserExceptionEnum userExceptionEnum; }
service层接口申明 com.example.service.IUserService
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.example.service;import com.example.domain.User;import java.util.List;public interface IUserService { User getUserById (int id) ; List<User> findAllUser () ; boolean updateUser (User user) ; boolean addUser (User user) ; boolean deleteUser (User user) ; }
service层实现类 com.example.service.impl.UserServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.example.service.impl;import com.example.domain.User;import com.example.exception.UserException;import com.example.exception.UserExceptionEnum;import com.example.mapper.UserMapper;import com.example.service.IUserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service public class UserServiceImpl implements IUserService { @Autowired UserMapper userMapper; @Override public User getUserById (int id) { if (id<0 ){ throw new UserException (UserExceptionEnum.TYPE_NOTtMATCH_EXCEPTION); } try { return userMapper.findById(id); }catch (Exception e){ e.printStackTrace(); throw new UserException (UserExceptionEnum.UNKNOWN_EXCEPTION); } } @Override public List<User> findAllUser () { return userMapper.findAll(); } @Override public boolean updateUser (User user) { return userMapper.updateUser(user)>0 ; } @Override public boolean addUser (User user) { return userMapper.insert(user)>0 ; } @Override public boolean deleteUser (User user) { return userMapper.delete(user)>0 ; } }
Controller层开发 controller
层涉及到具体的与前端进行交互,所以我们需要与前端进行约定
前端传递过来的参数统一格式
后端最终给前端传递参数的统一格式
step1 协商前端查询参数统一格式 我们定义类com.example.req.UserReq
,前端按照此格式传递参数发送请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.example.req;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class UserReq { private int id; private String userName; private String passWord; private String token; }
step2 协商后端传递前端结果统一格式 我们定义类com.example.resp.CommonResp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.example.resp;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class CommonResp <T> { private boolean success=true ; private String message="OK" ; private T data; public CommonResp (T data) { this .data = data; } }
step3 统一异常处理 注意,在项目中我们一般将异常不断向外抛,统一在Controller
层进行异常处理
通过@ControllerAdvice
配合 @ExceptionHandler
实现全局异常处理
我们编写类com.example.controller.ControllerExceptionHandler
来进行全局异常处理,即触发对应类型异常,返回特定类型的结果给前端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.example.controller;import cn.hutool.core.bean.BeanUtil;import com.example.domain.User;import com.example.req.UserReq;import com.example.resp.CommonResp;import com.example.service.IUserService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @Slf4j @RequestMapping("/user") @Api(tags = "用户模块相关接口") public class UserController { @Autowired IUserService userService; @GetMapping("/queryOne") @ApiOperation("根据id查询单个用户") public CommonResp<User> getUserById (UserReq userReq) { log.info("请求queryOne" ); User user = userService.getUserById(userReq.getId()); return new CommonResp <>(user); } @GetMapping("/queryAll") @ApiOperation("查询所有用户") public CommonResp<List<User>> getAllUser (UserReq userReq) { List<User> userList = userService.findAllUser(); return new CommonResp <>(userList); } @PutMapping("/updateInfo") @ApiOperation("更新用户信息") public CommonResp<Boolean> upDateUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.updateUser(user); return new CommonResp <>(s); } @PutMapping("/addInfo") @ApiOperation("添加用户") public CommonResp<Boolean> addUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.addUser(user); return new CommonResp <>(s); } @PutMapping("/deleteInfo") @ApiOperation("删除用户") public CommonResp<Boolean> deleteUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.deleteUser(user); return new CommonResp <>(s); } }
step4 controller层具体业务逻辑 com.example.controller.UserController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package com.example.controller;import cn.hutool.core.bean.BeanUtil;import com.example.domain.User;import com.example.req.UserReq;import com.example.resp.CommonResp;import com.example.service.IUserService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @Slf4j @RequestMapping("/user") @Api(tags = "用户模块相关接口") public class UserController { @Autowired IUserService userService; @GetMapping("/queryOne") @ApiOperation("根据id查询单个用户") public CommonResp<User> getUserById (UserReq userReq) { log.info("请求queryOne" ); User user = userService.getUserById(userReq.getId()); return new CommonResp <>(user); } @GetMapping("/queryAll") @ApiOperation("查询所有用户") public CommonResp<List<User>> getAllUser (UserReq userReq) { List<User> userList = userService.findAllUser(); return new CommonResp <>(userList); } @PutMapping("/updateInfo") @ApiOperation("更新用户信息") public CommonResp<Boolean> upDateUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.updateUser(user); return new CommonResp <>(s); } @PutMapping("/addInfo") @ApiOperation("添加用户") public CommonResp<Boolean> addUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.addUser(user); return new CommonResp <>(s); } @PutMapping("/deleteInfo") @ApiOperation("删除用户") public CommonResp<Boolean> deleteUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.deleteUser(user); return new CommonResp <>(s); } }
拦截器开发 自定义拦截器 如果请求需要做参数或者其他权限校验,我们可以自定义拦截器类,实现HandlerInterceptor
接口
com.example.interceptor.UserInterceptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.example.interceptor;import com.example.exception.UserException;import com.example.exception.UserExceptionEnum;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Slf4j @Component public class UserInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("进入拦截器,开始进行权限校验" ); String token = request.getParameter("token" ); if (!"bang" .equals(token)){ throw new UserException (UserExceptionEnum.PERMISSION_DENIED_EXCEPTION); } log.info("权限校验通过" ); return true ; } }
在配置类中配置拦截器,及其对应白黑名单 com.example.config.WebMvcConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.example.config;import com.example.interceptor.UserInterceptor;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired UserInterceptor userInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(userInterceptor) .addPathPatterns("/user/**" ) .excludePathPatterns("/doc.html" ); } }
后端接口测试 对于dao
层以及service
层的功能开发,我们编写单元测试进行测试
对于整体接口测试,我们借助knife4j
来进行在线接口文档测试,SpringBoot整合Knife4j
配置文件配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 knife4j: enable: true openapi: title: SSM-Template-Project description: "测试SSM-Template-Project" email: bang@google.com concat: bang url: https://bang.world.com version: v1.0 license: Apache 1.0 group: test1: group-name: 用户模块测试 api-rule: package api-rule-resources: - com.example.controller
controller层代码上添加添加对应注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package com.example.controller;import cn.hutool.core.bean.BeanUtil;import com.example.domain.User;import com.example.req.UserReq;import com.example.resp.CommonResp;import com.example.service.IUserService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @Slf4j @RequestMapping("/user") @Api(tags = "用户模块相关接口") public class UserController { @Autowired IUserService userService; @GetMapping("/queryOne") @ApiOperation("根据id查询单个用户") public CommonResp<User> getUserById (UserReq userReq) { log.info("请求queryOne" ); User user = userService.getUserById(userReq.getId()); return new CommonResp <>(user); } @GetMapping("/queryAll") @ApiOperation("查询所有用户") public CommonResp<List<User>> getAllUser (UserReq userReq) { List<User> userList = userService.findAllUser(); return new CommonResp <>(userList); } @PutMapping("/updateInfo") @ApiOperation("更新用户信息") public CommonResp<Boolean> upDateUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.updateUser(user); return new CommonResp <>(s); } @PutMapping("/addInfo") @ApiOperation("添加用户") public CommonResp<Boolean> addUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.addUser(user); return new CommonResp <>(s); } @PutMapping("/deleteInfo") @ApiOperation("删除用户") public CommonResp<Boolean> deleteUserInfo (UserReq userReq) { User user = BeanUtil.toBean(userReq, User.class); boolean s = userService.deleteUser(user); return new CommonResp <>(s); } }
在线文档测试 访问http://localhost:8080/doc.html