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
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
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容器中配置组件
4. 配置文件占位符
支持随机数,包括整数、字符串uuid等
5. Profile
激活方式spring.profiles.active=dev
对应application-dev.properties/yml
yml文件可使用---来分隔文档块,每一块都可以理解为一个profile的配置块,指定active后对应文档块的配置生效
也可以使用启动的参数配置, java -jar x.jar--spring.profiles.active=dev
6. 配置文件的优先级,以下四个目录都会加载,由高到低,高覆盖低,可以利用优先级实现互补配置
spring.config.location可以用作命令行参数
7. 自动配置
截至:
https://www.bilibili.com/video/BV1gW411W76m?p=19
Day 3
1. @Conditional
当满足某个条件时,才往容器中注入该Bean
2. 什么是SpEL表达式
SpEL(Spring Expression Language),即Spring表达式语言。它是一种类似JSP的EL表达式、但又比后者更为强大有用的表达式语言。为什么要用SpEL:因为它可以在spring容器内实时查询和操作数据,尤其是操作List列表型、Array数组型数据。所以使用SpEL可以有效缩减代码量,优化代码结构,笔者认为很有用
3. 配置debug启动
启动项目,控制台打印自动配置报告,方便得知哪些自动配置类生效
Positive matches:启用的、匹配上的自动配置类Negative matches:没有启动、没有匹配上的自动配置类
4. 日志介绍
SLF4J是一种抽象,它对应的实现有Log4J/Logback,抽象和两种实现都出自同一人之手
Logback是继Log4J出现性能问题后的另实现的一套框架
SLF4J理解为一种抽象、公认的规范,你系统要记录日志,我这个规范能满足你、也能约束你实现的更好,而实现这一规范就按需进行了,世面上有些已有的,开发人员也可根据自己需要自己按规范实现,slf4j-api.jar
就是规范的引入,再往下一层引入该规范的实现,实现比如null、logbak、log4j、java.util .logging等等
每个框架都有自己的日志用法,可能每种框架都用各自不同的日志框架,而Spring Boot这样的框架是一个集结多种框架于一体的开发解决方案,比如它有Spring/MyBatis/Hibernate/MQ...,各自用各自的日志就太过于混乱,slf4j提供了一套日志统一解决方案,大致原理如下:
- 已知市面上流行的所有日志框架
- 将它们与SLF4J进行一个转换,比如ALogging框架中的XX类,在其对应转换的Jar包中有同样的接口名称,但是其对应的实现类就用SLF4J的实现类
这样的替换包流行的有以下几种:
jcl-over-slf4j
jul-to-slf4j
log4j-over-slf4j
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默认设置为info,可以指定报名改变日志打印级别,级别分高低,如图
# 指定报名设置日志打印级别,以下是设置了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>
- 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的解析逻辑
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
为什么默认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.resources就套不了娃了,他这样可以套娃,用一个类可以支持resources这一类的所有配置,还清晰分层
设计模式
一直以来,设计模式都是面试的高频词汇,可能简单的总是就说单例、工厂等,但是如果想读懂spring里面各个功能,比如较为复杂的后置处理器,需要学习设计模式后加上手写Spring等教程