spring boot 学习笔记

Day 1

1. JDK版本之间的差异相关问题

2. 怎么通过maven设置构建项目的jdk版本:profiles

3. spring boot应用如何打包成一个可运行的jar包

要创建一个可执行jar,我们需要将spring-boot-maven-plugin添加到我们的pom.xml中。要做到这一点,请在dependencies部分下面插入以下几行:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

4. spring boot starter是什么,它有哪些类型的starter

https://docs.spring.io/spring-boot/docs/2.4.0-SNAPSHOT/reference/html/using-spring-boot.html#using-boot

5. spring boot为什么能从application.properties或者yaml文件中取配置

应该是从在启动类底层代码中固定的配置文件名称

6. 怎么通过spring boot获取配置文件中的值、yaml支持的数据类型

要加@Compent将类标记为容器中的一个组件

注解 org.springframework.boot.context.properties.ConfigurationProperties,需要匹配配置文件中的前缀prefix

用法示例:

@ConfigurationProperties(prefix = "person")

@ConfigurationProperties(prefix = "person")
@Compent
public Class Person {
    private String key1;
    private String key2;
    // set get ...
}

在yaml文件中就以:

person:
    key1: value1
    key2: value2

使用@Value

spring boot 学习笔记

7. @Autowire和@Resource区别

1.@Autowire是Spring开发的,而@Resource是jdk开发的

2.@Autowire是按照type来注解的,而@Resource是按照名称来的,如果名称找不到,那么就按照type

截至

https://www.bilibili.com/video/BV1gW411W76m?p=13

从14开始看

Day 2

1. @PropertySource

默认是从根目录的application.properties或application.yaml获取,可以通过@PropertySource指定配置文件目录和名称

2. @ImportResource

@ImportResource(locations = {"classpath:bean.xml"})

可以支持Spring时期使用的xml配置方式

3. @Configuration+@Bean的便捷方式往IOC容器中配置组件

spring boot 学习笔记

4. 配置文件占位符

支持随机数,包括整数、字符串uuid等

spring boot 学习笔记

5. Profile

激活方式

spring.profiles.active=dev

对应application-dev.properties/yml

yml文件可使用---来分隔文档块,每一块都可以理解为一个profile的配置块,指定active后对应文档块的配置生效

spring boot 学习笔记

也可以使用启动的参数配置, java -jar x.jar--spring.profiles.active=dev

6. 配置文件的优先级,以下四个目录都会加载,由高到低,高覆盖低,可以利用优先级实现互补配置

spring.config.location可以用作命令行参数

spring boot 学习笔记

7. 自动配置

截至:

https://www.bilibili.com/video/BV1gW411W76m?p=19

Day 3

1. @Conditional

当满足某个条件时,才往容器中注入该Bean

spring boot 学习笔记

2. 什么是SpEL表达式

SpEL(Spring Expression Language),即Spring表达式语言。它是一种类似JSP的EL表达式、但又比后者更为强大有用的表达式语言。

为什么要用SpEL:因为它可以在spring容器内实时查询和操作数据,尤其是操作List列表型、Array数组型数据。所以使用SpEL可以有效缩减代码量,优化代码结构,笔者认为很有用

3. 配置debug启动

spring boot 学习笔记

启动项目,控制台打印自动配置报告,方便得知哪些自动配置类生效

Positive matches:启用的、匹配上的自动配置类

Negative matches:没有启动、没有匹配上的自动配置类

4. 日志介绍

SLF4J是一种抽象,它对应的实现有Log4J/Logback,抽象和两种实现都出自同一人之手

Logback是继Log4J出现性能问题后的另实现的一套框架

SLF4J理解为一种抽象、公认的规范,你系统要记录日志,我这个规范能满足你、也能约束你实现的更好,而实现这一规范就按需进行了,世面上有些已有的,开发人员也可根据自己需要自己按规范实现,slf4j-api.jar就是规范的引入,再往下一层引入该规范的实现,实现比如null、logbak、log4j、java.util .logging等等

spring boot 学习笔记

每个框架都有自己的日志用法,可能每种框架都用各自不同的日志框架,而Spring Boot这样的框架是一个集结多种框架于一体的开发解决方案,比如它有Spring/MyBatis/Hibernate/MQ...,各自用各自的日志就太过于混乱,slf4j提供了一套日志统一解决方案,大致原理如下:

  • 已知市面上流行的所有日志框架
  • 将它们与SLF4J进行一个转换,比如ALogging框架中的XX类,在其对应转换的Jar包中有同样的接口名称,但是其对应的实现类就用SLF4J的实现类

这样的替换包流行的有以下几种:

jcl-over-slf4j
jul-to-slf4j
log4j-over-slf4j

spring boot 学习笔记

5. spring boot 日志场景、启动器

spring boot日志相关的底层依赖关系

  • spring boot 底层使用slf4j+logback方式进行日志记录
  • 将其他框架中的日志依赖转换为slf4j
  • 排除掉每个框架默认的日志,排除示例如下
<dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-console</artifactId>
        <version>${activemq.version}</version>
        <exclusions>
          <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
          </exclusion>
        </exclusions>
    </dependency>

spring boot 学习笔记

在正常开发中,日志对应的级别,spring boot默认设置为info,可以指定报名改变日志打印级别,级别分高低,如图

spring boot 学习笔记

# 指定报名设置日志打印级别,以下是设置了com.nobt包下的类打印都以trace级别打印
logging.level.com.nobt=trace

# 指定日志文件目录
logging.path=/spring/log

# 不指定路径在当前项目下生成springboot.log日志
# 可以指定完整的路径
logging.file=G:/springboot.log

官方文档中有这么一句:

If you want to write log files in addition to the console output you
need to set a logging.file or logging.path property

也就是说,它们不会同时生效,只配置其中一个就好了。

例如我配置的时候只指定path

logging.path = ./logs

那么记录日志的时候默认的文件名是:spring.log

截至

https://www.bilibili.com/video/BV1gW411W76m?p=26

Day 4

自己搭建的hello world maven 工程Junit一直不正常,故拉取 ityouknow 的教程合集,看了几个快速简单的,跟一两年前一样,上手写业务代码都是照葫芦画瓢,但是细节上的配置还需要熟悉,比如 maven 对应的好些配置,是不了解、不知道怎么配置的。

<project>  
  <modelVersion>4.0.0</modelVersion>  
<!--maven2.0必须是这样写,现在是maven2唯一支持的版本-->  
  <!-- 基础设置 -->  
  <groupId>...</groupId>  
  <artifactId>...</artifactId>  
  <version>...</version>  
  <packaging>...</packaging>  
  <name>...</name>  
  <url>...</url>  
  <dependencies>...</dependencies>  
  <parent>...</parent>  
  <dependencyManagement>...</dependencyManagement>  
  <modules>...</modules>  
  <properties>...</properties>  
  
  <!--构建设置 -->  
  <build>...</build>  
  <reporting>...</reporting>  
  
  <!-- 更多项目信息 -->  
  <name>...</name>  
  <description>...</description>  
  <url>...</url>  
  <inceptionYear>...</inceptionYear>  
  <licenses>...</licenses>  
  <organization>...</organization>  
  <developers>...</developers>  
  <contributors>...</contributors>  
  
  <!-- 环境设置-->  
  <issueManagement>...</issueManagement>  
  <ciManagement>...</ciManagement>  
  <mailingLists>...</mailingLists>   
  <scm>...</scm>  
  <prerequisites>...</prerequisites>  
  <repositories>...</repositories>  
  <pluginRepositories>...</pluginRepositories>  
  <distributionManagement>...</distributionManagement>  
  <profiles>...</profiles>  
</project>
  1. fork ityouknow 的spring boot教程并拉取本地环境搭建
https://gitee.com/nobt/spring-boot-examples

Day 5

spring boot admin 和 spring boot actuator

这两者的关系描述:Spring Boot Admin就是将Spring Boot Actuator中提供的endpoint信息可视化表示,在BookPub应用(被监控)的这一端,只需要进行一点配置即可。

也就是说Admin 依赖 Actuator,从pom 依赖关系中找到体现:

<!-- spring-boot-admin-server.pom -->

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      <version>2.1.0.RELEASE</version>
      <scope>compile</scope>
 </dependency>

Admin 可以做以下功能:监控你的 Spring Boot 程序,支持异常邮件通知

大体上有个了解,细节上的API、用法需要时再查阅

spring 断言

用法示例

spring的底层也较多的用到了该用法,其实就是个方法抽离的用法

org.springframework.util.Assert 的注释如下:

断言实用工具类,帮助验证参数。

有助于在运行时早期和清晰地识别程序员错误。

例如,如果一个公共方法的契约声明它不存在,允许{@code null}参数,{@code Assert}可以用来验证。这样做就清楚地表明违反了约定发生并保护类的不变量。

通常用于验证方法参数,而不是配置属性。

带着疑问阅读源码

BeanA中注入BeanB,或者依赖注解@DependsOn 他们在容器注册Bean时怎么确认这个先后顺序

getBean、doGetBean、createBean、doCreateBean

banner在哪个环节加载的、加载目录、文件名称

启动的第一步就是去找banner

// 1. 启动类的构造方法中
// org.springframework.boot.SpringApplication.run(String...)

    public ConfigurableApplicationContext run(String... args) {
            ...
            Banner printedBanner = printBanner(environment);
            ...
        }
        return context;
    }
// 2. org.springframework.boot.SpringApplication.printBanner(ConfigurableEnvironment)

// 3. org.springframework.boot.SpringApplicationBannerPrinter.print(Environment, Class<?>, PrintStream)

// 4. org.springframework.boot.SpringBootBanner.printBanner(Environment, Class<?>, PrintStream)
// 会有获取的操作,获取不到就打印默认的:
// 此方法中会循环打印静态变量:org.springframework.boot.SpringBootBanner.BANNER,默认就是spring的banner样式

class SpringBootBanner implements Banner {

    private static final String[] BANNER = { "",
            "  .   ____          _            __ _ _",
            " /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\",
            "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
            " \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )",
            "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
            " =========|_|==============|___/=/_/_/_/" };

    private static final String SPRING_BOOT = " :: Spring Boot :: ";

    private static final int STRAP_LINE_SIZE = 42;

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass,
            PrintStream printStream) {
        for (String line : BANNER) {
            printStream.println(line);
        }
        String version = SpringBootVersion.getVersion();
        version = (version == null ? "" : " (v" + version + ")");
        String padding = "";
        while (padding.length() < STRAP_LINE_SIZE
                - (version.length() + SPRING_BOOT.length())) {
            padding += " ";
        }

        printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT,
                AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version));
        printStream.println();
    }

}

配置文件从哪读取的、配置文件所在位置优先级定义、配置文件名称定义、properties/yml的解析逻辑

CSDN说明环境变量配置文件实现类

properties和xml对应一个实现类:org.springframework.boot.env.PropertiesPropertySourceLoader

yml和yaml对应一个实现类:org.springframework.boot.env.YamlPropertySourceLoader

国际化如何使用、配置、解析

// Initialize message source for this context.
initMessageSource();

能否理清refresh 方法逻辑走向、子父类关系

org.springframework.context.support.AbstractApplicationContext.refresh()

spring boot默认的server.port为什么是8080

server.port指定web服务的端口号,只要是application或者是yml文件中支持配置的,都会有对应的属性类进行获取配置的这些值,获取到后进行相应设置,所有的配置都有默认值,这从哪体现呢,从spring-boot-hello-world 的程序中不添加application配置文件或文件中为空,程序仍然正常启动

而server.port 对应的属性类文件是:org.springframework.boot.autoconfigure.web.ServerProperties.class

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)

加上 @ConfigurationProperties 代表该类是一个属性文件类,prefix 指定该类从配置文件中解析相关配置的前缀,也就是诸如 server.xxx 样的配置,将都会被它一一对应,比如 server.port 那在该类中就有个 port 字段,如果在配置文件中配了一个该 server.yyy,这个yyy在该类中没有对应的字段绑定,可能会导致报错,所以,加上了注解属性:ignoreUnknownFields = true 意为忽略未知字段

而当你不配置server.port时,它默认使用8080,是为什么,来按这个顺序看看在spring boot启动时创建Tomcat的逻辑:

1. org.springframework.context.support.AbstractApplicationContext.onRefresh()

2. org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh()

3. org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer()

4. org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory()

5. 再走到这
// 它根据ConditionalOnClass和ConditionalOnMissingBean来控制是否向容器中注入该Bean,这就是在创建一个Tomcat的
    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {

        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

    }

6. 再后置处理器将applicaiton或yml文件中有关tomcat的配置给Tomcat设置上
org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer.customize(ConfigurableTomcatWebServerFactory factory)

7. 在此过程中它默认使用的是Tomcat,将会创建一个Tomcat对象,而Tomcat对象中,port字段默认值就是8080

spring boot 学习笔记

为什么默认8080,总结为一句话:因为Tomcat端口默认值就是8080

所以一开始的猜测,它是在哪默认配置的,比如通过server.port,不是的

还全局搜索搜索到了 /META-INF/spring-configuration-metadata.json 里面有server.port 描述为8080,其实不是,这是在properties或yml配置文件中添加配置时的提示来源,看看官方的介绍:元数据官方解释

该提示功能需要在pom.xml中引入依赖:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-configuration-processor</artifactId>
     <optional>true</optional>
 </dependency>

精美的类设计

org.springframework.boot.autoconfigure.web.ResourceProperties

它的功能就是从配置文件中映射spring.resources前缀的配置,到这些属性里面去,这样嵌套的静态内部类

spring boot 学习笔记

如果让我写,我现在也只知道按类名区分开来写,但是写完后,spring.resources就套不了娃了,他这样可以套娃,用一个类可以支持resources这一类的所有配置,还清晰分层

设计模式

一直以来,设计模式都是面试的高频词汇,可能简单的总是就说单例、工厂等,但是如果想读懂spring里面各个功能,比如较为复杂的后置处理器,需要学习设计模式后加上手写Spring等教程

上一篇:SQL日常


下一篇:Windows 2012 DC 同步异常处理