在淘宝上买的课程的一个例子,看了视频,抄了一遍代码,那时候刚开始学springboot,所以感觉没什么用,然后就又学习了一段时间。最近回想起来有这样的一个系统符合我现阶段的学习程度,然后就又写了一遍。其中css和js实在没法自己搞,所以就直接把课程里的拿来用。所有做完后的样式确实不好看,但我自己觉得看的过去。
今天下午可以算是完工了(也就是增删改查),又想起大佬的话,也怕自己忘了,所以写博客记录一下。
1.技术方法
springboot+thymeleaf+mybatis+shiro
1.1 pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>bill</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
pom依赖差不多用的最新的版本,自己学习用的,应该没什么大的影响。
1.2 springboot使用thymeleaf
springboot使用thymeleaf我觉的比较方便,在HTML中引入<html xmlns:th="http://www.thymeleaf.org">
,就能使用thymeleaf的功能(不知道这样说对不对)。thymeleaf特性的使用还是要多学习的,能够灵活应用的感觉应该非常不错。
1.3 springboot-mybatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录(百度)。
刚接触springboot的时候就是学的mybatis,所以可能情有独钟吧。
1.4 springboot-shiro
本来的教程上没有这个技术点,但我在学习的过程中接触到了shiro,有一篇springboot+Vue的教学博客让我记忆犹新,所以我决定试一下。在这里给大家推荐一下 Evan-Nightly大佬的这篇博客 Vue + Spring Boot 项目实战,也给自己插个眼。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序(百度)。
2.代码梳理
2.1 结构
java类大概就是这些。
WebMvcConfig.java是拦截器、过滤器和视图控制器的配置文件。
package com.example.config;
import com.example.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MySpringMvcConfig implements WebMvcConfigurer {
//添加视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//main.html是显示在浏览器网址上的,main/main是项目main下面的main/html
registry.addViewController("main.html").setViewName("main/main");
registry.addViewController("index.html").setViewName("main/index");
}
//配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
//拦截所有请求
.addPathPatterns("/**")
//不拦截
.excludePathPatterns("/", "/login", "/index.html")
//测试用的请,不拦截,可在项目上线时删掉
.excludePathPatterns("/test", "/thymeleaf");
}
}
我在刚开始学springboot的时候,拦截器、过滤器和视图控制器还挺难理解的,虽然看了几遍概念介绍但感觉还是无法理解这到底是个什么东西。在做这个项目的时候我自己多试了几遍。改一个编一次看一下什么情况,慢慢的就知道是什么作用了。自学,笨鸟先飞,是这样的,没办法。
ShiroConfig.java是shiro配置类
package com.example.config;
import com.example.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Create by Administrator on 2020/2/24.
*/
@Configuration
public class ShiroConfig {
@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanProcessor(){
return new LifecycleBeanPostProcessor();
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getMyRealm());
return securityManager;
}
@Bean
public MyRealm getMyRealm() {
MyRealm wjRealm = new MyRealm();
wjRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return wjRealm;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
shiro的配置具体是什么意思我还真说不出来,感觉自己也就知道的大概,知其然而不知所以然。
我只用到了shiro的用户信息加密和认证登录功能,就是给用户注册时的密码加密,然后在登录时再比对加密的密码。
MyRealm.java
package com.example.realm;
/**
* Create by Administrator on 2020/2/24.
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//简单重写获取授权信息方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
return s;
}
//获取认证信息,即根据token中的用户名从数据库中获取密码和盐等信息并返回
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String userName = authenticationToken.getPrincipal().toString();
User user = userService.getUserByName(userName);
String passwordInDB = user.getPassword();
String salt = user.getSalt();
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt), getName());
return authenticationInfo;
}
}
MyRealm和ShiroConfig.java配合使用,其中的奥秘我还没懂,希望有懂的大佬帮忙教一下。
2.2 登录模块
package com.example.controller;
@Controller
public class LoginController {
@Autowired
UserService userService;
//跳转到用户登录界面
@RequestMapping("/index")
public String index(){
return "main/index";
}
//在页面点击登录按钮后执行登录
@RequestMapping("/login")
//username和password是前端传递过来的,这就是前后传递的一种方式
public String login(HttpSession httpSession, String username, String password){
System.out.println("username="+username+",password="+password);
//判断是否为空,这个可以改到前端独立判断
if(username.equals("") || password.equals("")){
//如果为空就跳转到登录页
return "redirect:/index";
}
//根据用户名获取用户
User loginuser = userService.getUserByName(username);
if(loginuser == null){
System.out.println("user==null");
return "redirect:/index";
}
//简单的判断密码是否正确
// if(!loginuser.getPassword().equals(password)){
// System.out.println("error password");
// return "redirect:/index";
// }
//shiro获取subject
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
try {
subject.login(usernamePasswordToken);
//存储到session中,拦截器中判断是否登录
httpSession.setAttribute("loginUser", loginuser);
return "redirect:/main.html";
}catch (AuthenticationException e){
return "redirect:/index";
}
// httpSession.setAttribute("loginUser", loginuser);
// return "redirect:/main.html";
}
//跳转到主页面
@GetMapping("/main/main")
public String mainMain(HttpServletRequest httpServletRequest){
System.out.println("mainMain method="+httpServletRequest.getMethod());
return "redirect:/main.html";
}
//退出登录
@GetMapping("/logout")
//public String logout(HttpSession httpSession){
public String logout(HttpServletRequest httpServletRequest){
System.out.println("logout method="+httpServletRequest.getMethod());
Subject subject = SecurityUtils.getSubject();
subject.logout();
//httpSession.removeAttribute("loginUser");
//httpSession.invalidate();
return "redirect:/index";
}
}
我不知道怎么讲,所以把该说的都用注释写出来
index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
<link rel="stylesheet" th:href="@{/css/public.css}"/>
<link rel="stylesheet" th:href="@{/css/style.css}"/>
</head>
<body style="background: #ffffff">
<section class="loginBox" style="margin-top: 100px">
<section class="loginCont">
<form class="loginForm" th:action="@{/login}" method="post">
<div class="inputbox">
<label>用户名</label>
<!-- username绑定到后端 -->
<input type="text" name="username"/>
</div>
<div class="inputbox">
<label>用户密码</label>
<!-- 后端接收到password -->
<input type="text" name="password"/>
</div>
<div class="subBtn" style="margin-top: 5px">
<!-- 提交表单 -->
<input type="submit" value="登录" onclick="this.form.submit()"/>
</div>
</form>
</secion>
</section>
</body>
</html>
提交后,后台判断用户是否存在,用户的密码是否正确,然后跳转到主界面main.html,然后点击菜单就会跳转到相应的界面。
2.3 用户模块
登录之后自然会有一些操作,但大多都基于对数据库的增删改查。
User.java
package com.example.bean;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* Create by Administrator on 2020/2/19.
*/
public class User {
private Integer id;
private String username;
private String realName;
private String password;
private String salt;
//性别 1:女 2:男
private int gender;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
//1管理员 2经理 3普通用户
private int userType;
public User(Integer id, String username, String realName, String password, String salt, int gender, Date birthday, int userType) {
this.id = id;
this.username = username;
this.realName = realName;
this.password = password;
this.salt = salt;
this.gender = gender;
this.birthday = birthday;
this.userType = userType;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", realName='" + realName + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
", gender=" + gender +
", birthday=" + birthday +
", userType=" + userType +
'}';
}
getter() and setter()...
在创建user类时要注意id的类型最好用Integer,否则在增加用户时会有问题,应为id在数据库中是自增的,而在增加用户时页面上没有,会有问题。
还有在建user表时password和salt的大小要大一点,我当时用25个字节结果加了shiro加密后有报错提示(具体的报错就不回放了)。
还有用户的生日也会有问题,要注意加上@DateTimeFormat(pattern = “yyyy-MM-dd”)注解。如果不加从页面保存时(增加或者更新)会有类型转换的错误。
然后重点说一下和shiro有关的内容,就是在注册时密码加密的过程。
@PostMapping("/user/add")
public String add(HttpServletRequest httpServletRequest, User user){
System.out.println("add method="+httpServletRequest.getMethod());
String password = user.getPassword();
String username = user.getUsername();
username = HtmlUtils.htmlEscape(username);
user.setUsername(username);
User user1 = userService.getUserByName(username);
if(null != user1){
return "redirect:/list/users";
}
//生成盐,默认长度16位
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
//设置hash迭代次数
int times = 2;
//获取hash后的密码
String encodingPassword = new SimpleHash("md5", password, salt, times).toString();
user.setSalt(salt);
user.setPassword(encodingPassword);
userService.addUser(user);
return "redirect:/list/users";
}
大概都写到注释里了。
再说一下在页面上展示所有用户,list.html
<table class="providerTable" cellpadding="0" cellspacing="0">
<tr>
<a type="button" th:href="@{/user/add}">
<button>增加用户</button>
</a>
</tr>
<tr class="firstTr">
<th width="10%">用户名</th>
<th width="20%">真实姓名</th>
<th width="10%">性别</th>
<th width="10%">出生日期</th>
<th width="10%">用户类型</th>
<th width="30%">操作</th>
</tr>
<!-- 循环取出所用用户 -->
<tr th:each="u : ${users}">
<td th:text="${u.username}">xxx</td>
<td th:text="${u.realName}">xx</td>
<!--性别:1 女 2 男-->
<td th:text="${u.gender == 1 ? '女' : '男'}">x</td>
<td th:text="${#dates.format(u.birthday, 'yyyy-MM-dd')}">xxx</td>
<!--1管理员 2经理 3普通用户-->
<td th:text="${u.userType==1 ? '管理员' : (u.userType==2 ? '经理' : '普通用户') }">xx</td>
<td>
<a th:href="@{/user/} + ${u.id}" ><img th:src="@{/img/read.png}" alt="查看" title="查看"/></a>
<a th:href="@{/user/} + ${u.id} +'?type=update'" ><img th:src="@{/img/xiugai.png}" alt="修改" title="修改"/></a>
<a th:href="@{/delete/user/}+${u.id}" ><img th:src="@{/img/schu.png}" alt="删除" title="删除"/></a>
</td>
</tr>
</table>
后台从数据库中查找出所有用户传递给前端
//列出所有用户
@RequestMapping("/list/users")
public String listUser(Map<String, Object> map){
List<User> users = userService.uListAll();
map.put("users", users);
return "user/list";
}
修改用户页update.html
<form id="updateForm" action="#" th:action="@{/user}" method="post">
<input th:type="hidden" name="_method" value="put">
<input th:type="hidden" name="id" th:value="${user.id}">
<input th:type="hidden" name="username" th:value="${user.username}">
<input th:type="hidden" name="password" th:value="${user.password}">
<div>
<!-- 这里需要显示用户原本信息 -->
<label >真实姓名:</label>
<input type="text" th:value="${user.realName}" name="realName" id="realName" placeholder="realName"/>
<span >*</span>
</div>
<div>
<label >用户性别:</label>
<select name="gender">
<option value="1" th:selected="${user.gender == 1}">女</option>
<option value="2" th:selected="${user.gender == 2}" selected>男</option>
</select>
</div>
<div>
<label for="birthday">出生日期:</label>
<input type="text" th:value="${#dates.format(user.birthday, 'yyyy-MM-dd')}" name="birthday" id="birthday" placeholder="2016年2月1日"/>
<span >*</span>
</div>
<div>
<label >用户类别:</label>
<!--1管理员 2经理 3普通用户-->
<input type="radio" name="userType" value="1" th:checked="${user.userType == 1}"/>管理员
<input type="radio" name="userType" value="2" th:checked="${user.userType == 2}" checked/>经理
<input type="radio" name="userType" value="3" th:checked="${user.userType == 3}"/>普通用户
</div>
<div>
<input type="button" value="保存" onclick="this.form.submit()"/>
<input type="button" value="返回" onclick="history.back(-1)"/>
</div>
</form>
在修改页需要显示用户原本的信息,所以前后端需要用id来绑定一个user。
//查看用户详细信息
@GetMapping("/user/{id}")
//参数type来区分前端是要查看用户详细信息还是要修改用户信息
public String view(HttpServletRequest httpServletRequest, @PathVariable("id") int id,
@RequestParam(value = "type", defaultValue = "view") String type,
Map<String, Object> map){
System.out.println("view method="+httpServletRequest.getMethod());
//通过前端传来的id获取到用户
User user = userService.getUserById(id);
//返回给前端用于页面显示用户原本的信息
map.put("user", user);
return "user/"+type;
}
点击保存按钮好提交修改的用户信息
//更新用户信息
@PostMapping("/user")
public String update(HttpServletRequest httpServletRequest, User user){
System.out.println("update method="+httpServletRequest.getMethod());
userService.updateUser(user);
return "redirect:/list/users";
}
删除我就简单做了一下点击删除按钮直接从数据库删除
<a th:href="@{/delete/user/}+${u.id}" ><img th:src="@{/img/schu.png}" alt="删除" title="删除"/></a>
//删除用户
@GetMapping("/delete/user/{id}")
public String delete(HttpServletRequest httpServletRequest, @PathVariable(value = "id") int id){
System.out.println("delete method="+httpServletRequest.getMethod());
userService.deleteUserById(id);
return "redirect:/list/users";
}
3. 总结
现在就总结好像有点早,但我写着写着发现好像没什么东西能写,或许可能功能比较少吧,Bill和Provider都和user类似,也是对数据库的增删改查操作。所以就不再赘述。
之后应该还会去完善一下,加上log系统和druid后台监控。
最后分享一下GitHub链接
https://github.com/azermu-milk/SpringbootTest.git
供大家clone。近期应该还会更新吧。
4.增加druid后台监控
4.1 介绍
在config文件夹下创建DruidConfig.java类,配置druid后台管理类和druid的filter类,当然还需要在application.yml中补全druid配置。
DruidConfig.java
package com.example.config;
/**
* Create by Administrator on 2020/2/25.
*/
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
//1.配置一个后台管理servlet
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
//设置初始化参数
Map<String, String> initParam = new HashMap<>();
initParam.put(StatViewServlet.PARAM_NAME_USERNAME, "root");
initParam.put(StatViewServlet.PARAM_NAME_PASSWORD, "root");
//如果不写 则默认所有ip都能访问
initParam.put(StatViewServlet.PARAM_NAME_ALLOW, "");
//写入本地电脑的IP地址
initParam.put(StatViewServlet.PARAM_NAME_DENY, "192.xxx.11.1");
bean.setInitParameters(initParam);
return bean;
}
//2.配置druid的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new WebStatFilter());
Map<String, String> initParam = new HashMap<>();
initParam.put(WebStatFilter.PARAM_NAME_EXCLUSIONS, "*.js,*.css,/druid/*");
bean.setInitParameters(initParam);
//设置拦截请求
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
大概就是这样,如注释所示。
application.yml配置
#数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
#间隔多久进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
#配置有一个连接在连接池中的最小生存时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
maxPoolPreparedStatementPerConnectionSize: 25
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
4.2 后台监控展示
刚开始我在statViewServlet()上少写了个@Bean,结果怎么改都没有后台的登录界面,全被我的拦截器拦截了,不过经过我再三检查终于被我发现了这个问题,哈哈。
5.日志框架
看了半天日志框架什么也没看明白,就把自己想用到的加进来了。
logging:
level:
com.example.mapper: debug
#path和name二选一,name的优先级会高于path
file:
path: C:\Users\Administrator\IdeaProjects\mybill\
#name: BillLog.log
#如果使用yml做配置文件,格式要用'',如果是properties则不用
pattern:
#console: '%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n'
#把控制台的日志输出到文件的格式
file: '%d{yyyy/MM/dd-HH:mm:ss} ===== [%thread] ===== %-5level ===== %logger ===== %msg%n'
这个改动也在GitHub上提交了。整个项目是完整的,拉下来加到IDEA中就能启动成功。
5.1分析日志底层实现
在web项目当中引用了 spring-boot-starter-web 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-web 中引入了 spring-boot-starter 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.6.RELEASE</version>
<scope>compile</scope>
</dependency>
spring-boot-starter 中引入了 spring-boot-starter-logging 日志启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
spring-boot-starter-logging 日志启动器 采用的是 logback 日志框架
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>compile</scope>
</dependency>
SpringBoot中默认日志启动器为 spring-boot-starter-logging ,默认采用的是 logback日志框架
logback的默认配置
<included>
<!--日志格式默认规定-->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!--日志文件默认生成路径-->
<property name="LOG_FILE"
value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<!--控制台日志信息默认配置-->
<include resource="org/springframework/boot/logging/logback/console-
appender.xml" />
<!--文件中日志信息默认配置-->
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<!--日志级别默认为: info -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</included>
6.写在最后
这个项目到这里应该结束了,我不会再动它了。
又该去找新的项目学习去了。。。