约定优于配置(很多配置有默认值)

特点:

入门快 无代码生成,无xml 提供大型项目需要的外部配置 不是增强spring,而是加快开发

核心功能

起步依赖 自动装配

入门

依赖选择spring Web 需要将controller类,放在启动类的子包中或者同级包下

  1. <!--热部署配置,还以一种改配置的方式,这里就不在掩饰-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-devtools</artifactId>
  5. <optional>true</optional>
  6. </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

http://localhost:8080/actuator/health

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容器中。