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.mzph.cn/diannao/4070.shtml

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

相关文章

uniapp视频播放器(h5+app)

关于uniapp视频播放器遇到的一些问题&#xff0c;mark下。 中途遇到了很多问题&#xff0c;如果有相同的伙伴遇到了类似的&#xff0c;欢迎交流 官方的video播放器在app上不友好&#xff0c;有以下功能不支持。 loadedmetadata、controlstoggle不支持导致只能手写控制层。 不…

Linux实验一:Linux环境及编程工具

目录 一、实验目的二、实验内容三、参考代码四、实验步骤步骤1. 编辑源代码test1.c步骤2. 编译源代码test1.c步骤3. 调试test1步骤4. 重新编译运行test1.c 五、实验结果六、实验总结 一、实验目的 1、掌握Linux C开发过程中的基本概念&#xff1b; 2、掌握如vim&#xff0c;GC…

keepalived检测Nginx高可用

node1 192.168.136.55 node2 192.168.136.56 两台机器都安装dnf install keepalived nginx [rootnode1 ~]# echo "web test page, ip is hostname -I." > /usr/share/nginx/html/index.html [rootnode2 ~]# echo "web test page, ip is hostname -I."…

【科学研究】农村出身:一种复杂的情感结构

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

LeetCode 面试题 17.08 —— 马戏团人塔

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 首先&#xff0c;我们对人的身高按照从小到大排序&#xff0c;特别注意&#xff0c;对于身高相等的人&#xff0c;要按照体重从高到低排序。这时候&#xff0c;序列已经满足了在上面的人要比下面的人矮一点&#…

在开发软件以便未来本地化到其他语言时需要考虑的事项

我们准备了一份关于开发软件以便未来本地化到其他语言时需要考虑的事项的简要指南。这非常重要&#xff0c;因为您的软件在其他国家市场上的销售可能会带来比本国市场更多的收入。 在开发软件时考虑到未来本地化到其他语言的一些重要方面包括&#xff1a; 设计多语言支持&…

有什么因素会影响IP稳定性?

IP稳定性指的是IP地址在一段时间内保持不变的能力&#xff0c;对于网络连接的安全性和可靠性至关重要。以下是一些可能影响IP稳定性的主要因素&#xff1a; 网络服务提供商&#xff08;ISP&#xff09;的政策&#xff1a;不同的ISP对于IP地址的管理和使用有不同的政策。一些IS…

springboot+thymeleaf实现一个简单的监听在线人数功能

功能步骤&#xff1a; 1. 当用户访问登录页面时&#xff0c;Logincontroller的showLoginForm方法被调用&#xff0c;返回登录页面的视图名字。 2. 用户提交表单&#xff0c;调用LoginController的login方法。 3.login方法 4.登录验证通过&#xff0c;home方法会被调用&#xf…

C++解方程组的库

解决多元多次方程组的问题&#xff0c;你可以考虑以下几个C库&#xff1a; Eigen: Eigen库是一个高性能的C模板库&#xff0c;用于线性代数运算。它提供了强大的矩阵运算功能&#xff0c;可以用来解多元一次方程组。对于多次方程组&#xff0c;你可能需要结合Eigen和一些数值优…

C++笔试强训day7

目录 1.字符串中找出连续最长的数字串 2.岛屿数量 3.拼三角 1.字符串中找出连续最长的数字串 链接 我的思路很简洁&#xff0c;就是双指针遍历&#xff0c;然后不断更新左位置left和右位置right和长度len。 然后我写代码的时候代码思路没跟上原本思路&#xff0c;直接把所有…

HTTP网络协议,接口请求的内容类型 content-type(2024-04-27)

1、简介 Content-Type&#xff08;内容类型&#xff09;&#xff0c;一般是指网页中存在的 Content-Type&#xff0c;用于定义网络文件的类型和网页的编码&#xff0c;决定浏览器将以什么形式、什么编码读取这个文件&#xff0c;这就是经常看到一些 PHP 网页点击的结果却是下载…

OSI七层模型、TCP/IP五层模型理解(个人解读,如何理解网络模型)

OSI七层模型 七层模型&#xff0c;亦称OSI&#xff08;Open System Interconnection&#xff09;。参考模型是国际标准化组织&#xff08;ISO&#xff09;制定的一个用于计算机或通信系统间互联的标准体系&#xff0c;一般称为OSI参考模型或七层模型。它是一个七层的、抽象的模…

航片卫星影像局部几何纠偏-道路错位校正

原图道路错位如下图所示&#xff1a; 局部几何纠偏结果如下图所示&#xff1a; 操作视频教程&#xff1a; MCM智拼图软件V8.5-局部位置纠偏-几何校正-PS联动_哔哩哔哩_bilibili

11 c++版本的贪吃蛇

前言 呵呵 这大概是 大学里面的 c 贪吃蛇了吧 有一些 面向对象的理解, 但是不多 最近 因为想要 在单片机上面移植一下 贪吃蛇, 所以 重新拿出了一下 这份代码 然后 将它更新为 c 版本, 还是 用了一些时间 这里 具体的实现 就不赘述, 仅仅是 发一下代码 以及 具体的使用…

上位机图像处理和嵌入式模块部署(树莓派4b用skynet实现进程通信)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们说过&#xff0c;在工业系统上面一般都是使用多进程来代替多线程。这后面&#xff0c;主要的原因还是基于安全的考虑。毕竟一个系统里面&a…

北斗引路,太阳为源,定位报警,保护渔业,安全护航!

2022年1月&#xff0c;农业农村部发布《“十四五”全国渔业发展规划》明确提出&#xff0c;到2025年&#xff0c;渔业质量效益和竞争力明显增强&#xff0c;渔业基础设施和装备条件明显改善&#xff0c;渔业治理体系和治理能力现代化水平明显提高&#xff0c;实现产业更强、生态…

用Excel做一个功能完备的仓库管理系统

1 基本设计思路 用到的Excel技术&#xff1a;sumif, vlookup, 表格(table)。基本思路&#xff1a;在有基础的商品、仓库等信息的情况下&#xff0c;对商品的每一个操作都有对应的单据&#xff0c;然后再汇总统计。标识&#xff1a;为了在不同的维度统计数量&#xff0c;各单据…

HarmonyOS开发案例:【使用List组件实现设置项】

介绍 在本篇CodeLab中&#xff0c;我们将使用List组件、Toggle组件以及Router接口&#xff0c;实现一个简单的设置页&#xff0c;点击将跳转到对应的详细设置页面。效果图如下&#xff1a; 相关概念 [CustomDialog]&#xff1a;CustomDialog装饰器用于装饰自定义弹窗。[List]…

Linux 权限与软件包管理器 yum

一. 研究Linux默认权限 目录 &#xff0c;起始权限&#xff1a;777 普通文件&#xff0c;起始权限666 Linux系统中存在权限掩码 使用umask指令也可以改变掩码 如果将掩码改为0000 我们可以看到权限发生改变&#xff08;重新设置掩码&#xff09; 最终权限起始权限 去掉 权限…

[华为OD] 给航天器一侧加装长方形或正方形的太阳能板 100

给航天器一侧加装长方形或正方形的太阳能板&#xff08;图中的红色斜线区域&#xff09;&#xff0c;需要先安装两个支 柱&#xff08;图中的黑色竖条&#xff09;&#xff0c;再在支柱的中间部分固定太阳能板。但航天器不同位置的支柱长度 不同&#xff0c;太阳能板的安装面…