spring boot3单模块项目工程搭建-上(个人开发模板)

 

⛰️个人主页:     蒾酒

🔥系列专栏:《spring boot实战》


目录

写在前面

上文衔接

常规目录创建

common目录

exception.handle目录

result.handle目录

controller目录

service目录

mapper目录

entity目录

test目录

写在最后


写在前面

本文介绍了springboot开发后端服务,单模块项目工程搭建。单模块搭建出完会出多模块项目搭建。坚持看完相信对你有帮助。

同时欢迎订阅springboot系列专栏,持续分享spring boot的使用经验。

上文衔接

本文衔接上文,可以看一下:

新版idea(2023)创建spring boot3项目_新版idea2023创建springboot3-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/135785412?spm=1001.2014.3001.5501

上文我们已经通过spring官网下载了一个模板,本文继续搭建一个前后端分离架构中后端接口服务单模块工程

常规目录创建

如图:

我们一个一个来讲解吧

common目录

此目录用于存放全局会用到的一些静态常量类、枚举类、业务异常类、工具类、自定义注解、切面类、DTO、VO、配置类等都可以放在该目录下

exception.handle目录

存放全局异常处理类。

感兴趣可以看看

Spring Boot3自定义异常及全局异常捕获_springboot 自定义异常获取-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136110267?spm=1001.2014.3001.5501

result.handle目录

存放全局返回格式统一处理类。

感兴趣可以看看

Spring Boot3统一结果封装_spring boot结果集封装-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136075039?spm=1001.2014.3001.5501

controller目录

此目录用于存放控制器类(负责接收用户的请求、调用适当的业务逻辑处理请求,并将处理结果返回给用户的类

例如userController:

import com.mijiu.commom.aop.annotation.RepeatSubmit;
import com.mijiu.commom.model.dto.UserLoginDTO;
import com.mijiu.commom.model.dto.UserSmsLoginDTO;
import com.mijiu.commom.model.vo.UserLoginVO;
import com.mijiu.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;/*** <p>* 用户表 前端控制器* </p>** @author 蒾酒* @since 2024-02-03*/
@RestController
@RequestMapping("/user")
@CrossOrigin(origins = "*")//允许所有来源的请求跨域
@Tag(name = "用户模块")
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}@PostMapping("/login")@RepeatSubmit(interval = 5000)@Operation(summary = "用户账密登录")public UserLoginVO login(@RequestBody @Validated UserLoginDTO userLoginDTO) {return userService.login(userLoginDTO);}@PostMapping("/login/sms")@Operation(summary = "用户短信验证登录")public UserLoginVO smsLogin(@RequestBody @Validated UserSmsLoginDTO userSmsLoginDTO) {return userService.smsLogin(userSmsLoginDTO);}}

 上述代码中的@RepeatSubmit(interval = 5000)这个自定义注解用来防止重复提交此处用来防止重复登录,这个注解就是放在Common/annotation/目录下的。

这个防重复提交功能是基于自定义注解+AOP实现的,那对应的切面类就是放在Common/aop/目录下的。

通常控制层是不写任何业务逻辑的,它的作用主要把业务功能暴漏为接口,再者进行参数校验

spring boot3参数校验基本用法_springboot3使用校验类注解-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136180252?spm=1001.2014.3001.5501就比用户控制器类包定义了两个接口,用户的账号密码登录和短信验证登录,那么它就要依赖下层的用户业务逻辑接口的实现类的对应实现方法。下面就介绍一下service目录

service目录

前面也提到过了service目录就是用来放各种业务功能规范接口和对应实现类的

例如UserService、UserServiceImpl:

import com.mijiu.commom.model.dto.UserLoginDTO;
import com.mijiu.commom.model.dto.UserSmsLoginDTO;
import com.mijiu.commom.model.vo.UserLoginVO;
import com.mijiu.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;/*** <p>* 用户表 服务类* </p>** @author 蒾酒* @since 2024-02-03*/
public interface UserService extends IService<User> {/**** @param userLoginDTO 用户登录表单* @return 用户信息返回*/UserLoginVO login(UserLoginDTO userLoginDTO);/**** @param userSmsLoginDTO 用户手机号登录表单* @return 用户信息返回*/UserLoginVO smsLogin(UserSmsLoginDTO userSmsLoginDTO);}
import java.util.Map;
import java.util.Objects;/*** <p>* 用户表 服务实现类* </p>** @author 蒾酒* @since 2024-02-03*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {private final UserMapper userMapper;private final JwtUtils jwtUtils;private final StringRedisTemplate stringRedisTemplate;public UserServiceImpl(UserMapper userMapper, JwtUtils jwtUtils, StringRedisTemplate stringRedisTemplate) {this.userMapper = userMapper;this.jwtUtils = jwtUtils;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic UserLoginVO login(UserLoginDTO userLoginDTO) {// 获取验证码idString captchaId = userLoginDTO.getCaptchaId();// 获取用户提交验证码String userCaptcha = userLoginDTO.getCaptcha();// 获取缓存验证码String cacheCaptcha = stringRedisTemplate.opsForValue().get("login:captcha:" + captchaId);// 比较验证码是否正确if (cacheCaptcha == null || !cacheCaptcha.equalsIgnoreCase(userCaptcha)) {throw new CaptchaErrorException(ResultEnum.USER_CAPTCHA_ERROR);}// 判断用户是否存在User loginUser = new LambdaQueryChainWrapper<>(userMapper).select(User::getId, User::getUserAccount, User::getPassword,User::getUserName, User::getUserRole,User::getAvatar, User::getStatus).eq(User::getUserAccount, userLoginDTO.getUserAccount()).one();if (loginUser == null) {throw new AccountNotFoundException(ResultEnum.USER_NOT_EXIST);}log.info("loginUser: {}", loginUser);// 判断密码是否正确String md5Password = DigestUtils.md5DigestAsHex(userLoginDTO.getPassword().getBytes());if (!md5Password.equals(loginUser.getPassword())) {throw new PasswordErrorException(ResultEnum.USER_PASSWORD_ERROR);}// 判断用户状态是否正常if (!loginUser.getStatus()) {throw new AccountForbiddenException(ResultEnum.USER_ACCOUNT_FORBIDDEN);}// 生成tokenString token = jwtUtils.generateToken(Map.of("userId", loginUser.getId(),"userRole", loginUser.getUserRole()),"user");//构建响应对象return UserLoginVO.builder().userName(loginUser.getUserName()).avatar(loginUser.getAvatar()).token(token).build();}@Overridepublic UserLoginVO smsLogin(UserSmsLoginDTO userSmsLoginDTO) {// 校验验证码是否存在HashOperations<String, String, String> hashOps = stringRedisTemplate.opsForHash();String captcha = hashOps.get("login:sms:captcha:" + userSmsLoginDTO.getPhone(), "captcha");if (StringUtils.isEmpty(captcha)) {log.error("手机号 {} 的验证码不存在或已过期", userSmsLoginDTO.getPhone());throw new CaptchaErrorException(ResultEnum.USER_CAPTCHA_NOT_EXIST);}// 查询用户是否已注册User loginUser = new LambdaQueryChainWrapper<>(userMapper).eq(User::getPhone, userSmsLoginDTO.getPhone()).one();// 如果未注册则进行注册if (Objects.isNull(loginUser)) {loginUser = register(userSmsLoginDTO.getPhone());}// 校验验证码是否正确if (!userSmsLoginDTO.getCaptcha().equals(captcha)) {log.error("手机号 {} 的验证码错误", userSmsLoginDTO.getPhone());throw new CaptchaErrorException(ResultEnum.AUTH_CODE_ERROR);}//判断用户是否被禁用if (!loginUser.getStatus()) {throw new AccountForbiddenException(ResultEnum.USER_ACCOUNT_FORBIDDEN);}log.info("手机号 {} 用户登录成功", userSmsLoginDTO.getPhone());return UserLoginVO.builder().token(jwtUtils.generateToken(Map.of("userId", loginUser.getId()), "user")).userName(loginUser.getUserName()).build();}private User register(String phone) {User user = new User();user.setPhone(phone);user.setUserName(phone);user.setStatus(true);if (userMapper.insert(user) < 1) {log.error("手机号 {} 用户注册失败!", phone);throw new AccountRegisterFailException(ResultEnum.USER_REGISTER_FAIL);}log.info("手机号 {} 用户注册成功", phone);return user;}
}

感兴趣这两种登录功能专业的实现方法的可以看下:

spring boot3登录开发-3(1账密登录逻辑实现)_springboot3登录-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136124858?spm=1001.2014.3001.5501spring boot3登录开发-2(2短信验证码接口实现)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136888851?spm=1001.2014.3001.5501回到正题控制层依赖业务逻辑层,业务逻辑层则依赖下层mapper(DAO)层---数据访问层,

下面继续介绍mapper目录

mapper目录

该层存放数据访问接口类通常只需要定义出接口具体的操作数据库的逻辑是借助ORM(对象关系映射)框架---mybatis/mybatis-plue/jpa等来快捷编写或者直接生成的。

例如UserMapper、UserMapper.xml:

import com.mijiu.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;/*** <p>* 用户表 Mapper 接口* </p>** @author 蒾酒* @since 2024-02-03*/
@Mapper
public interface UserMapper extends BaseMapper<User> {}
<?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.mijiu.mapper.UserMapper"><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="com.mijiu.entity.User"><id column="id" property="id" /><result column="user_name" property="userName" /><result column="password" property="password" /><result column="user_account" property="userAccount" /><result column="user_role" property="userRole" /><result column="avatar" property="avatar" /><result column="create_time" property="createTime" /><result column="update_time" property="updateTime" /><result column="is_delete" property="isDelete" /><result column="gender" property="gender" /></resultMap></mapper>

因为我用的是mybatis-plus框架,不需要写mapper,框架本身提供的一组通用mapper也够用,

如果用的是mybatis的话就需要写数据访问接口了

@Mapper
public interface UserMapper extends BaseMapper<User> {//根据账号密码查询用户User selectUserByNameAndPassword(User user);
}
<?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.mijiu.mapper.UserMapper"><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="com.mijiu.entity.User"><id column="id" property="id" /><result column="user_name" property="userName" /><result column="password" property="password" /><result column="user_account" property="userAccount" /><result column="user_role" property="userRole" /><result column="avatar" property="avatar" /><result column="create_time" property="createTime" /><result column="update_time" property="updateTime" /><result column="is_delete" property="isDelete" /><result column="gender" property="gender" /></resultMap><!--    根据账号密码查询用户--><select id="selectUserByNameAndPassword" resultMap="BaseResultMap">SELECT * FROM user WHERE user_account = #{userAccount} AND password = #{password}</select>
</mapper>

数据访问层依赖实体类层,去做属性映射接收sql执行返回数据集。下面继续介绍最后一层entity目录

entity目录

这个目录存放的Entity类通常与数据库表中的记录(Row)对应,它们之间存在一一对应的关系。

@Data
@TableName("user")
@ApiModel(value = "User对象", description = "用户表")
public class User implements Serializable {@Serialprivate static final long serialVersionUID = 1L;@ApiModelProperty("主键")@TableId(value = "id", type = IdType.AUTO)private Long id;@ApiModelProperty("用户昵称")@TableField("user_name")private String userName;@ApiModelProperty("密码")@TableField("password")private String password;@ApiModelProperty("账号")@TableField("user_account")private String userAccount;@ApiModelProperty("用户角色:user / admin")@TableField("user_role")private String userRole;@ApiModelProperty("头像")@TableField("avatar")private String avatar;@ApiModelProperty("创建时间")@TableField("create_time")private LocalDateTime createTime;@ApiModelProperty("更新时间")@TableField("update_time")private LocalDateTime updateTime;@ApiModelProperty("逻辑删除:1删除/0存在")@TableField("is_delete")private Boolean isDelete;@ApiModelProperty("性别")@TableField("gender")private Boolean gender;@ApiModelProperty("状态:1正常0禁用")@TableField("status")private Boolean status;@ApiModelProperty("手机号")@TableField("phone")private String phone;
}

test目录

主要用来放mapper层、service层的测试用例类

例如UserMapperTest:

@SpringBootTest
public class UserMapperTest {@Autowiredprivate UserMapper userMapper;@MockBeanprivate BaseMapper<User> baseMapper;@Testpublic void testSelectUserByNameAndPassword() {// 创建一个模拟的User对象,用于作为参数传入方法中User user = new User();user.setUserName("test");user.setPassword("password");// 创建一个模拟的查询结果User expectedResult = new User();expectedResult.setId(1L);expectedResult.setUserName("test");expectedResult.setPassword("password");// 模拟BaseMapper的行为,当调用其selectOne方法时,返回模拟的结果when(baseMapper.selectOne(new QueryWrapper<User>().eq("username", "test").eq("password", "password"))).thenReturn(expectedResult);// 调用被测试的方法User result = userMapper.selectUserByNameAndPassword(user);// 断言结果是否符合预期assertEquals(expectedResult, result);}
}

写在最后

spring boot3单模块项目工程搭建-上(个人开发模板)。任何问题评论区或私信讨论,欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/314845.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Java EE] 多线程(四):线程安全问题(下)

1.5 volatile关键字 我们在了解这个关键字之前,我们首先要把产生线程安全的第4个原因补齐,我们来说说由于内存可见性引起的线程安全问题. 我们来看下面这样一段代码: import java.util.Scanner;public class Demo16 {public static int count 0;public static void main(Str…

HTML随机点名程序

案例要求 1.点击点名按钮&#xff0c;名字界面随机显示&#xff0c;按钮文字由点名变为停止 2.再次点击点名按钮&#xff0c;显示当前被点名学生姓名&#xff0c;按钮文字由停止变为点名 案例源码 <!DOCTYPE html> <html lang"en"> <head> <m…

动态规划——斐波那契数列模型:746.使用最小花费爬楼梯

文章目录 题目描述算法原理解法一1.状态表示2.状态转移方程3.初始化4.填表顺序5.返回值 解法二1.状态表示2.状态转移方程3.初始化4.填表顺序5.返回值 代码实现解法一&#xff1a;C解法一&#xff1a;Java解法二&#xff1a;C解法二&#xff1a;Java 题目描述 题目链接&#xf…

K8S探针分享

一&#xff0c;探针介绍 1 探针类型 livenessProbe&#xff1a;存活探针&#xff0c;用于判断容器是不是健康&#xff1b;如果探测失败&#xff0c;Kubernetes就会重启容器。 readinessProbe&#xff1a;就绪探针&#xff0c;用于判断是否可以将容器加入到Service负载均衡池…

STM32H7使用FileX库BUG,SD卡挂载失败

问题描述&#xff1a; 使用STM32H7ThreadXFileX&#xff0c;之前使用swissbit牌的存储卡可正常使用&#xff0c;最近项目用了金士顿的存储卡&#xff0c;发现无法挂载文件系统。 原因分析&#xff1a; 调试过程发现&#xff0c;关闭D-Cache可以挂载使用exfat文件系统。 File…

接口测试全流程扫盲

扫盲内容&#xff1a; 1.什么是接口&#xff1f; 2.接口都有哪些类型&#xff1f; 3.接口的本质是什么&#xff1f; 4.什么是接口测试&#xff1f; 5.问什么要做接口测试&#xff1f; 6.怎样做接口测试&#xff1f; 7.接口测测试点是什么&#xff1f; 8.接口测试都要掌…

pytest-xdist:远程多主机 - 分布式运行自动化测试

简介&#xff1a;pytest-xdist插件使用新的测试执行模式扩展了pytest&#xff0c;最常用的是在多个CPU之间分发测试以加快测试执行&#xff0c;即 pytest -n auto同时也是一个非常优秀的分布式测试插件&#xff0c;分别支持ssh和socket两种方式实现master和worker的远程通讯。…

Linux 第十一章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

Ubuntu终端常用指令

cat cat 读取文件的内容 1、ls 一、 1、ll 显示当前目录下文件的详细信息,包括读写权限,文件大小,文件生成日期等(若想按照更改的时间先后排序,则需加-t参数,按时间降序(最新修改的时间排在最前)执行: $ ll -t, 按时间升序执行: $ ll -t | tac): ll 2、查看当前所处路径(完整…

自然语言处理: 第二十八章大模型基底之llama3

项目地址: meta-llama/llama3: The official Meta Llama 3 GitHub site 前言 LLaMa系列一直是人们关注的焦点&#xff0c;Meta在4月18日发布了其最新大型语言模型 LLaMA 3。该模型将被集成到其虚拟助手Meta AI中。Meta自称8B和70B的LLaMA 3是当今 8B 和 70B 参数规模的最佳模…

【oj题解】二分算法、二分答案

1909 - 跳石头 题目描述 一年一度的“跳石头”比赛又要开始了! 这项比赛将在一条笔直的河道中进行&#xff0c;河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间&#xff0c;有 N 块岩石&#xff08;不含起点和终点的岩石&#xf…

Qt:学习笔记一

一、工程文件介绍 1.1 main.cpp #include "widget.h" #include <QApplication> // 包含一个应用程序类的头文件 //argc&#xff1a;命令行变量的数量&#xff1b;argv&#xff1a;命令行变量的数组 int main(int argc, char *argv[]) {//a应用程序对象&…

朴素贝叶斯算法分类

def loadDataSet():postingList[[my, dog, has, flea, problems, help, please], #切分的词条[maybe, not, take, him, to, dog, park, stupid],[my, dalmation, is, so, cute, I, love, him],[stop, posting, stupid, worthless, garbage],[mr, licks, ate, my, steak, …

Linux - tar (tape archive)

tar 的全称是 Tape Archive。它最初是在 Unix 系统中用于将数据写入磁带的工具&#xff0c;但现在它通常用于创建、维护、修改和提取文件的归档文件。尽管 tar 可以用于压缩和解压缩文件&#xff0c;但它本身并不进行压缩&#xff0c;而是通常与 gzip 或 bzip2 等压缩工具一起使…

记录——FPGA的学习路线

文章目录 一、前言二、编程语言2.1 书籍2.2 刷题网站2.3 仿真工具 三、基础知识3.1 专业基础课3.2 fpga相关专业知识 四、开发工具五、动手实验 一、前言 也不是心血来潮想学习fpga了&#xff0c;而是祥哥还有我一个国科大的同学都在往fpga这个方向走 并且看过我之前文章的同…

springboot+springsecurity+vue前后端分离权限管理系统

有任何问题联系本人QQ: 1205326040 1.介绍 优秀的权限管理系统&#xff0c;核心功能已经实现&#xff0c;采用springbootvue前后端分离开发&#xff0c;springsecurity实现权限控制&#xff0c;实现按钮级的权限管理&#xff0c;非常适合作为基础框架进行项目开发。 2.效果图…

EureKa技术解析:科技行业的革新风暴(ai写作)

首先&#xff0c;这篇文章是基于笔尖AI写作进行文章创作的&#xff0c;喜欢的宝子&#xff0c;也可以去体验下&#xff0c;解放双手&#xff0c;上班直接摸鱼~ 按照惯例&#xff0c;先介绍下这款笔尖AI写作&#xff0c;宝子也可以直接下滑跳过看正文~ 笔尖Ai写作&#xff1a;…

【Hadoop】-Apache Hive使用语法与概念原理[15]

一、数据库操作 创建数据库 create database if not exists myhive; 使用数据库 use myhive; 查看数据库详细信息 desc database myhive; 数据库本质上就是在HDFS之上的文件夹。 默认数据库的存放路径是HDFS的&#xff1a;/user/hive/warehouse内 创建数据库并指定hdfs…

盛水最多的容器 ---- 双指针

题目链接 题目: 分析: 最大容积 即使就是最大面积, 长为下标之差, 宽为两下标对应值的最小值解法一: 暴力枚举: 将每两个数之间的面积都求出来, 找最大值, 时间复杂度较高解法二: 假设我们的数组是[6, 2, 5, 4], 我们先假设最左边和最右边, 即6 和 4 之间是最大面积长a*宽b此…

Xcode隐私协议适配

1. Privacy manifest files 1.1 简介 自己App或三方SDK&#xff08;通过XCFrameworks|Swift packages|Xcode projects集成的&#xff09;需要包含一个隐私清单文件&#xff08;privacy manifest&#xff09;叫作 PrivacyInfo.xcprivacy。它是一个属性列表&#xff0c;记录了A…