spring-boot-demo-logback
如何使用 logback 记录程序运行过程中的日志,以及如何配置 logback,可以同时生成控制台日志和文件日志记录,文件日志以日期和大小进行拆分生成。
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-logback</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-demo-logback</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-logback</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SpringBootDemoLogbackApplication.java
@SpringBootApplication
@Slf4j
public class SpringBootDemoLogbackApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoLogbackApplication.class, args);
int length = context.getBeanDefinitionNames().length;
log.trace("Spring boot启动初始化了 {} 个 Bean", length);
log.debug("Spring boot启动初始化了 {} 个 Bean", length);
log.info("Spring boot启动初始化了 {} 个 Bean", length);
log.warn("Spring boot启动初始化了 {} 个 Bean", length);
log.error("Spring boot启动初始化了 {} 个 Bean", length);
try {
int i = 0;
int j = 1 / i;
} catch (Exception e) {
log.error("【SpringBootDemoLogbackApplication】启动异常:", e);
}
}
}
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高, 所以我们使用下面的策略,可以避免输出 Error 的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!--过滤 Error-->
<level>ERROR</level>
<!--匹配到就禁止-->
<onMatch>DENY</onMatch>
<!--没有匹配到就允许-->
<onMismatch>ACCEPT</onMismatch>
</filter>
<!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
<!--<File>logs/info.spring-boot-demo-logback.log</File>-->
<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
<FileNamePattern>logs/spring-boot-demo-logback/info.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
<!--<totalSizeCap>1GB</totalSizeCap>-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
<!--<maxFileSize>1KB</maxFileSize>-->
<!--</triggeringPolicy>-->
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
</appender>
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>Error</level>
</filter>
<!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
<!--<File>logs/error.spring-boot-demo-logback.log</File>-->
<!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
<FileNamePattern>logs/spring-boot-demo-logback/error.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
<!--只保留最近90天的日志-->
<maxHistory>90</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
<maxFileSize>2MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
</appender>
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_ERROR"/>
</root>
</configuration>
spring-boot-demo-log-aop
使用 aop 切面对请求进行日志记录,并且记录 UserAgent 信息。
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-log-aop</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- 解析 UserAgent 信息 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-log-aop</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
AopLog.java
@Aspect
@Component
@Slf4j
public class AopLog {
@Pointcut("execution(public * com.xkcoding.log.aop.controller.*Controller.*(..))")
public void log() {}
@Around("log()")
public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
// 打印请求相关参数
long startTime = System.currentTimeMillis();
Object result = point.proceed();
String header = request.getHeader("User-Agent");
UserAgent userAgent = UserAgent.parseUserAgentString(header);
final Log l = Log.builder()
.threadId(Long.toString(Thread.currentThread().getId()))
.threadName(Thread.currentThread().getName())
.ip(getIp(request))
.url(request.getRequestURL().toString())
.classMethod(String.format("%s.%s", point.getSignature().getDeclaringTypeName(),
point.getSignature().getName()))
.httpMethod(request.getMethod())
.requestParams(getNameAndValue(point))
.result(result)
.timeCost(System.currentTimeMillis() - startTime)
.userAgent(header)
.browser(userAgent.getBrowser().toString())
.os(userAgent.getOperatingSystem().toString()).build();
log.info("Request Log Info : {}", JSONUtil.toJsonStr(l));
return result;
}
/**
* 获取方法参数名和参数值
* @param joinPoint
* @return
*/
private Map<String, Object> getNameAndValue(ProceedingJoinPoint joinPoint) {
final Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
final String[] names = methodSignature.getParameterNames();
final Object[] args = joinPoint.getArgs();
if (ArrayUtil.isEmpty(names) || ArrayUtil.isEmpty(args)) {
return Collections.emptyMap();
}
if (names.length != args.length) {
log.warn("{}方法参数名和参数值数量不一致", methodSignature.getName());
return Collections.emptyMap();
}
Map<String, Object> map = Maps.newHashMap();
for (int i = 0; i < names.length; i++) {
map.put(names[i], args[i]);
}
return map;
}
private static final String UNKNOWN = "unknown";
/**
* 获取ip地址
*/
public static String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String comma = ",";
String localhost = "127.0.0.1";
if (ip.contains(comma)) {
ip = ip.split(",")[0];
}
if (localhost.equals(ip)) {
// 获取本机真正的ip地址
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error(e.getMessage(), e);
}
}
return ip;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class Log {
// 线程id
private String threadId;
// 线程名称
private String threadName;
// ip
private String ip;
// url
private String url;
// http方法 GET POST PUT DELETE PATCH
private String httpMethod;
// 类方法
private String classMethod;
// 请求参数
private Object requestParams;
// 返回参数
private Object result;
// 接口耗时
private Long timeCost;
// 操作系统
private String os;
// 浏览器
private String browser;
// user-agent
private String userAgent;
}
}
TestController.java
@Slf4j
@RestController
public class TestController {
@GetMapping("/test")
public Dict test(String who) {
return Dict.create().set("who", StrUtil.isBlank(who) ? "me" : who);
}
@PostMapping("/testJson")
public Dict testJson(@RequestBody Map<String, Object> map) {
final String jsonStr = JSONUtil.toJsonStr(map);
log.info(jsonStr);
return Dict.create().set("json", map);
}
}
spring-boot-demo-exception-handler
统一的异常处理,包括了两种方式的处理:
第一种对常见API形式的接口进行异常处理,统一封装返回格式;
第二种是对模板页面请求的异常处理,统一处理错误页面。
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-exception-handler</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-exception-handler</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ApiResponse.java
统一的API格式返回封装,里面涉及到的
BaseException
和Status
这两个类,具体代码见 demo。
@Data
public class ApiResponse {
private Integer code;
private String message;
private Object data;
private ApiResponse() {
}
private ApiResponse(Integer code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public static ApiResponse of(Integer code, String message, Object data) {
return new ApiResponse(code, message, data);
}
public static ApiResponse ofSuccess(Object data) {
return ofStatus(Status.OK, data);
}
public static ApiResponse ofMessage(String message) {
return of(Status.OK.getCode(), message, null);
}
public static ApiResponse ofStatus(Status status) {
return ofStatus(status, null);
}
public static ApiResponse ofStatus(Status status, Object data) {
return of(status.getCode(), status.getMessage(), data);
}
public static <T extends BaseException> ApiResponse ofException(T t, Object data) {
return of(t.getCode(), t.getMessage(), data);
}
public static <T extends BaseException> ApiResponse ofException(T t) {
return ofException(t, null);
}
}
DemoExceptionHandler.java
@ControllerAdvice
@Slf4j
public class DemoExceptionHandler {
private static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = JsonException.class)
@ResponseBody
public ApiResponse jsonErrorHandler(JsonException exception) {
log.error("【JsonException】:{}", exception.getMessage());
return ApiResponse.ofException(exception);
}
@ExceptionHandler(value = PageException.class)
public ModelAndView pageErrorHandler(PageException exception) {
log.error("【DemoPageException】:{}", exception.getMessage());
ModelAndView view = new ModelAndView();
view.addObject("message", exception.getMessage());
view.setViewName(DEFAULT_ERROR_VIEW);
return view;
}
}
error.html
位于
src/main/resources/template
目录下
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta charset="UTF-8"/>
<title>统一页面异常处理</title>
</head>
<body>
<h1>统一页面异常处理</h1>
<div th:text="${message}"></div>
</body>
</html>
spring-boot-demo-template-freemarker
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-template-freemarker</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-template-freemarker</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
IndexController.java
@Controller
@Slf4j
public class IndexController {
@GetMapping(value = {"", "/"})
public ModelAndView index(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
User user = (User) request.getSession().getAttribute("user");
if (ObjectUtil.isNull(user)) {
mv.setViewName("redirect:/user/login");
} else {
mv.setViewName("index");
mv.addObject(user);
}
return mv;
}
}
UserController.java
@Controller
@RequestMapping("/user")
@Slf4j
public class UserController {
@PostMapping("/login")
public ModelAndView login(User user, HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
mv.addObject(user);
mv.setViewName("redirect:/");
request.getSession().setAttribute("user", user);
return mv;
}
@GetMapping("/login")
public ModelAndView login() {
return new ModelAndView("login");
}
}
index.ftl
<!doctype html>
<html lang="en">
<#include "./common/head.ftl">
<body>
<div id="app" style="margin: 20px 20%">
欢迎登录,${user.name}!
</div>
</body>
</html>
login.ftl
<!doctype html>
<html lang="en">
<#include "./common/head.ftl">
<body>
<div id="app" style="margin: 20px 20%">
<form action="/demo/user/login" method="post">
用户名<input type="text" name="name" placeholder="用户名"/>
密码<input type="password" name="password" placeholder="密码"/>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
application.yml
server:
port: 8080
servlet:
context-path: /demo
spring:
freemarker:
suffix: .ftl
cache: false
charset: UTF-8
Freemarker 语法糖学习文档
https://freemarker.apache.org/docs/dgui.html
spring-boot-demo-template-thymeleaf
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-template-thymeleaf</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-template-thymeleaf</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
IndexController.java
@Controller
@Slf4j
public class IndexController {
@GetMapping(value = {"", "/"})
public ModelAndView index(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
User user = (User) request.getSession().getAttribute("user");
if (ObjectUtil.isNull(user)) {
mv.setViewName("redirect:/user/login");
} else {
mv.setViewName("page/index");
mv.addObject(user);
}
return mv;
}
}
UserController.java
@Controller
@RequestMapping("/user")
@Slf4j
public class UserController {
@PostMapping("/login")
public ModelAndView login(User user, HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
mv.addObject(user);
mv.setViewName("redirect:/");
request.getSession().setAttribute("user", user);
return mv;
}
@GetMapping("/login")
public ModelAndView login() {
return new ModelAndView("page/login");
}
}
index.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<header th:replace="~{common/head :: header}"></header>
<body>
<div id="app" style="margin: 20px 20%">
欢迎登录,<span th:text="${user.name}"></span>!
</div>
</body>
</html>
login.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<header th:replace="~{common/head :: header}"></header>
<body>
<div id="app" style="margin: 20px 20%">
<form action="/demo/user/login" method="post">
用户名<input type="text" name="name" placeholder="用户名"/>
密码<input type="password" name="password" placeholder="密码"/>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
application.yml
server:
port: 8080
servlet:
context-path: /demo
spring:
thymeleaf:
mode: HTML
encoding: UTF-8
servlet:
content-type: text/html
cache: false
Thymeleaf语法糖学习文档
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
spring-boot-demo-template-beetl
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-template-beetl</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<ibeetl.version>1.1.63.RELEASE</ibeetl.version>
</properties>
<dependencies>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl-framework-starter</artifactId>
<version>${ibeetl.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-template-beetl</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
IndexController.java
/**
* <p>
* 主页
* </p>
*
* @author yangkai.shen
* @date Created in 2018-10-10 11:17
*/
@Controller
@Slf4j
public class IndexController {
@GetMapping(value = {"", "/"})
public ModelAndView index(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
User user = (User) request.getSession().getAttribute("user");
if (ObjectUtil.isNull(user)) {
mv.setViewName("redirect:/user/login");
} else {
mv.setViewName("page/index.btl");
mv.addObject(user);
}
return mv;
}
}
UserController.java
@Controller
@RequestMapping("/user")
@Slf4j
public class UserController {
@PostMapping("/login")
public ModelAndView login(User user, HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
mv.addObject(user);
mv.setViewName("redirect:/");
request.getSession().setAttribute("user", user);
return mv;
}
@GetMapping("/login")
public ModelAndView login() {
return new ModelAndView("page/login.btl");
}
}
index.html
<!doctype html>
<html lang="en">
<% include("/common/head.html"){} %>
<body>
<div id="app" style="margin: 20px 20%">
欢迎登录,${user.name}!
</div>
</body>
</html>
login.html
<!doctype html>
<html lang="en">
<% include("/common/head.html"){} %>
<body>
<div id="app" style="margin: 20px 20%">
<form action="/demo/user/login" method="post">
用户名<input type="text" name="name" placeholder="用户名"/>
密码<input type="password" name="password" placeholder="密码"/>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
application.yml
server:
port: 8080
servlet:
context-path: /demo
Beetl 语法糖学习文档
http://ibeetl.com/guide/#beetl
spring-boot-demo-template-enjoy
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-demo-template-enjoy</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<enjoy.version>3.5</enjoy.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>enjoy</artifactId>
<version>${enjoy.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-demo-template-enjoy</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
EnjoyConfig.java
@Configuration
public class EnjoyConfig {
@Bean(name = "jfinalViewResolver")
public JFinalViewResolver getJFinalViewResolver() {
JFinalViewResolver jfr = new JFinalViewResolver();
// setDevMode 配置放在最前面
jfr.setDevMode(true);
// 使用 ClassPathSourceFactory 从 class path 与 jar 包中加载模板文件
jfr.setSourceFactory(new ClassPathSourceFactory());
// 在使用 ClassPathSourceFactory 时要使用 setBaseTemplatePath
// 代替 jfr.setPrefix("/view/")
JFinalViewResolver.engine.setBaseTemplatePath("/templates/");
jfr.setSessionInView(true);
jfr.setSuffix(".html");
jfr.setContentType("text/html;charset=UTF-8");
jfr.setOrder(0);
return jfr;
}
}
IndexController.java
@Controller
@Slf4j
public class IndexController {
@GetMapping(value = {"", "/"})
public ModelAndView index(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
User user = (User) request.getSession().getAttribute("user");
if (ObjectUtil.isNull(user)) {
mv.setViewName("redirect:/user/login");
} else {
mv.setViewName("page/index");
mv.addObject(user);
}
return mv;
}
}
UserController.java
@Controller
@RequestMapping("/user")
@Slf4j
public class UserController {
@PostMapping("/login")
public ModelAndView login(User user, HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
mv.addObject(user);
mv.setViewName("redirect:/");
request.getSession().setAttribute("user", user);
return mv;
}
@GetMapping("/login")
public ModelAndView login() {
return new ModelAndView("page/login");
}
}
index.html
<!doctype html>
<html lang="en">
#include("/common/head.html")
<body>
<div id="app" style="margin: 20px 20%">
欢迎登录,#(user.name)!
</div>
</body>
</html>
login.html
<!doctype html>
<html lang="en">
#include("/common/head.html")
<body>
<div id="app" style="margin: 20px 20%">
<form action="/demo/user/login" method="post">
用户名<input type="text" name="name" placeholder="用户名"/>
密码<input type="password" name="password" placeholder="密码"/>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
application.yml
server:
port: 8080
servlet:
context-path: /demo
Enjoy 语法糖学习文档
http://www.jfinal.com/doc/6-1