前言
框架知识是每个程序员都应该或多或少都要有所了解,作为后端开发更是以后进阶架构师必备的知识储备;以此为出发点,我们可以从搭建一个简单的后端框架开始,了解相关的技术点和搭建思路。
我们可以从创建项目、引入相关依赖、配置相关信息、测试四个方面来构思和搭建这个简单的后端框架,照例,废话不多说,整起ing。。。
创建项目
第一先创建一个springboot工程,到springboot的官方网站创建项目 Spring Initializr,如下界面配置,这里创建的项目会在pom.xml文件中设置了maven;设置好基本信息后就可以点击SHARE把项目下载下来,接着之后的开发了
我用的是idea,之后的操作都是基于idea的,用eclipse的小伙伴可能得注意下(不过也没什么太大的区别,干就完了,欧力给!!!!)
将解压的项目添加到开发工具中,稍等开发工具加载项目后第一步就算完成了(由于项目是在官网配置的,maven仓库地址是国外的,下载得慢的话可以换成国内镜像仓库或者用自己配好的maven仓库),项目结果如下图:
相关依赖的配置 spring-boot-starter-web
首先,我们搭建后台框架一般都是要为前端服务,所以得引入web依赖,spring-boot的web开发内嵌了Servlet和服务器,并集合Spring MVC来完成开发。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
lombok
Lombok 是一种 Java 实用开发工具,可用来帮助开发人员消除 Java 的冗长,对于简单的 Java 对象(实体),它通过注解的方式实现实体字段的Get、Set、ToString、构造等方法。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
test
test依赖用来对接口利用测试用例进行接口测试
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
以上的依赖都是基础依赖,接下来就开始依据功能来配置依赖 数据库
无论什么后端项目,运行的基础一定时数据,所以有数据库连接是必不可少的;至于什么数据库就可以依据个人需要选择了额,这里以mysql为例
(1)connector 数据库连接驱动
数据库连接工具这里我使用的是mybatis-plus,在之前已经有写过mybatis+Generator数据库连接以及代码自动生成的介绍和配置了,有兴趣的可以查看之前的文章内容
<!--连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 整合mybatisplus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
(2)数据库配置
配置文件有两种格式 yml 和 properties 两种格式,两种格式都可以用使用,按个人习惯使用即可
properties格式:
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/blog?serverTimezone=UTC&useSSL=true
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
yml格式(缩进的形式):
spring:
datasource:
username: root
url: jdbc:mysql://localhost:3306/blog?serverTimezone=UTC&useSSL=true
password: Tbkj1@3#21
driver-class-name: com.mysql.cj.jdbc.Driver
日志配置
日志类型有很多:JUL , JCL , Jboss-logging , logback , log4j , log4j2 , slf4j等等
这里就只是用springbooot中t默认的日志实现 slf4j+logback,并且再xml文件的形式来配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds" debug="false">
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="log" />
<property name="console_log_pattern"
value="%red(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{39}) - %gray(%msg%n)"/>
<property name="charset" value="UTF-8"/>
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<!-- 例如:如果此处配置了INFO级别,则后面其他位置即使配置了DEBUG级别的日志,也不会被输出 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>${console_log_pattern}</pattern>
</encoder>
</appender>
<!--输出文件,只记录INFO级别信息-->
<appender name="info_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/roll_info/logback.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>${console_log_pattern}</pattern>
<charset>${charset}</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 如果超过10MB就删除 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--输出到文件,只记录WARN级别信息-->
<appender name="warn_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
</appender>
<!--输出到文件,只记录ERROR级别信息-->
<appender name="error_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
</appender>
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG
可以包含零个或多个appender元素。
-->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="info_file" />
<appender-ref ref="warn_file"/>
<appender-ref ref="error_file"/>
</root>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
<logger>仅有一个name属性,
一个可选的level和一个可选的additivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
如果未设置此属性,那么当前logger将会继承上级的级别。
additivity:是否向上级logger传递打印信息,默认是true
-->
<!-- 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别:
-->
<logger name="com.hyh.logback.web.LogTestController" level="WARN" additivity="false">
<appender-ref ref="console"/>
<appender-ref ref="warn_file"/>
<appender-ref ref="error_file"/>
</logger>
<!-- 如果多环境开发可以用springProfile -->
<!--开发环境:打印控制台-->
<springProfile name="dev">
<!--可以输出项目中的debug日志,包括mybatis的sql日志-->
<logger name="com.hyh.logback.web" level="DEBUG">
<appender-ref ref="console"/>
</logger>
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG
可以包含零个或多个appender元素。
-->
<root level="INFO">
<appender-ref ref="console"/>
</root>
</springProfile>
</configuration>
在配置文件中配置路径记好了
logging:
config: classpath:config/logback-spring.xml
在开发过程中,我们为了方便查看我们写业务sql,这里吧mybatis的日志也配置在yml文件中
mybatis-plus:
configuration:
call-setters-on-nulls: true
cache-enabled: false
##### mybatis-plus打印完整sql(只适用于开发环境)
# 原生配置
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
统一返回体
前后端分离的开发过程中通常要对返回体进行统一的处理,方便开发人员的管理和后期的维护!也是间接提高了代码的规范性和健壮性!通常的统一返回体一般包括返回数据体(data),返回信息(message)和返回状态代码(code)
package com.myself.blog.config;
import lombok.Data;
/**
* @Description 系统统一返回实体
* @Author 消逝
* @CreatTime 2020/12/29 17:54
*/
@Data
public class Result<T> {
/** 状态代码 */
private Integer code;
/** 返回消息 */
private String message;
/** 返回实体 */
private T data;
public Result(){}
/** 构造方法 */
public Result(Integer code,String message,T data){
this.code = code;
this.message = message;
this.data = data;
}
/** 成功返回 */
public Result success(T data){
Result result = new Result();
result.setCode(100);
result.setData(data);
return result;
}
/** 错误返回 */
public Result error(Integer code,String message,T data){
Result result = new Result();
result.setCode(code);
result.setData(data);
result.setMessage(message);
return result;
}
/** 错误返回 */
public Result error(Integer code,String message){
Result result = new Result();
result.setCode(code);
result.setData("");
result.setMessage(message);
return result;
}
/** 错误返回 */
public Result error(ErrorState errorState){
Result result = new Result();
result.setCode(errorState.getCode());
result.setData("");
result.setMessage(errorState.getMsg());
return result;
}
}
有了返回实体!还要对状态代码进行规划!规定几种状态代码
public enum ErrorState {
PARAMETER_ERROR(202,"参数错误"),
SYSTEM_ERROR(400,"系统错误"),
UNKNOWN_ERROR(404,"未知错误");
private Integer code;
private String msg;
ErrorState(){}
ErrorState(Integer code,String msg){
this.code = code;
this.msg = msg;
}
//省去Get和Set
}
统一异常处理
异常的处理也是需要的,将出现的所有异常都用固定一个自定义异常类接收处理,在config文件夹下创建自定义异常处理类
public class BlogException extends RuntimeException {
private Integer code;
//异常呢处理逻辑,具体可以依据需要添加修改
public BlogException(Integer code,String msg){
super(msg);
this.code = code;
}
public BlogException(ErrorState errorState){
super(errorState.getMsg());
this.code = errorState.getCode();
}
}
//当有自定义异常被调用时就会自动封装一个返回信息将错误信息回传
@ControllerAdvice
public class BlogExceptionHandler {
@ExceptionHandler(BlogException.class)
@ResponseBody
public Result result(BlogException e){
return new Result().error(e.getCode(),e.getMessage());
}
}
swagger
相信无论是前端还是后端开发,都或多或少地被接口文档折磨过;swagger就是方便对接口管理和测试的,Swagger提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
在配置文件创建配置文件并添加以下配置
@Configuration
@EnableSwagger2
@ConditionalOnProperty(prefix = "swagger",value = {"enable"},havingValue = "true")
public class SwaggerConfig {
/**
* 配置swagger
* @return
*/
@Bean
public Docket createRestApi(){
//指定api类型为swagger2
return new Docket(DocumentationType.SWAGGER_2)
// 调用接口装载文档信息
.apiInfo(apiInfo())
.select()
// 指定controller包,也可将不设置 any表示所有Controller
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
/**
* 文档信息配置
* @return
*/
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
// 文档页标题
.title("blog API 文档")
// 详细信息
.description("用于测试和记录系统API接口")
// 文档版本号
.version("1.0")
.build();
}
}
swagger仅在开发过程中使用,生产是不开放的,得在配置文件中设置控制开关
swagger:
enable: true //开启
多配置文件
一个项目基本的会有三种环境,开发dev,测试test,生产 prod。每个环境下的配置文件的内容都会不同,所以需要在不同环境下使用不同的配置文件。
profile 是spring用针对不同环境对不同的配置提供支持的,全局profile配置使用application-{profile}.properties,对于哪个配置会生效,需要在application.properties中通过spring.profiles.active属性来设置,其值对应{profile}值(yml文件也是一样的)
例如:application-dev = dev 为开发环境
//启用那个环境就改值即可
启用dev : spring.profiles.active=dev
测试
这样一来就差不多了,来学个列子测试下
//Controller层
@Api(value = "sysUser")
@RestController
@RequestMapping("/sysUser")
public class SysUserController {
private Logger logger = LoggerFactory.getLogger(SysUserController.class);
@Autowired
private SysUserService userService;
@ApiOperation(value = "获取用户列表")
@GetMapping("/getAll")
public Page<SysUser> getAll(){
return userService.getList();
}
@ApiOperation(value = "根据用户名称获取用户")
@GetMapping("/getUserByName")
public Result getUserByName(@RequestParam String name){
SysUser userInfo = userService.getOne(name);
return new Result().success(userInfo);
}
@ApiOperation(value = "测试")
@GetMapping("/test")
public Result getTest(){
return new Result().error(ErrorState.SYSTEM_ERROR);
}
@ApiOperation(value = "测试1")
@GetMapping("/test1")
public Result getTest1(){
throw new BlogException(ErrorState.UNKNOWN_ERROR);
}
}
//mapper
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}
//service
Page<SysUser> getList();
SysUser getOne(String name);
//impl
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper userMapper;
@Override
public Page<SysUser> getList() {
return userMapper.selectPage(new Page<>(), new QueryWrapper<SysUser>());
}
@Override
public SysUser getOne(String name) {
return userMapper.selectOne(new QueryWrapper<SysUser>().eq("name",name));
}
}
swagger测试结果,地址::8888/swagger-ui.html(端口为项目端口)
正确返参测试:
异常返参:
日志回显:
一个简单的后端框架就可以用可!哪来做做练手,或者做个小网站应该还是可以的!
文笔不好,不喜勿喷!若有错误请指出,会及时更改!一起进步,加油!!
暂无评论内容