SpringBoot-day2

1.Spring boot配置

1.1 引入

spring boot配置就是对spring boot进行一些配置

例如默认启动项目使用的是8080端口,但是我就是想用80又或者9090端口呢?该怎么配置?下面一一讲解

1.2 配置文件

SpringBoot使用一个全局的配置文件,配置文件名是固定的

这个配置文件有两种方式:

  1. application.properties (传统方式,不太优美)
  2. application.yml (推荐使用)

配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好了,有默认值,当我们想自定义的时候,就需要弄个配置文件自己在配置文件修改了;

1.3 YML语法

1.3.1 基本语法

k:(空格)v:表示一对键值对(空格必须有);

以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的;

以下面的举例

server:
  port: 8081

属性和值也是大小写敏感;

1.3.2 值的写法

  1. 字面量:普通的值(数字、字符串、布尔)
    k: v:字面直接来写;
    • 字符串默认不用加上单引号或者双引号;
    • "":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
    • '':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
  2. k: v:在下一行来写对象的属性和值的关系;注意缩进

对象还是k: v的方式.举例

person:
    name: zhangsan
    age: 22

行内写法

person: {name: zhangsan,age: 22}
  1. 数组(List、Set)

用- 值表示数组中的一个元素

hobbys:
    - lol
    - swimming

行内写法

 pets: [cat,dog,pig]

1.4 配置文件值的注入

在需要注入到的类上面打注解@ConfigurationProperties(prefix = "配置文件的key"),这样只会把配置文件和类的属性的名字和类型一致的值注入进去。

如果类的某个属性名字和配置文件的芈名字不一致,则需要在那个属性上面打个注解@Value("${对象key.属性key}")

1.4.1 准备&测试

  1. 配置文件 application.yml
person:
  name: zhangsan
  age: 20
  boss: false
  birthday: 2017/01/01
  maps: {k1: v1,k2: v2}
  hobby:
    - lol
    - rap
    - swimming
  dog:
    name: jinmao
    age: 2
  1. JavaBean
package cn.wangningbo.domain;

import org.springframework.stereotype.Component;

@Component
public class Dog {
    private String name;
    private Integer age;
    //提供get、set和toString方法
}
package cn.wangningbo.domain;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;

@Component //要解析spring里面配置属性,也要spring管理
@ConfigurationProperties(prefix = "person")
// 告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;  //prefix = "person":配置文件中哪个下面的所有属性进行一一映射
//指定从哪个配置文件中取读取,默认是application.properties/默认是application.yml,当然可以通过PropertySource注解指定,不支持yml,只支持properties
//@PropertySource("classpath:person.properties")
public class Person {
    private String name;
    private Integer age;
    private Boolean boss;
    @Value("${person.birthday}")//名字不一致或者只有一两个属性的时候使用它
    private Date date;
    private Map<String, String> maps;
    private List<String> list;
    private Dog dog;
    //提供get、set和toString方法
}
package cn.wangningbo.controller;

import cn.wangningbo.domain.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {
    @Autowired
    private Person person;

    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
        //测试注入是否成功
        System.out.println(person);
        return "hello config!";
    }
}
  1. 在子模块的pom.xml中导包
        <!--导入配置文件处理器,配置文件进行绑定就会有提示-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
  1. 测试
    启动App,在浏览器端访问地址:http://localhost:8080/hello,查看控制台输出的person对象的属性有没有值即可!

由于我在controller那里输出了注入的person,所以我访问那个@RequestMapping("/hello")就会进入到那个方法输出person

1.4.2 注入注意点

  1. 读取配置文件的注解方式有两种@ConfigurationProperties和@PropertySource
  2. @ConfigurationProperties支持properties和yml
  3. @PropertySource只支持properties

1.5.Profile多环境支持

1.5.1为什么要做多环境配置?

一套代码要在多种环境运行(开发,测试,上线),所以我们的配置文件要支持多种环境

1.5.2 多profile文件

们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml

默认使用application.properties的配置;

1.5.3 Yml支持多文档块模式

创建一个yml配置文件application.yml

server:
  port: 8081
spring:
  profiles:
    active: dev #默认启用环境
---
server:
  port: 8082
spring:
  profiles: test
---
server:
  port: 8083
spring:
  profiles: prod

1.5.4 激活特定环境

激活特定环境的方式有三种:

  1. 在配置文件中指定 spring.profiles.active=dev
    • 例如上面那个yml配置文件,我默认激活的就是dev
  2. 命令行: 部署环境

java -jar springboot_02_config-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod;

可以直接在测试的时候,配置传入命令行参数,上面这条名命令我传入的参数就是prod

  1. 虚拟机参数; 开发环境
    • -Dspring.profiles.active=dev
    • 上面这个参数表示使用dev环境,我是用的是idea,只需要在idea右上角点击那个Edit Configurations,把上面那行配置粘贴到VM options那里即可

1.6 自动配置

1.6.1 自动配置原理

  1. SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
  2. @EnableAutoConfiguration 作用:
    • 利用EnableAutoConfigurationImportSelector给容器中导入一些组件?
    • 可以查看selectImports()方法的内容;
    • List<String> configurations=getCandidateConfigurations(annotationMetadata,attributes);获取候选的配置将 类路径下META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到了容器中;每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;每一个自动配置类进行自动配置功能;
  3. 以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;
@Configuration   //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class)  //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中

@ConditionalOnWebApplication //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;    判断当前应用是否是web应用,如果是,当前配置类生效

@ConditionalOnClass(CharacterEncodingFilter.class)  //判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;

@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)  //判断配置文件中是否存在某个配置  spring.http.encoding.enabled;如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
public class HttpEncodingAutoConfiguration {
  
    //他已经和SpringBoot的配置文件映射了
    private final HttpEncodingProperties properties;
  
   //只有一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
        this.properties = properties;
    }
  
    @Bean   //给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件?
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
        return filter;
    }

根据当前不同的条件判断,决定这个配置类是否生效。一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;

  1. 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;配置文件能配置什么就可以参照某个功能对应的这个属性类

1.6.2 自动配置总结

  1. SpringBoot启动会加载大量的自动配置类
  2. 我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
  3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;

xxxxAutoConfigurartion:自动配置类;给容器中添加组件
xxxxProperties:封装配置文件中相关属性;

  1. 使用SpringBoot:
    • 1)创建SpringBoot应用,选中我们需要的模块
    • 2)SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
    • 3)自己编写业务代码;

2. 整合测试

2.1 引入

由于每次都要写controller来访问测试比较麻烦,其他我们可以通过springboot的测试直接测试。就和原来不用启动tomcat一样测试

2.2 导包

在pom.xml中导入场景启动器

        <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>
        </dependency>

2.3 创建一个SpringBoot应用,并在下面创建一个Bean

SpringBoot应用

package cn.wangningbo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

Bean

package cn.wangningbo.service;

import org.springframework.stereotype.Service;

@Service
public class ServiceTest {
    public void test() {
        System.out.println("测试。。。");
    }
}

2.4 3.4.写SpringBoot测试从Springboot中获取bean就算作成功

测试类上面配置两个注解

  • 1)@RunWith(SpringRunner.class)
  • 2)@SpringBootTest(classes = App.class)//通过指定入口类,就知道要从哪儿去扫描包
package cn.wangningbo;

import cn.wangningbo.service.ServiceTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class) //通过指定入口类,就知道要从哪儿去扫描包
public class SpringBootServiceTest {
    @Autowired
    private ServiceTest serviceTest;

    @Test
    public void test() throws Exception {
        System.out.println(serviceTest);
        serviceTest.test();
    }
}

3. spring boot 日志支持

3.1 为什么需要日志框架?

通过日志的方式可以记录系统运行的过程或错误。以便定位问题。

3.2 常见的日志框架

3.2.1 设计思想

  1. System.out.println("");将关键数据打印在控制台;去掉?写在一个文件?

  2. 框架来记录系统的一些运行时信息;日志框架 ; zhanglogging.jar;

  3. 高大上的几个功能?异步模式?自动归档?xxxx? zhanglogging-good.jar?

  4. 将以前框架卸下来?换上新的框架,重新修改之前相关的API;zhanglogging-prefect.jar;

  5. JDBC---数据库驱动;

写了一个统一的接口层;日志门面(日志的一个抽象层);logging-abstract.jar;

给项目中导入具体的日志实现就行了;我们之前的日志框架都是实现的抽象层;

3.2.2 常见框架

日志框架 = 日志门面+日志实现

现在市面上有很多种日志框架,我这里就介绍下面这种很厉害的日志门面和框架

日志门面:SLF4J

日志实现:Log4j2、Logback

SpringBoot:底层是Spring框架,Spring框架默认使用的日志门面是JCL

我使用SpringBoot就选用日志门面SLF4J和日志实现logback

3.3 SLF4J的使用

3.3.1 如何在系统中使用SLF4J

参考官网:https://www.slf4j.org/

以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;给系统里面导入slf4j的jar和 logback的实现jar

每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;

3.3.2 遗留问题

  1. 如何让系统中所有的日志都统一到slf4j:

    ==(1)将系统中其他日志框架先排除出去;(在pom.xml里面排除一下就好了)==

    ==(2)用中间包来替换原有的日志框架;==

    ==(3)我们导入slf4j其他的实现==

  2. 当项目是使用多种日志API时,可以统一适配到SLF4J,中间使用SLF4J或者第三方提供的日志适配器适配到SLF4J,SLF4J再底层用开发者想用的一个日志框架来进行日志系统的实现,从而达到了多种日志的统一实现。其中的技术实现大体有一下两种方式:

    (1)重写上有类的实现。

    比如jcl-over-slf4j和log4j-over-slf4j,为了承接log4j 1,重写了log4j 1 的Logger和LogFactory类。

    (2)对接上有类的扩展方案。

    比如jul-to-slf4j和log4j-to-slf4j,为了承接J.U.L,实现了继承java.util.logging.Handler的SLF4JBridgeHandler。

3.4 SpringBoot日志关系

  1. 日志关系小结:
    • (1)SpringBoot底层也是使用slf4j+logback的方式进行日志记录(我是怎么知道的?进入到pom.xml里面,鼠标右击-->Diagrams-->Show Dependencies...!注意:pom.xml里面要有web的场景启动器)
    • (2)SpringBoot也把其他的日志都替换成了slf4j;
    • (3)中间替换包?
    • (4)如果我们要引入其他框架?一定要把这个框架的默认日志依赖移除掉?

==SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可;==

3.5 logback日志使用

日志有级别之分,从小到大依次是:trace、debug、info、warn、error

默认级别:info

3.5.1 默认配置

查看日志的默认配置-->Maven:org.springframework.boot:spring-boot:2.0.5.RELEASE-->org.springframework.boot-->logging-->logback-->下面都是logback的配置

在pom.xml导包

        <!--web场景启动器-->
        <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>
        </dependency>

写一个测试方法,查看日志等级!

package cn.wangningbo.test;

import cn.wangningbo.App;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class WangNingBoTest {
    //slf4j+logback
    @Test
    public void testLogging() throws Exception {
        //拿到日志记录器
        Logger logger = LoggerFactory.getLogger(getClass());
        //记录日志分为以下几个等级,由低到高排序
        logger.trace("trace...");
        logger.debug("debug...");
        //默认级别就是info,比它级别高的就会输出,级别低的不会输出
        logger.info("info...");
        logger.warn("warn...");
        logger.error("error...");
    }
}

输出结果如下!说明默认级别就是info

2019-08-25 18:16:08.897  INFO 11164 --- [           main] cn.wangningbo.test.WangNingBoTest        : Started WangNingBoTest in 2.279 seconds (JVM running for 3.098)
2019-08-25 18:16:09.025  INFO 11164 --- [           main] cn.wangningbo.test.WangNingBoTest        : info...
2019-08-25 18:16:09.025  WARN 11164 --- [           main] cn.wangningbo.test.WangNingBoTest        : warn...
2019-08-25 18:16:09.025 ERROR 11164 --- [           main] cn.wangningbo.test.WangNingBoTest        : error...

3.5.1.1 修改默认配置

由上面可以看出,日志级别默认是info,但是我想修改一些默认配置,这时候就需要配置文件了!

创建一个配置文件application.yml

3.5.1.1.1 修改日志级别。

只需要在application.yml里面配置即可(注意:加上包路径)

logging:
  level: 
    cn: 
      wangningbo: trace

再次执行测试方法

package cn.wangningbo.test;

import cn.wangningbo.App;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class WangNingBoTest {
    //slf4j+logback
    @Test
    public void testLogging() throws Exception {
        //拿到日志记录器
        Logger logger = LoggerFactory.getLogger(getClass());
        //记录日志分为以下几个等级,由低到高排序
        logger.trace("trace...");
        logger.debug("debug...");
        //默认级别就是info,比它级别高的就会输出,级别低的不会输出
        logger.info("info...");
        logger.warn("warn...");
        logger.error("error...");
    }
}

输出结果.可以看到全部都输出了,说明修改日志级别成功了!

2019-08-25 18:26:02.414  INFO 17212 --- [           main] cn.wangningbo.test.WangNingBoTest        : Started WangNingBoTest in 3.77 seconds (JVM running for 5.035)
2019-08-25 18:26:02.567 TRACE 17212 --- [           main] cn.wangningbo.test.WangNingBoTest        : trace...
2019-08-25 18:26:02.567 DEBUG 17212 --- [           main] cn.wangningbo.test.WangNingBoTest        : debug...
2019-08-25 18:26:02.568  INFO 17212 --- [           main] cn.wangningbo.test.WangNingBoTest        : info...
2019-08-25 18:26:02.568  WARN 17212 --- [           main] cn.wangningbo.test.WangNingBoTest        : warn...
2019-08-25 18:26:02.568 ERROR 17212 --- [           main] cn.wangningbo.test.WangNingBoTest        : error...
3.5.1.1.2 修改日志输出路径

两种方式:file和path

  1. 方式一:file即可指定日志输出路径
logging:
  level:
    cn:
      wangningbo: trace
  file: D:/springboot.log # 完整路径

这时候再执行测试方法,即可在电脑的D:/springboot.log,这个文件里面存放的就是日志信息

  1. 方式二:path
    日志路径:当前项目根+path+spring.log
logging:
  level:
    cn:
      # 设置日志级别
      wangningbo: trace
  # 在当前磁盘的根路径下创建logs文件夹;使用spring.log作为默认文件
  path: /logs
3.5.1.1.3 修改日志输出格式

日志输出格式:

  • %d表示日期时间,
  • %thread表示线程名,
  • %-5level:级别从左显示5个字符宽度
  • %logger{50} 表示logger名字最长50个字符,否则按照句点分割。
  • %msg:日志消息,
  • %n是换行符

%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

修改日志的输出格式有两种:

  1. 修改在控制台输出的日志格式
  2. 在指定文件中的日志输出格式
logging:
  level:
    cn:
      # 设置日志级别
      wangningbo: trace
  # 指定完整的路径
#  file: D:/springboot.log
  # 在当前磁盘的根路径下创建logs文件夹;使用spring.log作为默认文件
  path: /logs
  # 配置日志的输出格式
  pattern:
    # 配置日志在控制台的输出格式
    console: "%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n"
    # 配置日志在文件中的输出格式
    file: "%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n"

再次运行测试方法,可以看到控制台和日志文件里面的时间输出格式都是我自定义的那种

3.5.2 指定配置

如果不想用默认配置,可以自定义配置。

指定配置方法:给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用它默认的配置了!

注意:自定义配置的文件名字是固定的,springBoot会自动识别并应用

Logback配置文件的名字:logback-spring.xml或logback.xml

  • logback.xml:直接就被日志框架识别了;
  • logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能
  • 注意:如果使用logback.xml作为日志配置文件,还要使用profile功能,会有以下错误 no applicable action for [springProfile]。如果要使用profile功能,就要使用logback-spring.xml作为文件名。但是不使用profile功能但使用logback-spring.xml也不会报错,所以推荐使用logback-spring.xml作为文件名

Log4j2配置文件的名字:log4j2-spring.xml或log4j2.xml

注意:使用了这些指定的配置文件名字以后,那个application.yml里面配置的相关日志信息都不生效了!就直接使用自己指定的这个配置文件了!

logback.xml 这个文件名不可以使用profile功能!下面这个是配置没有使用profile功能,可以正常运行的!

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="/logs" />
    <!-- 定义日志文件名称 -->
    <property name="appName" value="wangningbo-springboot"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
            %d表示日期时间,
            %thread表示线程名,
            %-5level:级别从左显示5个字符宽度
            %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
            %msg:日志消息,
            %n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->  
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!-- 
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->     
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 
        logger主要用于存放日志对象,也可以定义日志类型、级别
        name:表示匹配的logger类型前缀,也就是包的前半部分
        level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
        additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
        false:表示只用当前logger的appender-ref,true:
        表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- hibernate logger 配置日志级别-->
    <logger name="cn.itsource" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



    <!-- 
    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 
    -->
    <root level="info">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </root>
</configuration> 

测试。再次执行那个测试方法,可以看到控制台的时间输出格式是在logback.xml里面指定的那种格式。在当前项目的磁盘的根路径下创建logs文件夹,使用wangningbo-springboot.log作为日志文件的名字。

Profile功能

            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>

logback-spring.xml 使用Profile功能。我这里在日志的控制台输出和日志文件输出的格式处使用了。

此处配置的意思是:dev环境下使用和不是dev环境下的时间格式不一样,会自动选择!

配置环境的方式:点击idea右上角那个下拉框-->Edit Configurations...-->VM option那里填写虚拟机参数

虚拟机参数:-Dspring.profiles.active=dev

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="/logs" />
    <!-- 定义日志文件名称 -->
    <property name="appName" value="wangningbo-springboot"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
            %d表示日期时间,
            %thread表示线程名,
            %-5level:级别从左显示5个字符宽度
            %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
            %msg:日志消息,
            %n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->  
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!-- 
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->     
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 
        logger主要用于存放日志对象,也可以定义日志类型、级别
        name:表示匹配的logger类型前缀,也就是包的前半部分
        level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
        additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
        false:表示只用当前logger的appender-ref,true:
        表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- hibernate logger 配置日志级别-->
    <logger name="cn.itsource" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



    <!-- 
    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 
    -->
    <root level="info">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </root>
</configuration> 

3.6 切换日志框架

除了logback比较好用,也有log4j2也比较好用!如果不想用默认的那个logback,换成log4j2,就需要切换日志框架了!

切换日志框架的方法:

  1. 排除原来的
<!--排除原来logback配置-->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
  1. 把log4j2加进来
<!--加入log4j2配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

以上两步在pom.xml里面的配置方式如下

<!--web场景启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!--排除原来logback配置-->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--加入log4j2配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <!--测试场景启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

修改log4j2的默认配置:
log4j2和logback一样,会有一些默认配置,如果需要修改默认配置,方式是和上面logback的方式一样的。

修改log4j2使用指定配置:使用指定配置的方式也是和logback的方式一样,只需要创建指定的文件名的配置文件即可,然后写入配置!

Log4j2配置文件的名字:log4j2-spring.xml或log4j2.xml

log4j2-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="30">
    <!--先定义所有的appender-->
    <appenders>
        <!--这个输出控制台的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
        </console>
        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
        <File name="log" fileName="log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>
        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
        <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>
</configuration>

4. SpringBoot Web

4.1 入门-controller层返回json

  1. Controller层的实现方式有两种:
      1. @Controller
      1. @RestController

注意:@RestController = @Controller + @ResponseBody

  1. 方法的请求方式也有多种:
      1. @GetMapping("/str") = @RequestMapping("/str") = @RequestMapping(value = "/str",method = RequestMethod.GET)
      1. @PostMapping("/str") = @RequestMapping(value = "/str",method = RequestMethod.POST)
      1. @DeleteMapping
      1. @PutMapping
      1. @PatchMapping
  2. 返回的json格式:
      1. json字符串
      1. json对象
      1. json数组
  3. 技巧点:在获取日期的时候对日期进行格式化,只需要在类的get方法上面配置@JsonFormat即可。如下所示
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    public Date getDate() {
        return date;
    }

4.1.1 方式一

controller

package cn.wangningbo.controller;

import cn.wangningbo.domain.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

@Controller//配置controller方式1
@RequestMapping("/json01")
public class JsonController01 {

    //json字符串
    @RequestMapping("/str")
    @ResponseBody
    public String jsonStr() {
        return "test";
    }

    //json对象
    @RequestMapping("/obj")
    @ResponseBody
    public Person jsonObj() {
        return new Person(1L, "zs", new Date());
    }

    //json数组
    @RequestMapping("/array")
    @ResponseBody
    public List<Person> jsonArray() {
        return Arrays.asList(new Person(1L, "二狗", new Date()),
                new Person(2L, "Tom", new Date()));
    }
}

测试

启动app,使用浏览器分别访问地址,查看返回值格式是否正确

4.1.2 方式二

controller

package cn.wangningbo.controller;

import cn.wangningbo.domain.Person;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

@RestController//配置controller方式2
@RequestMapping("/json02")
public class JsonController02 {

    //json字符串
    @RequestMapping("/str")
    public String jsonStr() {
        return "test";
    }

    //json对象
    @RequestMapping("/obj")
    public Person jsonObj() {
        return new Person(1L, "zs", new Date());
    }

    //json数组
    @RequestMapping("/array")
    public List<Person> jsonArray() {
        return Arrays.asList(new Person(1L, "二狗", new Date()),
                new Person(2L, "Tom", new Date()));
    }
}

测试

启动app,使用浏览器分别访问地址,查看返回值格式是否正确

4.2 入门-controller层返回页面

4.2.1 模板引擎分析

4.2.1.1 常见的模板引擎

JSP、Velocity、Freemarker、Thymeleaf

SpringBoot推荐的Thymeleaf;因为其语法更简单,功能更强大;

4.2.1.2 Thymeleaf入门

  1. 引入Thymeleaf

在pom.xml里面配置

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        
    <properties>
        <!--切换thymeleaf版本-->
        <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
        <!-- 布局功能的支持程序  thymeleaf3主程序  layout2以上版本 -->
        <!-- thymeleaf2   layout1-->
        <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
    </properties>
  1. 使用

模板存放位置从ThymeleafProperties类里可以看到

/**
 * Properties for Thymeleaf.
 *
 * @author Stephane Nicoll
 * @author Brian Clozel
 * @author Daniel Fernández
 * @author Kazuki Shimizu
 * @since 1.2.0
 */
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {

    private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;

    public static final String DEFAULT_PREFIX = "classpath:/templates/";

    public static final String DEFAULT_SUFFIX = ".html";

    /**
     * Whether to check that the template exists before rendering it.
     */
    private boolean checkTemplate = true;

只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染

controller

package cn.wangningbo.controller;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
public class ThymeleafController {
    @GetMapping("/hello")
    public ModelAndView hello(Model model) {
        model.addAttribute("hello", "hello test...");
        return new ModelAndView("helloTest");
    }
}

在resources目录下创建一个目录名字为templates,templates目录下放的html文件就可以自动渲染

注意:导入命名空间 xmlns:th="http://www.thymeleaf.org"

resources/templates/helloTest.html

<!DOCTYPE html>
<!--导入命名空间 -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ThymeleafTest</title>
</head>
<body>
<h1>成功了!</h1>
<!--使用语法th:text 将div里面的文本内容设置为 -->
<div th:text="${hello}">这是显示欢迎信息</div>
</body>
</html>
  1. 细节
    • ①th:text;改变当前元素里面的文本内容;
      th:任意html属性;来替换原生属性的值。if判断,forEach等操作!
    • ②表达式
Simple expressions:(表达式语法)
Variable Expressions: ${...}:获取变量值;OGNL;
1)、获取对象的属性、调用方法
2)、使用内置的基本对象:
#ctx : the context object.
#vars: the context variables.
#locale : the context locale.
#request : (only in Web Contexts) the HttpServletRequest object.
#response : (only in Web Contexts) the HttpServletResponse object.
#session : (only in Web Contexts) the HttpSession object.
#servletContext : (only in Web Contexts) the ServletContext object.
${session.foo}
3)、内置的一些工具对象:
#execInfo : information about the template being processed.
#messages : methods for obtaining externalized messages inside variables
expressions, in the same way as they would be obtained using #{…} syntax.
#uris : methods for escaping parts of URLs/URIs
#conversions : methods for executing the configured conversion service (if any).
#dates : methods for java.util.Date objects: formatting, component extraction, etc.
#calendars : analogous to #dates , but for java.util.Calendar objects.
#numbers : methods for formatting numeric objects.
#strings : methods for String objects: contains, startsWith, prepending/appending,
etc.
#objects : methods for objects in general.
#bools : methods for boolean evaluation.
#arrays : methods for arrays.
#lists : methods for lists.
#sets : methods for sets.
#maps : methods for maps.
#aggregates : methods for creating aggregates on arrays or collections.
#ids : methods for dealing with id attributes that might be repeated (for example,
as a result of an iteration).
Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样;
补充:配合 th:object="${session.user}:
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
Message Expressions: #{...}:获取国际化内容
Link URL Expressions: @{...}:定义URL;
@{/order/process(execId=${execId},execType='FAST')}
Fragment Expressions: ~{...}:片段引用表达式
<div th:insert="~{commons :: main}">...</div>
Literals(字面量)
Text literals: 'one text' , 'Another one!' ,…
Number literals: 0 , 34 , 3.0 , 12.3 ,…
Boolean literals: true , false
Null literal: null
Literal tokens: one , sometext , main ,…
Text operations:(文本操作)
String concatenation: +
Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
Binary operators: + , - , * , / , %
Minus sign (unary operator): -
Boolean operations:(布尔运算)
Binary operators: and , or
Boolean negation (unary operator): ! , not
Comparisons and equality:(比较运算)
Comparators: > , < , >= , <= ( gt , lt , ge , le )
Equality operators: == , != ( eq , ne )
Conditional operators:条件运算(三元运算符)
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
Special tokens:
No-Operation: _

4.3 控制器-自动配置

4.3.1 自动配置说明

使用SpringBoot:

  • 1)、创建SpringBoot应用,选中我们需要的模块;
  • 2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
  • 3)、自己编写业务代码;

自动配置原理:

  • 这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?......
  • xxxxAutoConfiguration:帮我们给容器中自动配置组件;
  • xxxxProperties:配置类来封装配置文件的内容;

4.3.2 springmvc自动配置

https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications

Spring Boot 自动配置好了SpringMVC。
以下是SpringBoot对SpringMVC的默认配置:==(WebMvcAutoConfiguration)==

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))
    • ContentNegotiatingViewResolver:组合所有的视图解析器的;
    • ==如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;==
  • Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars

  • Static index.html support. 静态首页访问

  • Custom Favicon support (see below). favicon.ico

  • 自动注册了 of Converter, GenericConverter, Formatter beans.

    • Converter:转换器; public String hello(User user):类型转换使用Converter
    • Formatter 格式化器; 2019.8.25===Date;
@Bean
        @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件中配置日期格式化的规则
        public Formatter<Date> dateFormatter() {
            return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
        }
  • Support for HttpMessageConverters (see below).

    • HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User---Json;
    • HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter;
      ==自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)==
  • Automatic registration of MessageCodesResolver (see below).定义错误代码生成规则

  • Automatic use of a ConfigurableWebBindingInitializer bean (see below).
    ==我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)==
    初始化WebDataBinder;
    请求数据=====JavaBean;

org.springframework.boot.autoconfigure.web:web的所有自动场景;

如果您想保留Spring Boot MVC功能,并且只想添加额外的MVC配置(拦截器、格式化程序、视图控制器等),您可以添加自己的类型为webmvcconfigureadapter的@configuration类,但不需要@enableWebMVC。如果要提供RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,可以声明提供此类组件的WebMVCregistrationSadapter实例。

如果要完全控制SpringMVC,可以添加自己的@configuration,并用@enableWebMVC注释。

4.3.3 静态资源配置

4.3.3.1 引入

开发web中,会有很多的静态资源,由于springboot都是java包的方式,那这些今天资源放到哪儿呢?接下来通过自动配置进行分析!

4.3.3.2 分析

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {
  //可以设置和静态资源有关的参数,缓存时间等

WebMvcAutoConfiguration

@Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
                return;
            }
            Integer cachePeriod = this.resourceProperties.getCachePeriod();
            if (!registry.hasMappingForPattern("/webjars/**")) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler("/webjars/**")
                                .addResourceLocations(
                                        "classpath:/META-INF/resources/webjars/")
                        .setCachePeriod(cachePeriod));
            }
            String staticPathPattern = this.mvcProperties.getStaticPathPattern();
            //静态资源文件夹映射
            if (!registry.hasMappingForPattern(staticPathPattern)) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler(staticPathPattern)
                                .addResourceLocations(
                                        this.resourceProperties.getStaticLocations())
                        .setCachePeriod(cachePeriod));
            }
        }

        //配置欢迎页映射
        @Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(
                ResourceProperties resourceProperties) {
            return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
                    this.mvcProperties.getStaticPathPattern());
        }

       //配置喜欢的图标
        @Configuration
        @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
        public static class FaviconConfiguration {

            private final ResourceProperties resourceProperties;

            public FaviconConfiguration(ResourceProperties resourceProperties) {
                this.resourceProperties = resourceProperties;
            }

            @Bean
            public SimpleUrlHandlerMapping faviconHandlerMapping() {
                SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
                mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
                //所有  **/favicon.ico 
                mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
                        faviconRequestHandler()));
                return mapping;
            }

            @Bean
            public ResourceHttpRequestHandler faviconRequestHandler() {
                ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
                requestHandler
                        .setLocations(this.resourceProperties.getFaviconLocations());
                return requestHandler;
            }

        }
  1. 所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;webjars:以jar包的方式引入静态资源;

    静态资源jar的网址:http://www.webjars.org/。可以从这里找到所需静态资源的jar,拷贝到pom.xml引入即可!

    引入成功后的访问方式jquery举例:http://localhost:8080/webjars/jquery/3.4.1/jquery.min.js

        <!--引入jquery-webjar 在访问的时候只需要写webjars下面资源的名称即可-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.4.1</version>
        </dependency>

html中引入静态资源的方式

<script th:src="@{/webjars/jquery/3.4.1/jquery.min.js}"></script>
  1. "/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射

    • "classpath:/META-INF/resources/",
    • "classpath:/resources/",
    • "classpath:/static/",
    • "classpath:/public/"
    • "/":当前项目的根路径

    localhost:8080/abc 就会去静态资源文件夹里面找abc

    我这里使用的静态资源文件夹是static。所处目录:resources/static。与templates同级目录

  2. 欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;

    localhost:8080 找名字为index的页面

    配置默认访问的页面

  3. 所有的 **/favicon.ico 都是在静态资源文件下找;

    配置若需要给网页顶部设计小图标,只需要在静态文件夹里面存放一个名字为favicon.ico的小图片

    配置网页顶部的小图标
    注意: 改模板不需要重启,按照下面操作一下就好

  • 1)关闭模板缓存
    application.yml
spring:
  thymeleaf:
    cache: false
  • 2)ctrl+f9或者点击Build-->Build Project 重新编译

4.3.4 扩展springmvc配置

有时候想添加自己springmvc的配置比如下,比如拦截器,又或者视图解析器

原理:

  • 1)、WebMvcAutoConfiguration是SpringMVC的自动配置类
  • 2)、在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)
  • 3)、容器中所有的WebMvcConfigurer都会一起起作用;
  • 4)、我们的配置类也会被调用;

效果:SpringMVC的自动配置和我们的扩展配置都会起作用;

实现方式:编写一个自己的配置类,继承于WebMvcConfigurerAdapter即可!==注意:不能标注@EnableWebMvc,这样既保留了所有的自动配置,也能用我们扩展的配置;==

自己写个拦截器,比如模拟一个登录拦截器,继承自HandlerInterceptorAdapter,覆写前置处理和后置处理方法

package cn.itsource.springboot.interceptor;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle......");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle......");
        super.postHandle(request, response, handler, modelAndView);
    }
}

扩展springmvc配置的类

package cn.itsource.springboot.config;

import cn.itsource.springboot.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
//@EnableWebMvc //千万不要写,写了原来的配置就没有用了
public class MyMvcConfig extends WebMvcConfigurerAdapter {

    //添加视图映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

//        @RequestMapper(“/index”)
//        public String success(){
//            return “yhptest”
//        }
        registry.addViewController("/index").setViewName("yhptest");
    }


    //添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addPathPatterns("/**")拦截所有请求, .excludePathPatterns("/login.html","/login","/index")不拦截的请求
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/login.html","/login","/index");
    }
    
}

4.3.5 接管springmvc配置

接管springmvc配置的意思:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了!

使用方法:在配置类中添加@EnableWebMvc即可;

5 XMind总结

springboot-day2-xmind.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 1.Spring核心 2.Spring注解编程-IOC2.1 引入2.2 环境准备2.3 传统方式获取bean2....
    程序员Darker阅读 410评论 0 0
  • SpringMVC原理分析 Spring Boot学习 5、Hello World探究 1、POM文件 1、父项目...
    jack_jerry阅读 1,265评论 0 1
  • springboot 概述 SpringBoot能够快速开发,简化部署,适用于微服务 参考嘟嘟大神SpringBo...
    一纸砚白阅读 5,384评论 2 20
  • https://github.com/cuzz1/springboot-learning 一、Spring Boo...
    cuzz_阅读 3,435评论 1 6
  • SpringBoot基础 学习目标: 能够理解Spring的优缺点 能够理解SpringBoot的特点 能够理解S...
    dwwl阅读 5,439评论 4 81