SpringBoot学习笔记

positive:正确的,积极的

negative:否认的,消极的

navigate:导航

navicat:数据库连接软件

candidate:候选者

detect:检测

indicate:表明

indentation:缩进

feature:特征

        • 1.前言
        • 2.SpringBoot项目的创建
        • 3.关于SpirngBoot的pom文件继承的两种写法
          • 3.1 继承方式
          • 3.2 导入方式
        • 4.SpringBoot配置文件的加载顺序与优先级
        • 5.yml配置文件的编写、切换、读取
          • 5.1 语法格式
          • 5.2 属性值编写与读取
          • 5.3 配置文件相关注解
          • 5.4 profile切换
        • 6.自动配置原理分析
        • 7.日志
          • 7.1 市面上的日志选择
          • 7.2 官方slf4j入门案列
          • 7.3 SLF4J官方介绍
          • 7.4 如何让系统中所有的日志都统一到slf4j
          • 7.5 SpringBoot日志关系
          • 7.6 SpirngBoot日志使用
          • 7.7 SpringBoot日志框架的切换
          • 7.8 log4j2配置文件详解
        • 8.Web开发
          • 8.1 静态资源文件的映射
          • 8.2 Thymeleaf
          • 8.3 webMVC自动配置原理与定制

1.前言

该笔记仅仅只是记录自己在观看SpringBoot视频的学习过程中,我认为自己需要记忆的,自己记不住的一些知识点。所以笔记可能比较松散简单,有关任何SpringBoot的知识,欢迎大家留贴讨论。

2.SpringBoot项目的创建

通过idea的Spring Initialzer来快速选择需要的模块功能,然后idea会自动生成SpringBoot项目


3.关于SpirngBoot的pom文件继承的两种写法

3.1 继承方式
因pom文件是单继承的,所以局限性比较大
<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.0.2.RELEASEversion>
    <relativePath/> 
parent>
3.2 导入方式
这种方式就非常好了,这样pom文件还可以继续继承一个自己公司的
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-dependenciesartifactId>
            <version>2.0.2.RELEASEversion>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

4.SpringBoot配置文件的加载顺序与优先级

.properties的优先级大于.yml

.properties的配置和.yml的配置全部都会加载,高优先级会覆盖低优先级,并且形成互补配置

默认会先加载不带profile标识的配置文件,然后如果默认配置文件中有设置spring.profile.active属性,则加载对应的配置文件,注意,此时的配置文件优先级会比默认的高!!例如在application.yml中配置端口为8081,配置spring.profile.active为dev,那么如果在application-dev.yml中如果配置了8082,那么启动时端口为8082

意思就是制定spring哪个配置文件是活跃的,既然是活跃的,那么优先级肯定最高了!

优先级1最大,4最小,所有位置的配置文件都会加载,高优先级会覆盖低优先级的值

  1. 项目根/config
  2. 项目根
  3. classpath:/config
  4. classpath:

还有一种在启动springboot项目的时候指定配置项spring.config.location,这种是最屌的,可以覆盖项目项目启动后已经有的值。简单的说就是可以不用修改项目代码,只要重启指定配置文件的位置,就可以了。这个位置可以是任何位置。一般运维会用用。

5.yml配置文件的编写、切换、读取

5.1 语法格式

k: v 一个k加冒号然后空格然后再写值,空格很重要!!

如果有多级的,另起一行然后再打几个空格,打几个空格无所谓,只要同一级的属性对其即可!!

5.2 属性值编写与读取

实体对象

@Data
@Component
@ConfigurationProperties("person")
public class Person {
    private Integer age;
    private String name1;
    private String name2;
    private String name3;
    private Boolean isStudent;
    private Double score;
    private Date birthDay;
    private String[] arr;
    private List list;
    private Map map;
    private String foo;
    private String random;
}

yml配置

字面量:字符串,数字,布尔,日期等

复合类型:list,map

占位符,随机数的写法

#字面量
person:
  age: 18
#  字符串无需加单双引号,双引号会进行转义,单引号不会进行转义(是啥就是啥)
  name1: zhangsan1 \n lisi
  name2: 'zhangsan3 \n lisi'
  name3: "zhangsan2 \n lisi"
  isStudent: false
  score: 99.5
#  日期格式应该支持绑定很多种格式
  birthDay: 2018/6/10 11:15:20
  arr:
  - v1
  - v2
#  也可以像下面这种一行的写法
  list: [v1,v2,v3]
#  map的写法和对象是差不多的
#  map:
#    key1: v1
#    key2: v2
  map: {key1: v1,key2: v2}
#  占位符的方式,可以获取系统中其他的配置量
  foo: ${person.name1}+今年+${person.age}
#  自带的简单随机数获取
#  random: ${random.long}
#  random: ${random.int}
#  random: ${random.value}
#  random: ${random.int(10,20)}
  random: ${random.uuid}

测试输出

5.3 配置文件相关注解

@ConfigurationProperties:可以进行属性的批量配置,只需指定配置文件的前缀即可!注意,需要配置属性的类,必须是一个组件,即必须标有@Component,或者在某个组件(配置类)的地方声明了@EnableConfigurationProperties(加入该bean的class),该注解会自动把标有@ConfigurationProperties的类加入到spring容器进行管理。总结的说就是,@EnableConfigurationProperties注解会把该注解指定的类(如果该类标记有@ConfigurationProperties,即使该类不标记@Component)注册到spring容器中,如果@EnableConfigurationProperties不指定class类,那么他仅仅只是开启这个功能,他会在spring容器中查找各个带有@ConfigurationProperties注解的组件使其生效。

@Configuration 
public class Config {

     private Foo foo;

    //如果一个组件他有且仅有一个构造函数,那么不管他这个构造函数有几个参数
    //他会自动在容器中查找所需要的组件进行组装。
     public Config(Foo foo){
         this.foo=foo;
     }
 }

“`java
@Configuration
public class Config {

 //或者这种注入,我比较喜欢这种
 @Autowired
 private Foo foo;

}
“`

该注解还可以添加@Validated,那么下面的属性就会进行JSR303标准的校验。

@Data
@Component
@ConfigurationProperties("person")
//开启数据校验,将配置文件中的数据绑定到该类时会进行数据的校验工作
@Validated
public class Person {
    private Integer age;
    //name1属性必须是一个合法的email地址,否则会报错
    @Email
    private String email;
}

@PropertySource@Value

同样的@PropertySource也要在组件上才能生效!

第一个注解可以加载配置文件,任意位置,任意名称的配置文件。

第二个注解可以对类的属性值进行写入,可以是常量,可以读取当前容器所在环境中已存在的配置量,可以是SpEL表达式

@Data
@Component
@PropertySource("classpath:student.properties")
public class Student {
    @Value("zhangsan")
    private String name;
    @Value("${student.age}")
    private Integer age;
    @Value("#{56+24.4}")
    private Double score;
}
5.4 profile切换

编写格式:application-{profile}.properties/yml

启用方式: 在默认的application.properties/yml中设置spring.profile.active={profile},来指定

其他启用方式:命令行,jar指定运行参数,设置系统变量等等有需要可以查

6.自动配置原理分析

SpringBoot开启自动配置的关键入口是注解@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

该注解又有两个注解,分别是:

@AutoConfigurationPackage:这个注解的作用是扫描@EnableAutoConfiguration注解所在的类的包下的所有子孙包,将标有@Component注解的主键扫描进ioc容器中。其底层实现也是通过@Import注解来实现

@Import(AutoConfigurationImportSelector.class):这个注解的作用是,将后边选择器所返回的类注册到ioc容器中。

AutoConfigurationImportSelector:自动配置导入选择器,这个类是实现自动配置的核心,里面有一个重要的方法,selectImports(),该方法返回字符串数组,该数组其实就是需要注册到spirng容器中的各种configuration类

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
        .loadMetadata(this.beanClassLoader);
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //核心方法,getCandidateConfigurations,获取候选的配置类。
    List configurations = getCandidateConfigurations(annotationMetadata,
                                                             attributes);
    configurations = removeDuplicates(configurations);
    Set exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return StringUtils.toStringArray(configurations);
}

继续点getCandidateConfigurations方法进去看:

protected List getCandidateConfigurations(AnnotationMetadata metadata,
                                                  AnnotationAttributes attributes) {
    //这里就不再点进去看了,大概意思是指定配置文件的位置,key值,classLoader等一些信息
    //其中getSpringFactoriesLoaderFactoryClass()方法返回的就是EnableAutoConfiguration的全类目,以该全类目为key
    //然后在当前项目所有jar包的类路径下得META-INF/spring.factories去匹配该key对应的value,即能够被返回的所有的配置类的全类名
    List configurations = SpringFactoriesLoader.loadFactoryNames(
        getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations,
                    "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

protected Class getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

以下是autoConfiguration包下的spring.factories

虽然是加载所有的jar包下得META-INF/spring.factories,但是因为前面已经指定了key键,也就是getSpringFactoriesLoaderFactoryClass()这个方法,返回的值为:EnableAutoConfiguration.class,该class对应的全类名,也就是key键就是:org.springframework.boot.autoconfigure.EnableAutoConfiguration

至此,所有的配置类全部扫描出来,但是到目前为止,这么多配置类并不是都加入了ioc容器。SpringBoot还要再做一层处理。这一层处理就是判断每一个配置类加载的条件,然后来决定是否加入ioc容器。这边我以简单的HttpEncodingAutoConfiguration配置类,来做介绍,其他配置类的加载条件是差不多的。

//配置类的标记
@Configuration
//将HttpEncodingProperties这个类开启自动属性的绑定,并加入到ioc容器中
@EnableConfigurationProperties(HttpEncodingProperties.class)
//这三个都是条件判断的注解,只有满足条件,那么当前配置类才会生效,即被spring容器所管理
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

    private final HttpEncodingProperties properties;

    //当只有一个有参构造时,参数会自动从容器中获取
    public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnMissingBean
    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;
    }

    ......省略

}

可以看到,一开始返回的那么多xxxxAutoConfiguration类并不是全部都进行注册,而是会加一些判断,比如是否是web环境?是否有什么什么类已经存在?等等。通过这些限制条件,spring会自动帮我们把我们当前项目环境所需要的配置自动加载!!也就是SpringBoot会自动帮我们检测当前项目环境,都有哪些jar包被导入,jdk版本等等,然后来决定要注册哪些xxxAutoConfiguration,并注册这些自动配置类里面配置的组件,从而实现了自动配置。

一般来说每个xxxxAutoConfiguration会对应一个xxxxProperties的实体类(该实体类标有@ConfigurationProperties,并被xxxxAutoConfiguration类标有的@EnableConfigurationProperties所指定),即该xxxxProperties实体类被纳入了容器进行管理。

@Conditional派生注解(Spring注解版原生的@Conditional作用)

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

@Conditional扩展注解 作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean;
@ConditionalOnMissingBean 容器中不存在指定Bean;
@ConditionalOnExpression 满足SpEL表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项

可以在配置文件中设置debug=true,那么在springboot启动的时候就会打印出哪些类被配置了!

7.日志

7.1 市面上的日志选择

JCL、slf4j、Jboss-logging、JUL、log4j、logback、log4j2、….

日志门面 (日志的抽象层) 日志实现
JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-logging Log4j JUL(java.util.logging) Log4j2 Logback

log4j,Logback,SLF4J都是同一个人写的!!!

左边选一个门面(抽象层)、右边来选一个实现;

日志门面: SLF4J;

日志实现:Logback;

Spring框架默认是用JCL

SpringBoot框架默认是用SLF4J和Logback

开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;

7.2 官方slf4j入门案列
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}
7.3 SLF4J官方介绍

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

7.4 如何让系统中所有的日志都统一到slf4j

如何统一项目中各个框架使用不同日志的问题,让各个框架全部统一使用SLF4J,这个官网的介绍

  1. 将系统中其他日志框架先排除出去;
  2. 用中间包来替换原有的日志框架;
  3. 我们导入slf4j其他的实现(xxxx-over-slf4j.jar)
7.5 SpringBoot日志关系

主要起作用的是这个依赖里面的logging依赖

<dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starterartifactId>
dependency>

<dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-loggingartifactId>
dependency>

底层依赖关系

总结:

​ 1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录

​ 2)、SpringBoot也把其他的日志都替换成了slf4j;

​ 3)、中间替换包(类名和包名和原来一模一样!但是里面的内容不一样,类似tk.mapper)

​ 4)、如果我们要引入其他框架,一定要把这个框架的默认日志依赖移除掉.

​ Spring框架用的是commons-logging;

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <exclusions>
                <exclusion>
                    <groupId>commons-logginggroupId>
                    <artifactId>commons-loggingartifactId>
                exclusion>
            exclusions>
        dependency>

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

7.6 SpirngBoot日志使用
logging.file logging.path Example Description
(none) (none) 只在控制台输出
指定文件名 (none) my.log 输出日志到my.log文件
(none) 指定目录 /var/log 输出到指定目录的 spring.log 文件中
#logging.level是一个map,key为包名,value为日志输出的级别
#如果没有指定默认级别,则用SpringBoot默认的,info级别
#file和path属性指定其一就可以了,如果都指定中生效file
logging:
#map的写法也可以这样写
#  level: {com: warn,com.yckj: error}
  level:
   com: error
   com.yckj: trace

#   不指定路劲,"my.log",只指定文件名,则默认在项目的根目录生成该日志文件
#   如果带斜杠,"/my.log",则在项目所在的盘符根目录生成
#   或者使用全路劲,"E:/springlog/my.log"
#  file: E:/springlog/my.log
#指定路劲,然后文件名为SpringBoot默认的"spring.log"
#如果不加/,那么就是在当前项目的根目录创建文件夹,并在该文件夹内创建spring.log文件
#如果加了/,那么就是以项目所在的磁盘为根目录,创建文件夹
  path: spring/log
#  注意,在用yml写pattern的时候请使用双引号或者单引号扩起来,不然会解析不了
  pattern:
#  设置控制台输出的样式
    console: '%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n'
#    这是输出到文件中的样式
    file: '%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n'
@SpringBootApplication
@Slf4j
public class SpringBootLogApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootLogApplication.class, args);
        //日志的级别  trace

        log.trace("这是trace");
        log.debug("这是debug");

        //SpringBoot默认级别是info的,所以trace和debug信息不输出
        //设置为什么级别,就是该级别及更高级别的日志才会被输出
        log.info("这是info");
        log.warn("这是warn");
        log.error("这是error");
    }
}

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

以上的配置只是简单的设置下日志的输出配置,若要定制,则需要自己加入相对应的日志实现框架的配置文件。SpringBoot下非常的简单,这是官方文档的说明。

带”-spring”标识的配置文件可以设置日志在不同环境下输出的格式。

log4j2.xml:直接就被日志框架识别了;

log4j2-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能

例如:

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    
    <layout class="ch.qos.logback.classic.PatternLayout">
        <springProfile name="dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%npattern>
        springProfile>
        <springProfile name="!dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%npattern>
        springProfile>
    layout>
appender>

总结:如果什么都不配置,仅仅只需要在application.yml中简单的设置下,比如设置下输出到控制台的格式啊,输出文件的格式啊,输出的文件去哪里啊,日志的级别啊。比如像下面这个图中的配置

当然,这并不能满足我们复杂的业务需求。这个时候我们就需要加入不同的日志框架所对应的不同的配置文件名。这个在上面已经截图了。(SpringBoot官方建议写带”-spring”后缀的,这样可以开始高级profile的功能)。SpringBoot默认会从类路径下加载,当然你也可以放到任何地方,然后配置一下logging.config然后指定下配置文件的位置即可!比如像这样

logging:
  config: classpath:log/logback.xml

这样就可以加载到了,然后再日志的配置文件中进行配置。这边我司项目主要用log4j2,所有我这边就贴下log4j2相关的配置了。

7.7 SpringBoot日志框架的切换

将SpringBoot默认的slf4j+logback切换到slf4j+log4j2

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
    
    <exclusions>
        <exclusion>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-loggingartifactId>
        exclusion>
    exclusions>
dependency>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-log4j2artifactId>
dependency>

然后在项目中添加log4j2-spring.xml的配置文件即可!

IDE可以打开pom的依赖关系,然后进行不需要的jar包的排除

7.8 log4j2配置文件详解

引用下其他人的配置文件,我就不再说明了。有需要的可以慢慢看,或者自行谷歌百度。

8.Web开发

8.1 静态资源文件的映射

在WebMvcAutoConfiguration中有这样一个方法:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
    CacheControl cacheControl = this.resourceProperties.getCache()
        .getCachecontrol().toHttpCacheControl();
    //添加/webjars/的资源映射,例如jquery可以以maven导包的形式导入工程。
    //项目启动后访问:http://localhost:8081/webjars/jquery/3.1.0/jquery.js
    //springboot会把webjars后面的访问路径转发到项目类路径下或当前项目所有jar包的类路劲下的
    //META-INF/resources/webjars/下来进行访问。可以看下面我发的两张图
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry
                                             .addResourceHandler("/webjars/**")
                                             .addResourceLocations("classpath:/META-INF/resources/webjars/")
                                             .setCachePeriod(getSeconds(cachePeriod))
                                             .setCacheControl(cacheControl));
    }
}

我先在自己项目的类路径下创建如下文件夹和资源文件

然后我就可以通过浏览器进行访问了

当然以上的方式还是比较麻烦的。SpringBoot默认为我们设置好了一些静态资源的路径。

//这个是WebMvcAutoConfiguration的addResourceHandlers方法的里面一部分
//主要作用就是对访问路劲"/**"做映射,映射的路径就是下面那个路径。
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
            if (!registry.hasMappingForPattern(staticPathPattern)) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler(staticPathPattern)
                                .addResourceLocations(getResourceLocations(
                                        this.resourceProperties.getStaticLocations()))
                                .setCachePeriod(getSeconds(cachePeriod))
                                .setCacheControl(cacheControl));
            }
"classpath:/META-INF/resources/", 
"classpath:/resources/",
"classpath:/static/", 
"classpath:/public/" 
"/":当前项目的根路径

综上,我们可以在项目的类路劲下创建以上这些文件,然后把静态资源放到这里面即可!比第一种要方便很多。

WebMvcAutoConfiguration还可以配置欢迎页面,网站标题的图标。在welcomePageHandlerMapping和FaviconConfiguration.faviconHandlerMapping方法中设置。

8.2 Thymeleaf

SpringBoot推荐使用的模板引擎,因我司都是前后端完全分离的,所以完全不用写html+css+js。

这边贴一个官网的文档。上面介绍的非常详细,若日后有需要,可以看看。官方PDF文档

8.3 webMVC自动配置原理与定制

首先我们看看官方文档中介绍webMVC自动配置了哪些功能:官方Web开发模块

SpringBoot学习笔记_第1张图片

你可能感兴趣的:(SpringBoot)