上一小节中我们从0到1 使用Vite搭建了一个Vue3项目,并集成了Element Plus 实现了一个简单的增删改查页面。
这一篇中我们将使用IDEA快速搭建一个SpringBoot3.x的项目。
1、File->new->project
2、选择“Spring Initializr”,点击next;
3、选择spring boot版本及添加相关依赖
这一步我们需要选择springboot版本,及Dependencies信息,当然了Dependencies 不是必须选择的。可以在项目建好之后需要什么添加什么。
简要说明:我们这里选择了四个Dependencies
4、创建好的项目结构
5、配置Maven
在这里配置本地Maven本地路径、Maven仓库。在本地Maven的settings.xml中会配置maven的镜像资源等信息
6、 下载相关依赖
在这里我们下载之前配置好的依赖,一般项目创建后会默认下载
在实际开发中,我们一般都会有好几套运行环境。比如①开发环境 ②测试环境 ③ 生产环境等等
我们不可能每次都去修改一个配置文件,这就显得很麻烦。下面我们主要说一说怎么配置多环境
① 修改application.properties 配置文件
idea创建的springboot项目,默认的配置文件是application.properties。我们这里将application.properties修改成application.yml。
为什么这么修改呢?因为yml文件配置起来比较方便,可以省略好多冗余内容,当然了你不改也是没啥问题的。
修改后的applicatio.yml文件如下,通过active:属性来切换环境
spring:
application:
name: springbootdemo
profiles:
active: dev
②添加开发环境配置
新建application-dev.yml 文件,在这里我们就可以配置开发环境下的端口,数据库连接等信息,具体内容如下
server.port
属性,指定应用程序在服务器上监听的端口号。这里设置为8080spring.datasource
属性,配置应用程序与数据库的连接
driver-class-name
指定数据库驱动程序的类名url
指定数据库的连接地址,这里是本地MySQL数据库的地址和端口号以及数据库名称username
和 password
分别指定连接数据库所需的用户名和密码spring.mybatis.mapper-locations
属性,指定MyBatis映射器XML文件的位置。这里配置为 classpath:mapper/*.xml ,表示映射器文件位于classpath下的mapper目录中server:
port: 8080 # 配置端口为8080
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3308/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
mybatis:
mapper-locations: classpath:mapper/*.xml
③ 添加生产环境配置
新建application-prod.yml文件,这里配置的是生产环境。具体内容如下,配置一样的,只是各种换成了生产的
server:
port: 8989
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://www.xiezhrspace.cn:3308/mybatisdemo?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
mybatis:
mapper-locations: classpath:mapper/*.xml
以上配置好之后,我们就可以启动springboot项目
浏览器输入:http://localhost:8080 后出现如下内容,说明项目启动成功
通过之前的步骤,我们的springboot项目已经可以正常运行起来了,接下来我们就从数据库中获取数据并通过json格式返回前台
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(0) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户密码',
`sex` enum('1','2') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户性别',
`birthday` date NULL DEFAULT NULL COMMENT '用户生日',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户邮箱',
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户电话',
`addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户地址',
`stop_flag` enum('1','0') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户启用标志',
`create_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '用户创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '用户更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `sys_user` VALUES (1, 'xiezhr', '程序员小凡', '12345678', '1', '1999-09-19', '[email protected]', '13288888888', '云南省昆明市', '0', '2023-09-04 21:08:32', NULL);
INSERT INTO `sys_user` VALUES (2, 'xiaoqi', '程序员晓柒', '123456', '1', '2020-10-04', '[email protected]', '13288888888', '云南文山', '0', '2023-09-04 21:09:42', NULL);
INSERT INTO `sys_user` VALUES (3, 'xiaodeng', '财务小邓', '123456', '2', '2019-09-04', '[email protected]', '13588888888', '云南文山', '0', '2023-09-04 21:10:43', NULL);
创建与表结构一致的实体类SysUser
@Data 是Lombok依赖的注解,使用它我们就不用写get、set方法了
@Data
public class SysUser implements Serializable {
private static final long serialVersionUID = 526934774547561999L;
/**
* 主键id
*/
private Integer id;
/**
* 用户名
*/
private String username;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户密码
*/
private String password;
/**
* 用户性别
*/
private String sex;
/**
* 用户生日
*/
private Date birthday;
/**
* 用户邮箱
*/
private String email;
/**
* 用户电话
*/
private String phone;
/**
* 用户地址
*/
private String addr;
/**
* 用户启用标志
*/
private String stopFlag;
/**
* 用户创建时间
*/
private Date createTime;
/**
* 用户更新时间
*/
private Date updateTime;
}
数据访问对象,是MVC架构中负责与数据库进行交互的组件。它封装了数据库的访问操作,提供给Service层调用。Dao层通常包含一系列方法,用于对数据库进行增删改查操作,以及与数据库的连接、事务管理等。
@Mapper 表示这个接口是一个MyBatis的Mapper接口,用于定义数据库操作的方法
@Mapper
public interface SysUserMapper {
List<SysUser> querySyserList();
}
MyBatis的映射文件(mapper),用于操作数据库中的sys_user表。其中定义了一个resultMap用于映射查询结果到SysUser对象,还定义了一个select语句用于查询sys_user表中的所有用户信息。 id=“querySyserList” 必须与mapper接口中方法名一致
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiezhr.mapper.SysUserMapper">
<resultMap type="com.xiezhr.model.entity.SysUser" id="SysUserMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="nickname" column="nickname" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="sex" column="sex" jdbcType="VARCHAR"/>
<result property="birthday" column="birthday" jdbcType="TIMESTAMP"/>
<result property="email" column="email" jdbcType="VARCHAR"/>
<result property="phone" column="phone" jdbcType="VARCHAR"/>
<result property="addr" column="addr" jdbcType="VARCHAR"/>
<result property="stopFlag" column="stop_flag" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
resultMap>
<select id="querySyserList" resultMap="SysUserMap">
select * from sys_user
select>
mapper>
Service是MVC架构中负责处理业务逻辑的组件。它封装了业务逻辑的实现细节,提供给Controller调用。Service层通常包含一系列方法,用于处理各种业务需求,如数据处理、事务管理、业务规则校验等。
SysUserService
public interface SysUserService {
List<SysUser> querySyserList();
}
SysUserServiceImpl
@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserMapper userMapper;
@Override
public List<SysUser> querySyserList() {
return userMapper.querySyserList();
}
}
Controller是MVC架构中负责接收用户请求并处理的组件。它接收来自用户的请求,并根据请求的内容调用相应的Service方法进行业务处理,然后返回结果给用户。Controller通常负责路由请求、参数验证、调用Service等操作
SysUserController
@RestController
@RequestMapping("/sysUser")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@RequestMapping(value = "/querySysUser",method = RequestMethod.GET)
public List<SysUser> querySysUser(){
return sysUserService.querySyserList();
}
}
到此我们三大组件的代码都写完了,接下来我们来看看我们写好的接口
浏览器地址栏输入:http://localhost:8080/sysUser/querySysUser
为了保证所有接口返回的数据格式一致,减少重复代码编写。我们将对返回结果进行统一处理。
具体返回结果格式如下
{
"code": 200, // 状态码,表示请求的处理结果
"message": "请求成功", // 状态消息,对请求结果的简要描述
"data": { // 数据对象,用于存储具体的返回数据
"key1": "value1",
"key2": "value2"
}
}
code
:表示请求的处理结果,一般采用HTTP状态码或自定义的业务状态码message
:对请求结果的简要描述,通常是一个字符串data
:用于存储具体的返回数据,可以是一个对象、数组或其他类型的数据IResultCode
的接口它位于
com.xiezhr.common.result
包中,可以由不同的类来实现,实现一致且统一的结果码和消息的处理和返回
package com.xiezhr.common.result;
public interface IResultCode {
String getCode();
String getMsg();
}
getCode()
: 这个方法返回一个 String
类型的结果码getMsg()
: 这个方法返回一个 String
类型的结果消息ResultCode
定义了一个枚举类
ResultCode
,它实现了IResultCode
接口,并包含了一些常见的响应码和对应的消息。
@AllArgsConstructor
@NoArgsConstructor
public enum ResultCode implements IResultCode, Serializable {
SUCCESS("200","成功"),
NOT_FOUND("404","未找到"),
INTERNAL_SERVER_ERROR("500","服务器内部错误"),
;
private String code;
private String msg;
@Override
public String getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
}
定义系统中常见的响应码和对应的消息,用于表示不同的业务场景或操作的执行结果
每个枚举常量都包含一个 code
和一个 msg
,分别表示响应码和消息内容
枚举常量包括了一些常见的响应码,如 SUCCESS
表示成功, INTERNAL_SERVER_ERROR
服务器内部错误, NOT_FOUND
表示未找到
定义了一个名为
Result
的类,用于表示统一的响应结构体
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
private String code;
private String msg;
private T data;
public static<T> Result<T> success() {
return success(null);
}
public static<T> Result<T> success(T data) {
Result result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMsg(ResultCode.SUCCESS.getMsg());
result.setData(data);
return result;
}
public static <T> Result<T> error(String msg) {
Result<T> result = new Result<>();
result.setCode(ResultCode.ERROR.getCode());
result.setMsg(ResultCode.ERROR.getMsg());
return result;
}
}
到此,统一响应返回我们已经封装好了,我们来改造一下Controller中的代码看看效果
SysUserController
未改之前
@RequestMapping(value = "/querySysUser",method = RequestMethod.GET)
public List<SysUser> querySysUser(){
return sysUserService.querySyserList();
}
SysUserController
修改之后
@RequestMapping(value = "/querySysUser",method = RequestMethod.GET)
public Result querySysUser(){
return Result.success(sysUserService.querySyserList());
}
前端返回结果
从上面的返回结果,我们会发现将密码等敏感信息返回到了前端,这是非常不可取的
这时,我们就需要根据前端的需求,灵活地选择需要展示的数据字段
如果你还傻傻分不清PO BO VO DTO POJO DAO DO 这些概念,可以看看知乎的这篇文章,个人觉得还是说的比较清楚的了
https://www.zhihu.com/question/39651928/answer/2490565983
定义一个需要返回前端的VO
@Data
public class UserInfoVO {
/**
* 用户名
*/
private String username;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户性别
*/
private String sex;
/**
* 用户生日
*/
private Date birthday;
/**
* 用户邮箱
*/
private String email;
/**
* 用户电话
*/
private String phone;
/**
* 用户地址
*/
private String addr;
}
interface SysUserService
改造前
public interface SysUserService {
List<SysUser> querySyserList();
}
interface SysUserService
改造后
public interface SysUserService {
List<UserInfoVO> querySyserList();
}
SysUserServiceImpl
改造前
@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserMapper userMapper;
@Override
public List<SysUser> querySyserList() {
return userMapper.querySyserList();
}
}
SysUserServiceImpl
改造后
@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserMapper userMapper;
@Override
public List<UserInfoVO> querySyserList() {
List<UserInfoVO> userInfoVOList = new ArrayList<UserInfoVO>();
List<SysUser> sysUserList= userMapper.querySyserList();
for (SysUser sysUser : sysUserList) {
UserInfoVO userInfoVO = new UserInfoVO();
BeanUtils.copyProperties(sysUser,userInfoVO);
userInfoVOList.add(userInfoVO);
}
return userInfoVOList;
}
}
service 返回由原来的SysUser -->变成UserInfoVO
浏览器地址栏输入:http://localhost:8080/sysUser/querySysUser
这时返回前端的json数据中已经没有密码了
认真看文章的小伙伴可能已经发现了,前面返回的json数据中,日期是 “birthday”: “1999-09-18T16:00:00.000+00:00” 这样的
这样的日期可读性非常差,有没有什么方法可以格式化下日期呢?
其实呢,日期格式化非常简单,我们只需要在之前定义好的UserInfoVO
的日期属性上加上一个注解即可
import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
我们来测试一下
通过格式化的日期就是我们习惯的日期格式了
日常开发中,我们处理异常一般都会用到
try-catch
、throw
和throws
的方式抛出异常。这种方式不仅仅程序员处理麻烦,对用户来说也不太友好
我们都希望不用写过多的重复代码处理异常,又能提升用户体验。
这时候全局异常处理就显得很便捷很重要了
小伙伴们如果对异常还不熟悉的可以看看这篇文章
https://blog.csdn.net/rong09_13/article/details/128090748
Springboot对于异常的处理也做了不错的支持,它提供两个注解供我们使用
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value =Exception.class)
public String exceptionHandler(Exception e){
System.out.println("发生了一个异常"+e);
return e.getMessage();
}
}
我们在Result 类中添加如下两个方法来处理自定义异常和其他异常返回结果
//自定义异常返回的结果
public static <T> Result<T> bussinessErr(BusinessException e) {
Result<T> result = new Result<>();
result.setCode(e.getErrorCode());
result.setMsg(e.getErrorMsg());
result.setData(null);
return result;
}
//其他异常处理方法返回的结果
public static <T> Result<T> otherErr(ResultCode resultCode) {
Result<T> result = new Result<>();
result.setCode(resultCode.getCode());
result.setMsg(resultCode.getMsg());
result.setData(null);
return result;
}
新建com.xiezhr.exception包,并自定义异常类
public class BusinessException extends RuntimeException{
private String errorCode;
private String errorMsg;
public BusinessException() {
}
public BusinessException(String errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
我们自定义一个全局异常处理类,来处理各种异常,包括自己定义的异常和内部异常 。
这样可以简化不少代码,不用自己对每个异常都使用try,catch的方式来实现
我们在com.xiezhr.exception
包下面添加全局异常处理类GlobalExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public<T> Result<T> bizExceptionHandler(BusinessException e) {
return Result.bussinessErr(e);
}
/**
* 处理其他异常
*
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result exceptionHandler(Exception e) {
return Result.otherErr(ResultCode.INTERNAL_SERVER_ERROR);
}
}
我们在SysUserController 中添加如下代码来测试下异常,看看能不能捕获到
@RequestMapping("/getBusinessException")
public Result DeException(){
throw new BusinessException("400","我出错了");
}
@RequestMapping("/getException")
public Result Exception(){
Result result = new Result();
int a=1/0;
return result;
}
日志记录应用程序的运行状态,通过日志开发者可以更好的了解应用程序的运行情况
当系统出现bug时,也能通过日志快速定位问题和解决问题
① 常用日志框架
JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…
② 日志框架之间的关系
日志框架的设计类似于JDBC-数据库驱动的设计,提供了统一的接口抽象层,然后再由各个数据库厂商去实现它,
从而实现连接不同数据库(Oracle、MySQL、PostgreSQL、MongoD…)
日志门面(日志抽象层) | 日志实现 |
---|---|
JCL SLF4j jboss-logging |
Log4j JUL(java.util.logging) Log4j2 Logback |
我们需要需要选择一个日志门面 和日志实现
springboot 默认选用SLF4j 和Logback
① 日志输出分析
② 日志级别
日志级别由低到高如下
TRACE < DEBUG< INFO< WARN < ERROR
如果设置为 WARN
,则低于 WARN
的信息都不会输出
Spring Boot
中默认配置ERROR
、WARN
和INFO
级别的日志输出到控制台
怎么调整日志级别呢?
logging.level.root=DEBUG
java -jar your-application.jar --logging.level.root=DEBUG
③ 日志写到文件中
需在application.properties或application.yml配置文件中设置
logging.file
或logging.path
属性
logging.file
,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.log
logging.path
,设置目录,会在该目录下创建spring.log
文件,并写入日志内容,如:logging.path=/var/log
如果只配置 logging.file
,会在项目的当前路径下生成一个 xxx.log
日志文件。
如果只配置 logging.path
,在 /var/log
文件夹生成一个日志文件为 spring.log
注:二者不能同时使用,如若同时使用,则只有logging.file
生效
默认情况下,日志文件的大小达到10MB
时会切分一次,产生新的日志文件,默认级别为:ERROR、WARN、INFO
①第一种方法
我们参照官网:https://www.slf4j.org/manual.html 的例子即可
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
②第二种方法
第一种方法中,每次使用都要创建了一个名为
logger
的Logger对象,使用起来有点繁琐。这里我们引入注解方式实现
使用注解@Slf4j 需要安装lombok
插件
可以用{}
占位符来拼接字符串,而不需要使用+
来连接字符串。
@SpringBootTest
@Slf4j
class SpringbootdemoApplicationTests {
@Test
void testLog() {
String name = "xiezhr";
int age = 20;
log.info("name:{},age:{}", name, age);
}
}
日志输出如下:
2023-09-16T12:27:57.014+08:00 INFO 53792 --- [ main] c.x.s.SpringbootdemoApplicationTests : name:xiezhr,age:20
前面几节说的都是springboot基本日志配置,如果这些都不能满足我们的需求,我们就需要添加logback-spring.xml
官方推荐的配置文件进行配置
logback-spring.xml
中 配置了两个 分别是①输出到控制台②将日志写到文件中 并且使用 指定开发/生产环境配置
大家可以参考下面配置
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<property name="LOG_HOME" value="/logs/${APP_NAME}"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUGlevel>
filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}Pattern>
<charset>UTF-8charset>
encoder>
appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/log.logfile>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} -%5level ---[%15.15thread] %-40.40logger{39} : %msg%n%npattern>
<charset>UTF-8charset>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFOlevel>
filter>
appender>
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
root>
springProfile>
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
root>
springProfile>
configuration>
看到这,大家如果对日志还不太清楚的可以看看下面雷神讲的视频,
https://www.bilibili.com/video/BV1sb411H7Po
最终完整项目结构如下
本篇文章我们手把手创建了一个SpringBoot项目,并整合Mybatis实现了将数据库中数据通过json返回前端。
对返回json结果进行统一封装,异常的统一处理,日期时间的处理、开发/生产环境配置以及系统日志配置。
以上都是基于日常工作开发中的实际案例,希望对你有所帮助。
本期内容到此九结束了,希望对你有所帮助。我们下期再见~ (●’◡’●)