如何开发一个Spring boot工程

devtools/2024/11/13 10:14:02/

引言

很多网站的后端都是基于Spring boot - mybatis进行开发的。
本文以此技术栈进行一个功能模块的开发

以最常见的注册功能为例

全部用最新的版本进行开发

工具

  1. 操作系统:Windows 10
  2. Java开发包:JDK 21
  3. 项目管理工具:Maven 3.6.3
  4. 项目开发工具:IntelliJ IDEA 2021.1.3 x64
  5. 数据库:Mysql
  6. 浏览器:Edge
  7. 服务器架构:Spring Boot 3.2.5 + MyBatis 3.0.3 + AJAX

部分坐标

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter-test</artifactId><version>3.0.3</version><scope>test</scope>
</dependency>

功能模块分析

本文开发的是注册模块,所以我们只会用一张表,其他模块思路与本文一致。

开发顺序

  • 我们应该遵循:数据库 -> 持久层 -> 业务层 -> 表现层 -> 前端页面 这样的顺序进行开发。

  • 功能上,我们应该先做基础功能,并遵循 增 -> 查 -> 删 -> 改的顺序来开发。

创建数据库

制作注册功能,本质是将用户的信息存放到数据库中。
我们建立一个数据库名为db

CREATE DATABASE db character SET utf8;

此时我们就需要考虑用户表中应该有哪些字段。

  • 首先应该有一个id并且这个id是唯一且不能为空,也就是主键,并且我们也可以为这个字段设置为自增字段。
  • 主要信息。在我们常用的软件上不难看出厂商保留了我们的哪些信息。
    如:用户名:username,密码:password。
  • 上面这两个是必不可少的,根据我们实际的项目,也可以保存其他的信息
    如:手机号:phone,电子邮箱:email,性别:sex,头像:avatar等。

此处我们就选用:自增主键id,用户名username,密码password,性别sex,手机号phone。

后续可能会有一些补充

建表语句:

CREATE TABLE `user` (`id` bigint NOT NULL,`username` varchar(32) NOT NULL,`password` varchar(32) NOT NULL,`sex` int,`phone` varchar(32) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

建表后在配置文件中连接数据库

此处使用yml类型的配置文件

spring:datasource:url: jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456

项目开发

此处开始我们代码的正式开发

创建实体类

我们在给数据库添加数据时,不能将字段全部用变量传入,这样很麻烦,也不方便后续的一些操作,所以我们会创建表对应的实体类对象
User

java">package com.angelday.test.entity;public class User {private Integer id;private String username;private String password;private Integer age;private String phone;/*后面是构造方法,Setter、Getter、toString方法由于可以使用IDEA自动生成,此处不再赘述*/
}

持久层

前面说过,注册的本质就是向数据库中添加一行数据
所以它的SQL语句是:

INSERT INTO db (username, password, age, phone) VALUES (值列表);

本项目使用的是mybatis进行开发
我们在根目录下创建一个包 mapper,并且在这个包下创建一个接口

java">package com.angelday.test.mapper;import com.angelday.test.entity.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper {/*** 添加用户* @param user 添加来的user对象* @return 受影响的行数*/Integer insertUser(User user);
}

并在resources包下创建一个包mapper用作UserMapper接口的映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.angelday.test.mapper.UserMapper"><!--id为对应接口的方法名,useGennerateadKeys开启自增,keyProperty指明自增字段--><insert id="insertUser" useGeneratedKeys="true" keyProperty="uid">INSERT INTO t_user(username, password, phone, sex)VALUES (#{username}, #{password}, #{phone}, #{sex})</insert>
</mapper>

只有这个配置文件SpringBoot并不能识别到它,所以我们应该在配置文件中指定扫描路径

mybatis:mapper-locations: classpath:mapper/*.xml

单元测试

昨晚持久层之后我们要利用Springboot提供的@SpringBootTest进行测试,测试一定不要省略,这样可以减小问题的范围。

java">package com.angelday.store;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class StoreApplicationTests {@Autowiredprivate UserMapper userMapper@Testvoid userMapperTest(){User user = new User();user.setUsername("Zhangsan");user.setPassword("123456");user.setSex(1);//一般都是规定1是男0是女user.setPhone("12332112321");//该方法返回int类型的整数表示受影响的行数int rows = insertUser(User user);}
}

执行这个方法之后去数据库中查看信息,如果信息没有任何问题,即可执行下一步。

业务层

包结构一般为根目录下建一个service包
service包中有impl和ex两个包分别装Service的实现类与业务层中可能出现的异常。

规划异常

我们的用户名并没有设置为唯一,所以没有常见的用户名重复异常,我们就建立一个最大的异常作为所有Service层中会发生的异常

java">package com.angelday.test.service.ex;/*** 业务异常基类,所有的业务层异常都继承这个异常*/
public class ServiceException extends RuntimeException{public ServiceException() {super();}public ServiceException(String message) {super(message);}public ServiceException(String message, Throwable cause) {super(message, cause);}public ServiceException(Throwable cause) {super(cause);}protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}
}

开发Service接口

我们在service包下建立一个UserService接口

java">package com.angelday.test.service;import com.angelday.test.entity.User;
import org.springframework.stereotype.Service;/*** 用户模块*/
@Service
public interface IUserService {/*** 用户注册* @param user*/void reg(User user);
}

实现类

在现实项目中一般我们的表中会有创建人创建时间修改人修改时间等字段用来记录日志,
而这些日志信息前端不会主动传来,所以我们会在业务层中补充这些字段

java">package com.angelday.store.service.impl;import ...@Service
public class UserServiceImpl implements IUserService {@Autowiredprivate UserMapper userMapper;/*** 用户注册* @param user 表单内容*/@Overridepublic void reg(User user){//将user中的信息完善如:创建人创建时间等user.setCreatedUser(user.getUsername());user.setModifiedUser(user.getUsername());Date date = new Date();user.setCreatedTime(date);user.setModifiedTime(date);//密码一般不会用明文的形式存储到数据库中,此处就用常见的md5加密,对密码加密//md5加密String password = user.getPassword();password = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));user.setPassword(password);//执行成功返回1Integer rows = userMapper.insertUser(user);if (rows != 1){throw new ServiceException("产生未知错误");}}
}

单元测试

记住,一定要测试!一定要测试!一定要测试!!!

java">@SpringBootTest
class StoreApplicationTests {@Autowiredprivate UserService userService;@Testvoid userServiceTest(){User user = new User();user.setUsername("Lisi");user.setPassword("654321");user.setSex(1);user.setPhone("12332112321");//该方法返回int类型的整数表示受影响的行数int rows = reg(User user);}
}

执行后检查数据库的数据是否正确

表现层

这里我们需要确定前端具体要访问什么路径,一般都是由项目经理提前订好了,此处我们就自定义一个user/reg路径,前端传来User对象,请求方式为POST

请求参数:/user/reg
请求参数:User user
请求类型:POST
响应结果:Result< Void>

约定响应结果

后端所有的响应结果都要封装到这个类中,它一般包含以下三个变量
1.状态码 2. 消息 3. 数据

com.angelday.test.util下建立Result

java">package com.angelday.test.util;/*** 统一返回结果类*/
public class JsonResult<E> {private Integer state;private String message;private E data;//构造方法 & Getter和Setter方法
}

Controller

根据与前端的约定,我们创建UserController

java">@RestController
@RequestMapping("users")
public class UserController{@Autowiredprivate UserService userService;/*** 用户注册* @param user 表单内的注册信息* @return 返回成功状态码*/@RequestMapping("reg")public Result<Void> reg(User user){userService.reg(user);return new Result<>(200);}
}

优化

写到这里发现:

  • 还有业务层的异常没有处理
  • 成功的状态码不应该直接我们自己写,而是由前后端约定好的值,声明为常量
集中处理异常,声明状态码与消息的常量

创建一个Controler的基类,所有的表现层都继承这个类,在这个类里我们进行集中处理异常,并且封装一些状态码和信息做为常量。

使用@ExceptionHandler可以集中处理异常

java">/*** 控制类的基类,负责管理通用的操作与数据*/
public class BaseController {public static final int OK = 200;/*** 集中处理异常* @param e 异常对象* @return 返回的json格式数据*/@ExceptionHandler({ServiceException.class,FileUploadException.class})public Result<Void> handlerException(Throwable e){Result<Void> result = new Result(e);if (e instanceof ServiceException){result.setState(1000);result.setMessage("在插入数据时发生未知错误");}}
}

最后我们让UserController继承BaseController

测试

此处可以使用PostMan或者直接用浏览器访问该路径

localhost:8080/users/reg?username=zhangsan&password=123456

如果数据成功添加到数据库中,本模块开发完成

前端页面

页面的组件此处不详细讲解,只是向大家说明,表单的idform-register,提交表单数据的按钮idbutton-register
大家只需要根据自己的表单id与按钮id修改名称即可。

AJAX请求

一般在body标签的结尾前添加script标签,用来添加函数

<body>
..........
<!--页面的组件--><script>javascript">//首先监听按钮是否被点击//此处使用id选择器,并且为其添加click事件$("#button-register").click(function (){//按钮点击后发送AJAX请求$.ajax({url: "/users/reg",type: "POST",// 使用serialize()方法会自动将表单json数据转化为username=Xxx的形式data: $("#form-register").serialize(),dataType: "JSON",//成功的回调函数success: function (json) {if (json.state == 200){alert("注册成功");} else {alert("注册失败");}},//失败的回调函数error: function (xhr) {alert("注册时产生未知错误,请稍后重新尝试!"+ xhr.status);}	});});</script>
</body>

单元测试

此处我们可以运行TestApplication主程序开启服务,并在页面填入数据进行提交,如果返回警告框:注册成功,并且数据库中的数据没有异常,整个模块功能的开发阶段,完成!

结语

如果有其他的问题欢迎私信我,后续有其他的优化:如:使用Redis、使用会话跟踪技术、拦截器/过滤器等,我会再创一个分栏。


http://www.ppmy.cn/devtools/34332.html

相关文章

车载电子电器架构 —— 如何理解和使用Update bit

车载电子电器架构 —— 如何理解和使用Update bit 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不…

【MySQL】4.MySQL的InnoDB引擎深度解析:事务、索引、MVCC、锁机制与性能优化等

InnoDB&#xff0c;作为MySQL数据库系统中的默认存储引擎&#xff0c;以其卓越的事务处理能力和对ACID属性的全面支持&#xff0c;成为了众多开发者和数据库管理员的首选。然而&#xff0c;要充分利用InnoDB的强大功能&#xff0c;就需要深入理解其内部机制&#xff0c;包括事务…

第2章 MySQL的调控按钮-启动选项和系统变量

原文链接&#xff1a; https://relph1119.github.io/mysql-learning-notes/#/mysql/02-MySQL%E7%9A%84%E8%B0%83%E6%8E%A7%E6%8C%89%E9%92%AE-%E5%90%AF%E5%8A%A8%E9%80%89%E9%A1%B9%E5%92%8C%E7%B3%BB%E7%BB%9F%E5%8F%98%E9%87%8F 我们可以发现在日常生活中大多数软件都有自…

vs2019 - astyle-extension 扩展插件的编译与使用

文章目录 vs2019 - astyle-extension 扩展插件的编译与使用概述笔记编译有警告run_vs2019.bat检查C#组件编译工程卸载旧版插件安装新版插件确认astyle-extension插件没有被VS2019禁止给astyle-extension插件在VS2019工具栏上加个按钮插件配置astyle-extension 插件的astyle的版…

论文阅读_使用有向无环图实现流程工程_AgentKit

英文名称: AgentKit: Flow Engineering with Graphs, not Coding 中文名称: AgentKit&#xff1a;使用图而非编码进行流程工程 链接: https://arxiv.org/pdf/2404.11483.pdf 代码: https://github.com/holmeswww/AgentKit 作者: Yue Wu, Yewen Fan, So Yeon Min, Shrimai Prabh…

c语言排序算法之五(插入排序)

前言 以下内容是被验证可以有效理解插入排序&#xff0c;代码也较容易理解。如果你发现还有很多需要增加的&#xff0c;欢迎留言。 为什么要单独写排序算法这一系列&#xff0c;看过一些贴子普遍篇幅较长。看完还依旧云里雾里&#xff0c;难以直观理解原理及整个过程。代码永…

【Osek网络管理测试】[TG1_TC7]tError

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 测试tErrore时间参数是否正确 本处规定tError范围在[950…

「PHP系列」PHP MySQL 简介及运用

文章目录 一、PHP MySQL 简介二、MySQL安装三、相关链接 一、PHP MySQL 简介 PHP MySQL是一个结合了PHP和MySQL的组合&#xff0c;用于在Web应用程序中处理数据库操作。 MySQL&#xff1a; 定义&#xff1a;MySQL是一个流行的开源关系型数据库管理系统&#xff08;RDBMS&…