springboot工具类
本文最后更新于:2 年前
javaweb中图片文件上传和下载
使用springboot创建web项目
服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:
commons-fileupload
commons-io
Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,实现步骤如下:
1.创建Controller
因为图片上传是一个公共接口所以可以创建一个名为CommonController
的文件,url地址为/common
/**
* 主要用于文件上传和下载
*/
@RestController
@RequestMapping("/common")
public class CommonController {
@Value("${reggie.path}") // 保存图片的路径
private String basePath;
}
上传图片
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
public R<String> upload(MultipartFile file){
// file是一个临时文件,后续需要进行转存
// file需要与前端上传的参数名保持一致,否则无法获取到上传的文件
// 转存之前需要对文件名进行处理
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
// 防止文件被覆盖,使用uuid作为文件名
String filename = UUID.randomUUID().toString();
filename = filename + suffix;
File dir = new File(basePath); // 判断文件夹是否存在不存在则创建
if (!dir.exists()){
dir.mkdir();
}
// 将图片保存的指定位置
try {
file.transferTo(new File(basePath + filename));
} catch (IOException e) {
e.printStackTrace();
}
return R.success(filename);
}
下载图片
@GetMapping("/download")
public void download(String name, HttpServletResponse response) throws IOException {
// 文件输入流读取本地文件
FileInputStream fileInputStream = null;
// 使用响应输出流将图片显示在网页上
ServletOutputStream outputStream = null;
try {
fileInputStream = new FileInputStream(new File(basePath + name));
outputStream = response.getOutputStream();
response.setContentType("image/jpeg");
byte[] bytes = new byte[1024];
int len = 0;
// 读取服务器上的图片后 由响应输出流
while( (len = fileInputStream.read(bytes)) != -1){
outputStream.write(bytes, 0, len);
outputStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
fileInputStream.close();
outputStream.close();
}
}
2.前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="../../plugins/element-ui/index.css" />
<link rel="stylesheet" href="../../styles/common.css" />
<link rel="stylesheet" href="../../styles/page.css" />
</head>
<body>
<div class="addBrand-container" id="food-add-app">
<div class="container">
<el-upload class="avatar-uploader"
action="/common/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeUpload"
ref="upload">
<img v-if="imageUrl" :src="imageUrl" class="avatar"></img>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="../../plugins/vue/vue.js"></script>
<!-- 引入组件库 -->
<script src="../../plugins/element-ui/index.js"></script>
<!-- 引入axios -->
<script src="../../plugins/axios/axios.min.js"></script>
<script src="../../js/index.js"></script>
<script>
new Vue({
el: '#food-add-app',
data() {
return {
imageUrl: ''
}
},
methods: {
handleAvatarSuccess (response, file, fileList) {
this.imageUrl = `/common/download?name=${response.data}`
},
beforeUpload (file) {
if(file){
const suffix = file.name.split('.')[1]
const size = file.size / 1024 / 1024 < 2
if(['png','jpeg','jpg'].indexOf(suffix) < 0){
this.$message.error('上传图片只支持 png、jpeg、jpg 格式!')
this.$refs.upload.clearFiles()
return false
}
if(!size){
this.$message.error('上传文件大小不能超过 2MB!')
return false
}
return file
}
}
}
})
</script>
</body>
</html>
mybatis-plus-generator
MP的代码生成器,可以节约创建文件的时间
1.导入依赖
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
2.编写生成器代码
可以放在专门的工具类包下
package com.sunzy.utils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
/**
* mp代码生成器
*/
public class CodeGenreator {
public static void main(String[] args) {
generator();
}
public static void generator(){
FastAutoGenerator.create("jdbc:mysql://localhost:3306/admin_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true",
"username", "password")
.globalConfig(builder -> {
builder.author("sunzy") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("E:\\Sunzh\\java\\admin_demo\\src\\main\\java\\"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.sunzy") // 设置父包名
.moduleName("") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "E:\\Sunzh\\java\\admin_demo\\src\\main\\resources\\mapper\\")); // 设置mapperXml生成路径 OutputEile.xml这里可能会报错 根据版本修改
})
.strategyConfig(builder -> {
builder.entityBuilder().enableLombok();
builder.mapperBuilder().enableMapperAnnotation().build();
builder.controllerBuilder().enableHyphenStyle() // 连字符转驼峰
.enableRestStyle(); // 开启rest控制器
builder.addInclude("sys_user") // 设置需要生成的表名
.addTablePrefix("t_", "sys_"); // 设置过滤表前缀
})
// .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
完成后,直接运行改工具类就可以直接生成以下文件:
UserMapper.java IUserService.java UserServiceImpl.java UserController.java
3.Controller模板
可以根据需要修改自己的模板
package ${package.Controller};
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
/**
* <p>
* $!{table.comment} 前端控制器
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end
@Resource
private ${table.serviceName} ${table.entityPath}Service;
// 新增或者更新
@PostMapping
public boolean save(@RequestBody ${entity} ${table.entityPath}) {
return ${table.entityPath}Service.saveOrUpdate(${table.entityPath});
}
@DeleteMapping("/{id}")
public Boolean delete(@PathVariable Integer id) {
return ${table.entityPath}Service.removeById(id);
}
@PostMapping("/del/batch")
public boolean deleteBatch(@RequestBody List<Integer> ids) {
return ${table.entityPath}Service.removeById(ids);
}
@GetMapping
public List<${entity}> findAll() {
return ${table.entityPath}Service.list();
}
@GetMapping("/{id}")
public ${entity} findOne(@PathVariable Integer id) {
return ${table.entityPath}Service.getById(id);
}
@GetMapping("/page")
public Page<${entity}> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize) {
LambdaQueryWrapper<${entity}> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc("id");
return ${table.entityPath}Service.page(new Page<>(pageNum, pageSize), queryWrapper);
}
}
#end
更多的模板可以到com.baomidou.mybatis-plus-generator
包中复制到项目的resources\templates
中,根据自己的需求修改模板
Swagger
1.导入依赖
<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>
2.创建配置类
再com.sunzy.fmmall.config.SwaggerConfig
路径下创建swagger的配置类
配置文档的封面信息 包括标题,版本,作者信息
修改需要扫面的controller包位置即可
package com.sunzy.fmmall.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* swagger可以自动生成接口文档
* 1.配置生成文档的信息
* 2.配置生成规则
*
*/
/**
* Docket用来封装接口文档
*
* @return
*/
@Bean
public Docket getDocket(){
//创建封面信息对象
ApiInfoBuilder apiInfoBuilder = new ApiInfoBuilder();
apiInfoBuilder.title("《锋迷商城》后端接口说明")
.description("此文档详细说明了锋迷商城项目后端接口规范....")
.version("v 2.0.1")
.contact( new Contact("sunzy","www.suzny.com","sunzy@wang.com") );
ApiInfo apiInfo = apiInfoBuilder.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo) //指定生成的文档中的封面信息:文档标题、版本、作者
.select()
.apis(RequestHandlerSelectors.basePackage("com.sunzy.fmmall.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
}
启动springboot项目访问项目地址/swagger-ui.html即可看到相关接口信息,并可以进行测试
3.swagger注解说明
Swagger提供一套详细的注解可以对接口进行详细的说明
@Api 类注解,对控制器类添加此注解可以对控制器进行功能说明
@Api(value = "用户管理", tags = "提供用户注册和登录服务")
@ApiOperation 方法注解,对每个方法作用进行详细说明
@ApiOperation("用户登录接口")
@ApiImplicitParams和@ApiImplicitParam 对方法中的参数进行详细的说明,包括字段名,备注,是否必须,以及默认值
@ApiImplicitParams(
@ApiImplicitParam(dataType = "string", name = "username", value = "用户账号", required = true),
@ApiImplicitParam(dataType = "string", name = "password", value = "用户密码", required = true)
)
@ApiModel 当接口参数和返回值类型为对象类型是需要添加此注解
@ApiModel(value = "ResultVO对象",description = "封装接口返回给前端的数据")
@ApiModelProperty 模型中的成员变量进行说明
@ApiModelProperty(value = "响应状态码",dataType = "int")
private int code;
@ApiIngnore接口方法注解,添加此注解的方法不会出现在接口文档中
4.swagger-ui插件
导入插件依赖
<!--swagger-ui插件依赖-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
访问url:http://localhost:8080/doc.html ,ui界面设计更加美观
tkMapper
整合tkmapper
1.导入依赖
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
2.配置application.yml文件
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/fmmall?characterEncoding=utf-8&useSSL=false
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
type-aliases-package: com.sunzy.fmmall.dao
3.修改启动类
注意MapperScan需要使用tkmapper包中的
package com.sunzy.tkmapper;
//import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
//@MapperScan("com.sunzy.tkmapper.dao")
@MapperScan("com.sunzy.tkmapper.dao")
public class TkmapperDemoApplication {
public static void main(String[] args) {
SpringApplication.run(TkmapperDemoApplication.class, args);
}
}
4.逆向工程
导入插件依赖
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.4.4</version>
</dependency>
</dependencies>
</plugin>
添加配置文件GeneratorConfig.xml
记得修改对应包名<!--**-->
标记处需要修改
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration
1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 引⼊数据库连接配置 -->
<!-- <properties resource="jdbc.properties"/>-->
<context id="Mysql" targetRuntime="MyBatis3Simple"
defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!-- 配置 GeneralDAO -->
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers"
value="com.sunzy.fmmall.general.GeneralDao"/><!--**-->
</plugin>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/fmmall?useSSL=false"
userId="root" password="root"><!--**-->
</jdbcConnection>
<!-- 配置实体类存放路径 -->
<javaModelGenerator
targetPackage="com.sunzy.fmmall.entity"
targetProject="src/main/java"/> <!--**-->
<!-- 配置 XML 存放路径 -->
<sqlMapGenerator targetPackage="/"
targetProject="src/main/resources/mapper"/> <!--**-->
<!-- 配置 DAO 存放路径 -->
<javaClientGenerator targetPackage="com.sunzy.fmmall.dao"
targetProject="src/main/java" type="XMLMAPPER"/> <!--**-->
<!-- 配置需要指定⽣成的数据库和表,% 代表所有表 -->
<table tableName="%">
<!-- mysql 配置 -->
<!-- <generatedKey column="id" sqlStatement="Mysql"
identity="true"/>-->
</table>
<!-- <table tableName="tb_roles">-->
<!-- <!– mysql 配置 –>-->
<!-- <generatedKey column="roleid" sqlStatement="Mysql"
identity="true"/>-->
<!-- </table>-->
<!-- <table tableName="tb_permissions">-->
<!-- <!– mysql 配置 –>-->
<!-- <generatedKey column="perid" sqlStatement="Mysql"
identity="true"/>-->
<!-- </table>-->
</context>
</generatorConfiguration>
创建com.sunzy.fmmall.general.GeneralDao
package com.sunzy.fmmall.general;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
public interface GeneralDao<T> extends Mapper<T>, MySqlMapper<T> {
}
在pom.xml中添加配置文件的位置
使用插件进行代码生成
JWT实现登录权限认证
1.导入JWT依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2.生成对应的token并返回给前端
// 验证成功则生成对应的token
// 使用jwt生成token
JwtBuilder builder = Jwts.builder();
Map<String, Object> map = new HashMap<>();
map.put("key1", "value2");
map.put("key2", "value2");
JwtBuilder jwtBuilder = builder.setSubject(username) //设置subject
.setIssuedAt(new Date()) // 设置token生成的时间
.setId(user.getUserId() + "") // 设置userid为token的唯一id
.setClaims(map) // map中可以存放用户的角色和权限信息
.setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000 * 2)) // 设置token的过期时间 为两天
.signWith(SignatureAlgorithm.HS256, "sunzy123456");// 设置token的加密方式和加密密钥
String token = jwtBuilder.compact(); // 获取token
return new ResultVO(ResStatus.OK, token, user);
3.前端进行登录验证时获取对应的token
4.JWT进行token解析
if(token == null || "".equals(token)){
return new ResultVO(ResStatus.NO, "failed", null);
}else {
JwtParser parser = Jwts.parser();
parser.setSigningKey("sunzy123456"); // 密钥需要与加密时使用的一致
try{
// 如果token正确 且在有效期内 则解析正常否则会出现异常
Jws<Claims> claimsJws = parser.parseClaimsJws(token);
Claims body = claimsJws.getBody(); // 获取token中的用户数据
String subject = body.getSubject(); // 获取token中发subject
String key1 = body.get("key1", String.class); /// 获取添加在map中的值
}catch(UnsupportedJwtException e){
return new ResultVO(ResStatus.NO, "token不合法请重新登录!", null);
}catch(ExpiredJwtException e){
return new ResultVO(ResStatus.NO, "token已过期,请重新登录!", null);
}
catch (Exception e){
return new ResultVO(ResStatus.NO, "未知错误", null);
}
5.使用拦截器验证token
创建拦截器
package com.sunzy.fmmall.interceptor; import com.alibaba.fastjson.JSON; import com.sunzy.fmmall.vo.ResStatus; import com.sunzy.fmmall.vo.ResultVO; import io.jsonwebtoken.*; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @Component public class CheckTokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getParameter("token"); if (token == null || "".equals(token)) { doResponse(response, "请先登录!"); return false; } else { JwtParser parser = Jwts.parser(); parser.setSigningKey("sunzy123456"); // 密钥需要与加密时使用的一致 try { // 如果token正确 且在有效期内 则解析正常否则会出现异常 Jws<Claims> claimsJws = parser.parseClaimsJws(token); Claims body = claimsJws.getBody(); // 获取token中的用户数据 String subject = body.getSubject(); // 获取token中发subject String key1 = body.get("key1", String.class); /// 获取添加在map中的值 return true; } catch (UnsupportedJwtException e) { doResponse(response,"token不合法,请重新登录!"); } catch (ExpiredJwtException e) { doResponse(response,"token已过期,请重新登录!"); } catch (Exception e) { doResponse(response,"未知错误!"); } return false; } } private void doResponse(HttpServletResponse response, String msg) throws IOException { ResultVO resultVO = new ResultVO(ResStatus.NO, msg, null); String string = JSON.toJSONString(resultVO); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); PrintWriter writer = response.getWriter(); writer.write(string); writer.flush(); writer.close(); } }
配置拦截器
package com.sunzy.fmmall.config; import com.sunzy.fmmall.interceptor.CheckTokenInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 拦截器的配置类 */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Autowired private CheckTokenInterceptor checkTokenInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // registry是拦截器的注册器 // 将自己创建的拦截器加入进来 即可实现拦截功能 registry.addInterceptor(checkTokenInterceptor) .addPathPatterns("/**") // 拦截所有路径 .excludePathPatterns("/user/**"); // 除了用户登录和注册路径 } }
加密使用到工具类
1.md5
package com.qfedu.fmmall.utils;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
//MD5 生成器
public class MD5Utils {
public static String md5(String password){
//生成一个md5加密器
try {
MessageDigest md = MessageDigest.getInstance("MD5");
//计算MD5 的值
md.update(password.getBytes());
//BigInteger 将8位的字符串 转成16位的字符串 得到的字符串形式是哈希码值
//BigInteger(参数1,参数2) 参数1 是 1为正数 0为0 -1为负数
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
2.base64
package com.qfedu.fmmall.utils;
import java.util.Base64;
//base64 加密 解密 激活邮件的时候 为 邮箱地址 code验证码 进行加密
//当 回传回来后 进行邮箱地址 和 code 的解密
public class Base64Utils {
//加密
public static String encode(String msg){
return Base64.getEncoder().encodeToString(msg.getBytes());
}
//解密
public static String decode(String msg){
return new String(Base64.getDecoder().decode(msg));
}
}
logback日志
添加xml文件
在springboot项目的resource目录下创建logback-spring.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd-HH:mm:ss E} %level [%thread]-%class[%line]: %msg%n</pattern>
</springProfile>
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd-HH:mm:ss E} %level [%thread]-%class[%line]: %msg%n</pattern>
</springProfile>
<!--日志的编码格式-->
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="timeFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--TimeBasedRollingPolicy 基于时间的滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log/log-%d{yyyy-MM-dd-HH}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="timeFile"/>
</root>
</configuration>
在sercie实现类创建Logger对象,输⼊⽇志
日志会被记录到项目的/log目录下,且是按每日记录
log/log-%d{yyyy-MM-dd-HH}.log
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!