约定优于配置(很多配置有默认值)
特点:
入门快 无代码生成,无xml 提供大型项目需要的外部配置 不是增强spring,而是加快开发
核心功能
起步依赖 自动装配
入门
依赖选择spring Web 需要将controller类,放在启动类的子包中或者同级包下
<!--热部署配置,还以一种改配置的方式,这里就不在掩饰-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
yml支持的数据类型
#普通数据的配置
name: jack
#对象的配置
user:
username: rose
password: 123
#配置数组
array:
beijing,
tianjin,
shanghai
#配置集合
yangl:
test:
name: tom
arr: 1,jack,2,tom
list1: #这种对象形式的,只能单独写一个对象去接收,所以无法使用@value注解获取
- zhangsan
- lisi
list2:
- driver: mysql
port: 3306
- driver: oracle
port: 1521
map:
key1: value1
key2: value2
#端口配置
server:
port: 8081
两种方式将yml文件数据注入到成员变量中
@Value("${name}")
private String name;
//第二种方式,使用@ConfigurationProperties注解方式,提供GET/SET方法
在类上注解@ConfigurationProperties(prefix = "yangl.test")
//也可以不导入下面的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
异常处理
package com.qf.controller;
import com.qf.exception.MyException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//测试类
@RestController
public class ExceptionController {
@RequestMapping("/exception")
public String exception(){
int i = 1/0;
return "exception";
}
@RequestMapping("/myexception")
public String myexception()throws MyException{
throw new MyException("自定义异常");
}
}
package com.qf.exception;
//自定义异常类
public class MyException extends Exception{
public MyException(String msg){
super(msg);
}
}
package com.qf.exception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
//拦截异常,这是全局异常处理类
@RestControllerAdvice
public class MyExceptionHandler {
//处理指定异常
@ExceptionHandler(value = Exception.class)
public Object Handler1(Exception e, HttpServletRequest request){
System.out.println("Handler1");
HashMap<String, Object> map = new HashMap<>();
map.put("msg",e.getMessage());
map.put("url",request.getRequestURL());
return map;
}
@ExceptionHandler(value = MyException.class)
public Object Handler2(MyException e, HttpServletRequest request){
System.out.println("Handler2");
HashMap<String, Object> map = new HashMap<>();
map.put("msg",e.getMessage());
map.put("url",request.getRequestURL());
return map;
}
}
过滤器(Listener,拦截器同理)
全局异常类
package com.qf.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(urlPatterns = "/filter/*" , filterName = "filter1")//指定拦截路径和名字
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("LoginFilter ");
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse)response;
chain.doFilter(req,resp);
}
}
package com.qf.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/filter")
public class FilterController {
@RequestMapping("/login")
public String login() {
System.out.println("登录");
return "login";
}
}
package com.qf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
//启动类
@SpringBootApplication
@ServletComponentScan("指定配置类路径")
//Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册
public class Springboot02Application {
public static void main(String[] args) {
SpringApplication.run(Springboot02Application.class, args);
}
}
拦截器
package com.lpl2.work.handler;
import org.aspectj.lang.annotation.Aspect;
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;
public class MyInterceptor implements HandlerInterceptor {
//进入controller方法之前调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入controller方法preHandle之前调用");
return true;//true表示放行,false表示不放行
}
//调用完controller之后,视图渲染之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("调用完controller之后,视图渲染之前"+"postHandle");
}
//页面跳转之后,整个流程执行之后,一般用于资源清理操作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("postHandle,页面跳转之后,整个流程执行之后,一般用于资源清理操作");
}
}
package com.qf.interceptor;
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 MyInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//设置拦截器并指定拦截路径
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/interceptor/*");
//registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");//拦截所有
//registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/test");//指定不拦截
//添加自定义拦截器
WebMvcConfigurer.super.addInterceptors(registry);
}
}
package com.qf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class InterceptorController {
@RequestMapping("/interceptor/myinterceptor")
public String myinterceptor(){
System.out.println("myinterceptor");
return "/index.html";
}
}
整合mybatis,Druid连接池
导入依赖
<!-- 引入mybatis相关依赖,必须写版本号 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- 引入mysql相关依赖,如果不写版本号,引入的8.0以上版本
可以设置为其他版本
-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
资源类application.properties
# 数据库配置
# 默认使用mysql的驱动是8.x的版本,注意driver-class-name,url中增加时区的配置
spring.datasource.url=jdbc:mysql://localhost:3306/java1909?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# mybatis配置
# 配置别名需要扫描的包
mybatis.type-aliases-package=com.qf.pojo
# 引入映射文件
mybatis.mapper-locations=classpath:mapper/*.xml
# 配置日志在控制台显示sql语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#使用阿里巴巴druid数据源,默认使用自带的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
yml文件(默认)
mybatis:
type-aliases-package: com.qf.pojo
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/java2001?serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
Dao层加注解
@Repository
启动类加注解(新手最容易忘记)
@MapperScan("com.qf.mapper")//指定dao层
@SpringBootApplication
分页插件pagehelper
https://zhuanlan.zhihu.com/p/344982068
依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
添加方法
@RequestMapping("/findByPage")
public PageInfo findByPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "2") Integer pageSize){
PageHelper.startPage(pageNum,pageSize);
List<Account> accounts = accountService.findAll();
PageInfo pageInfo = new PageInfo(accounts);
return pageInfo;
}
FreeMarker
网页静态化 ${document.documentTime?datetime}模板中日期格式
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
application.properties
lpl:也就是访问静态页面的路径,需要跳转的页面的位置
########## 配置freemarker ##########
#是否开启缓存
spring.freemarker.cache=false
#路径,lpl:也就是访问静态页面的路径
spring.freemarker.template-loader-path=classpath:/templates
#文件后缀
spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
实体类
package com.qf.pojo;
public class Student {
private Integer id;
private String name;
private Integer age;
private String address;
public Student(Integer id, String name, Integer age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
在templates目录下建立student.ftl文件,然后拷贝到D:ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1" width=600>
<tr>
<th>index</th>
<th>id</th>
<th>name</th>
<th>age</th>
<th>address</th>
</tr>
<#list students as student>
<#if student_index % 2 == 0>
<tr bgcolor="red">
<#else>
<tr bgcolor="yellow">
</#if>
<td>${student_index}</td>
<td>${student.id}</td>
<td>${student.name}</td>
<td>${student.age}</td>
<td>${student.address}</td>
</tr>
</#list>
</table>
</body>
</html>
FreeMarkerController
package com.qf.controller;
import com.qf.pojo.Student;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
@Controller
@RequestMapping("/freemarker")
public class FreeMarkerController {
@RequestMapping("/student")
public String hello(Model model) throws Exception {
//创建List集合获取多个元素
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1, "jack", 18, "郑州二七"));
students.add(new Student(2, "rose", 19, "郑州中原"));
students.add(new Student(3, "tom", 20, "郑州金水"));
//导包的时候注意,没有All
model.addAttribute("students",students);
return "/student";
}
//生成静态页面的方法
@RequestMapping("createHtml")
@ResponseBody
public String createHtml()throws Exception{
//获取配置对象
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//设置字符集
configuration.setDefaultEncoding("utf-8");
//设置加载的模版目录
configuration.setDirectoryForTemplateLoading(new File("D:/ftl"));
//创建List集合获取多个元素
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,"张三",18,"北京"));
students.add(new Student(2,"李四",19,"上海"));
students.add(new Student(3,"王五",20,"广州"));
//使用map集合加载数据
HashMap<String,ArrayList> map = new HashMap<>();
map.put("students",students);
//创建输出流对象
FileWriter fileWriter = new FileWriter(new
File("D:/ftl_html/student.html"));
//获取加载的模板
Template template = configuration.getTemplate("student.ftl");
//生成html文件
template.process(map,fileWriter);
//关流
fileWriter.close();
return "success";
}
}
Thymeleaf
默认的访问静态目录就在 templates
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
application.properties
########## 配置thymeleaf ##########
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.prefix=classpath:/templates
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.servlet.content-type=text/html
实体类
package com.qf.pojo;
import java.util.Date;
public class User {
private Integer id;
private String username;
private String password;
private Date birthday;
public User(Integer id, String username, String password, Date birthday) {
this.id = id;
this.username = username;
this.password = password;
this.birthday = birthday;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday=" + birthday +
'}';
}
}
controller
package com.qf.controller;
import com.qf.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.Date;
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("findAll")
public String findAll(Model model) {
ArrayList<User> users = new ArrayList<>();
users.add(new User(1001, "张三", "123", new Date()));
users.add(new User(1002, "李四", "456", new Date()));
users.add(new User(1003, "王五", "789", new Date()));
model.addAttribute("users", users);
return "/list";
}
@RequestMapping("findById")
public String findById(Model model, String uid) {
System.out.println(uid);
if (uid.equals("1001")) {
User user = new User(1001, "张三", "123", new Date());
model.addAttribute("user", user);
}
return "/queryOne";
}
}
在templates目录创建list.html以及queeruOne.html
list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:if="${users!=null}">
<table border="1" width="600">
<tr th:each="user,state : ${users}">
<td th:text="${state.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
</tr>
</table>
<hr>
<table border="1" width="600">
<!-- 第二个变量,可以获取遍历的元素的状态-->
<tr th:each="user,state : ${users}">
<td th:text="${state.index}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
</tr>
</table>
<hr>
<table border="1" width="600">
<!--如果不设置表示状态的变量,默认遍历的元素的变量名+Stat
表示状态的变量-->
<tr th:each="user : ${users}">
<td th:text="${userStat.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-
dd HH:mm:ss')}"></td>
</tr>
</table>
</div>
<hr color="red">
<table border="1" width="600">
<tr>
<th>序号</th>
<th>姓名</th>
<th>生日</th>
<th>详情</th>
</tr>
<tr th:each="user : ${users}">
<td th:text="${userStat.count}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td><a th:href="@{/user/findById(uid=${user.id})}">查询</a></td>
</tr>
</table>
</body>
</html>
quweuOne.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:object="${user}">
<!-- *{...}选择表达式一般跟在th:object后,直接选择object中的属性-->
<input type="hidden" th:id="*{id}" name="id">
用户名:<input type="text" th:value="*{username}" name="username" /><br /><br />
密码:<input type="text" th:value="*{password}" name="password"/>
</form>
</body>
</html>
整合Redis
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.properties
#配置redis数据库索引(默认0号库)
spring.redis.database=0
#配置redis服务器ip地址
spring.redis.host=192.168.228.135
#配置redis服务器的端口号
spring.redis.port=6379
controller
package com.qf.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/redis")
public class RedisController {
@Autowired
private RedisTemplate redisTemplate ;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/testString")
public String test(){
//redisTemplate.opsForValue();//操作字符串
//redisTemplate.opsForList();//操作List
//redisTemplate.opsForSet();//操作Set
//redisTemplate.opsForZSet();//操作ZSet
//redisTemplate.opsForHash();//操作Map
redisTemplate.opsForValue().set("username","jack");
String username = (String)redisTemplate.opsForValue().get("username");
System.out.println(username);
stringRedisTemplate.opsForValue().set("password","123");
String password = stringRedisTemplate.opsForValue().get("password");
System.out.println(password);
return "success";
}
}
整合异步方法调用
创建AsyncServiceImpl
package com.qf.service.impl;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncServiceImpl {
@Async//异步方法(设置为异步方法后,访问controller时会直接返回结果,不需要再等待该方法执行完毕)
public void findAll(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在查询数据...");
}
}
创建AsyncController
package com.qf.controller;
import com.qf.service.impl.AsyncServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("async")
public class AsyncController {
@Autowired
private AsyncServiceImpl asyncService;
@RequestMapping("findAll")
public String findAll(){
asyncService.findAll();
return "success";
}
}
在启动类上添加 @EnableAsync 注解,然后启动,访问该方法即可
@SpringBootApplication
@EnableAsync//支持异步操作
整合quartz
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
任务类
package com.qf.quartz;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyQuartz {
@Scheduled(cron="*/2 * * * * ? ")
public void testQuartz(){
System.out.println("testQuartz:"+new Date().toLocaleString());
}
}
启动类添加@EnableScheduling
@SpringBootApplication
@EnableAsync//支持异步操作
@EnableScheduling//开启定时任务
日志
SpringBoot默认使用的日志是Logback,官方建议日志文件命名为:logback-spring.xml
在resources目录创建logback-spring.xml
日志的级别:
ERROR:错误日志 有问题 需要改变 WARN:警告 最优写法 DEBUG:调试 日志 INFO:消息 记录过程 @Slf4j 代码中直接使用: log.info() log.debug()
log.warn()
log.error()
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。
默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="true">
<!-- 定义变量,可通过 ${log.path}和${CONSOLE_LOG_PATTERN} 得到变量值 -->
<property name="log.path" value="D:/log" />
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} |-[%-5p] in %logger.%M[line-%L] -%m%n"/>
<!-- 输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- Threshold=即最低日志级别,此appender输出大于等于对应级别的日志
(当然还要满足root中定义的最低级别)
-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<!-- 日志格式(引用变量) -->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 追加到文件中 -->
<appender name="file1" class="ch.qos.logback.core.FileAppender">
<file>${log.path}/mylog1.log</file>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 滚动追加到文件中 -->
<appender name="file2" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/mylog2.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录
文件超过最大尺寸后,会新建文件,然后新的日志文件中继续写入
如果日期变更,也会新建文件,然后在新的日志文件中写入当天日志
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 新建文件后,原日志改名为如下 %i=文件序号,从0开始 -->
<fileNamePattern>${log.path}/newlog-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 每个日志文件的最大体量 -->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1kb</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 日志文件保留天数,1=则只保留昨天的归档日志文件 ,不设置则保留所有日志-->
<maxHistory>1</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="file1"/>
<appender-ref ref="file2"/>
</root>
</configuration>
整合EasyExcel
EasyExcel官网:https://alibaba-easyexcel.github.io/
pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.3</version>
</dependency>
实体类
package com.qf.pojo;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
@Data
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
测试类,运行后得到文件
package com.qf.esayexcel;
import com.alibaba.excel.EasyExcel;
import com.qf.pojo.DemoData;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class TestEasyExcel {
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
@Test
public void simpleWrite() {
// 写法1
String fileName = "D:/TestEasyExcel.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
}
读取操作
创建监听器
package com.qf.esayexcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.qf.pojo.DemoData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
LOGGER.info("存储数据库成功!");
}
}
}
Dao
package com.qf.esayexcel;
import com.qf.pojo.DemoData;
import java.util.List;
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
测试类
@Test
public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1:
String fileName = "D:/TestEasyExcel.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
Swagger
@EnableSwagger2//开启Swagger2,位于开关类
pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
配置类,http://localhost:8080/swagger-ui.html
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
}
package com.qf.swagger.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.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
//配置Swagger的Bean实例
@Bean
public Docket createDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
//配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("测试API文档标题")
.description("测试api接口文档描述")
.termsOfServiceUrl("http://www.baidu.com")
.version("1.0")
.build();
}
}
pojo
package com.qf.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel("用户")
public class User {
@ApiModelProperty("编号")
private String uid;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
controller
package com.qf.controller;
import com.qf.entity.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
//@Api(description = "用户接口")
@Api(tags = "用户接口")
@RestController
@RequestMapping("/user")
public class UserController {
@ApiOperation("查询单个用户")
@RequestMapping("/findById")
public User findById(@RequestParam @ApiParam("用户ID") String uid){
User user = new User();
user.setUid(uid);
user.setUsername("张三");
user.setPassword("123");
return user;
}
@ApiOperation("删除单个用户")
@PostMapping("/delete")
public User delete(String uid){
User user = new User();
user.setUid(uid);
user.setUsername("李四");
user.setPassword("456");
return user;
}
@ApiOperation("查询所有用户")
@GetMapping("/findAll")
public List<User> findAll(){
User user1 = new User();
user1.setUid("1001");
user1.setUsername("张三");
user1.setPassword("123");
User user2 = new User();
user2.setUid("1002");
user2.setUsername("李四");
user2.setPassword("456");
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
return users;
}
}
修改配置类
package com.qf.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
//配置Swagger的Bean实例
@Bean
public Docket createDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(createApiInfo())
.groupName("yangl")//分组名称(可以创建多个Docket就有多个组名)
.enable(true)//enable表示是否开启Swagger
.select()
//RequestHandlerSelectors指定扫描的包
.apis(RequestHandlerSelectors.basePackage("com.qf.controller"))
.build();
}
//配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
public ApiInfo createApiInfo(){
return new ApiInfoBuilder()
.title("测试标题")
.description("测试描述")
.termsOfServiceUrl("http://www.baidu.com")
.build();
//return ApiInfo.DEFAULT;
}
}
Swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息 @Api:修饰整个类,描述Controller的作用 @ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiModel:用对象来接收参数 ,修饰类
@ApiModelProperty:用对象接收参数时,描述对象的一个字段
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述,一般描述错误的响应
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiParam:单个参数描述
@ApiImplicitParam:一个请求参数,用在方法上
@ApiImplicitParams:多个请求参数
lombok
@NoArgsConstructor //无参构造
@AllArgsConstructor //全部参数构造
http://localhost:8080/swagger-ui.html
项目整合Actuator
是SpringBoot项目中一个非常强大一个功能,有助于对应用程序进行监视和管理,通过restful api请求来监管、审计、收集应用的运行情况。
Actuator 的核心是端点Endpoint,它用来监视应用程序及交互,spring-boot-actuator 中已经内置了非常多的 Endpoint(health、info、beans、metrics、httptrace、shutdown等等),同时也允许我们自己扩展自己的 Endpoints。每个 Endpoint 都可以启用和禁用。要远程访问 Endpoint,还必须通过 JMX 或 HTTP 进行暴露,大部分应用选择HTTP,Endpoint 的ID默认映射到一个带 /actuator 前缀的URL。例如,health 端点默认映射到 /actuator/health
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在application.yml中写入以下内容:
默认的 Endpoint 映射前缀是 /actuator,可以通过如上 base-path 自定义设置。
每个 Endpoint 都可以配置开启或者禁用。但是仅仅开启 Endpoint 是不够的,还需要通过 jmx 或者 web 暴露他们,通过 exclude 和 include 属性配置。
management:
endpoints:
# 暴露 EndPoint 以供访问,有jmx和web两种方式,exclude 的优先级高于 include
jmx:
exposure:
exclude: '*'
include: '*'
web:
exposure:
# exclude: '*'
include: ["health","info","beans","mappings","logfile","metrics","shutdown","env"]
base-path: /actuator # 配置 Endpoint 的基础路径
cors: # 配置跨域资源共享
allowed-origins: http://example.com
allowed-methods: GET,POST
enabled-by-default: true # 修改全局 endpoint 默认设置
endpoint:
auditevents: # 1、显示当前引用程序的审计事件信息,默认开启
enabled: true
cache:
time-to-live: 10s # 配置端点缓存响应的时间
beans: # 2、显示一个应用中所有 Spring Beans 的完整列表,默认开启
enabled: true
conditions: # 3、显示配置类和自动配置类的状态及它们被应用和未被应用的原因,默认开启
enabled: true
configprops: # 4、显示一个所有@ConfigurationProperties的集合列表,默认开启
enabled: true
env: # 5、显示来自Spring的 ConfigurableEnvironment的属性,默认开启
enabled: true
flyway: # 6、显示数据库迁移路径,如果有的话,默认开启
enabled: true
health: # 7、显示健康信息,默认开启
enabled: true
show-details: always
info: # 8、显示任意的应用信息,默认开启
enabled: true
liquibase: # 9、展示任何Liquibase数据库迁移路径,如果有的话,默认开启
enabled: true
metrics: # 10、展示当前应用的metrics信息,默认开启
enabled: true
mappings: # 11、显示一个所有@RequestMapping路径的集合列表,默认开启
enabled: true
scheduledtasks: # 12、显示应用程序中的计划任务,默认开启
enabled: true
sessions: # 13、允许从Spring会话支持的会话存储中检索和删除(retrieval and deletion)用户会话。使用Spring Session对反应性Web应用程序的支持时不可用。默认开启。
enabled: true
shutdown: # 14、允许应用以优雅的方式关闭,默认关闭
enabled: true
threaddump: # 15、执行一个线程dump
enabled: true
# web 应用时可以使用以下端点
heapdump: # 16、 返回一个GZip压缩的hprof堆dump文件,默认开启
enabled: true
jolokia: # 17、通过HTTP暴露JMX beans(当Jolokia在类路径上时,WebFlux不可用),默认开启
enabled: true
logfile: # 18、返回日志文件内容(如果设置了logging.file或logging.path属性的话),支持使用HTTP Range头接收日志文件内容的部分信息,默认开启
enabled: true
prometheus: #19、以可以被Prometheus服务器抓取的格式显示metrics信息,默认开启
enabled: true
SpringBoot装配原理
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
@Configuration Spring配置类(spring的一个组件)
@EnableAutoConfiguration 这个注解是开启自动配置的功能。
总结:SpringBoot启动的时候通过@EnableAutoConfiguration注解找到META-INF/spring.factories文件中的所有自动配置类,并对其加载,这些自动配置类都是以AutoConfiguration结尾来命名的。它实际上就是一个JavaConfig形式的IOC容器配置类,通过以Properties结尾命名的类中取得在全局配置文件中配置的属性,如server.port。 Properties类的含义:封装配置文件的相关属性。 AutoConfiguration类的含义:自动配置类,添加到IOC容器中。