1.根据 componentScan("包名")
扫描所有 jar包,和本地项目
注意,spring启动类包及其子包都会扫描到,指的不仅仅是本地项目的,jar包也是
本质是 componentScan(“启动类本包的String”),componentScan("启动类所在包子包的String")
所以在 封装jar包的时候(特别是Springboot项目的),要想这个jar包的类可以 被加入
到ioc容器中的话,那么涉及的包名 要和本地项目启动类所在包和子包名一样
那么就可以扫描的,不然就扫描不到
2.扫描所有 MEFI/INF下的 spring.factories的类
实际的作用,就是将某个服务的jar包,可以引入 External Libarary中
这样才能用 这个服务的东西,这个jar包也受本项目 spring ioc加载的 控制
1.spring-boot-stater-prarent,里面 manager了很多的依赖
2.显示调用你想用的依赖即可,如果想要 换版本号,可以在自己的 pom文件里,加,采用就近原则会采用这个
3.springboot就是管理了你需要的all 依赖,我们只用显示调用即可,而且每个依赖里面都包含了很多依赖
starter里面包含 autoconfigure包 + 其他依赖
1.什么叫自动配置:(按需加载)
1. 首先 所有的 依赖 咱都有
3. 自动装配,就是首先 会扫描 到 springboot-autoConfigure包下的所有
自动配置 类(就是配置类)
4. 但是不会 全部都生成 beanDefinition,这就是按需加载了
1.有conditional过滤条件
1.可能是需要 某个类的依赖(所有这就是你想自动配置啥,首先得把包依赖有)
2.还有可能是其他条件
所有,这127个自动配置类(包括里面的bean方法,内部配置类)
基本包含了,所有的 自动ioc bean(各个功能的),但是想要这些bean全部
注入 ioc,那么你就得满足所有 条件,这样才会 生成 beanDefiniiton
这样才有可能 会 生成 ioc对象
2.springboot与 ssm+tomccat最大的不同就是,boot里面的 所有组件(包括tomcat)都是由ioc创建并管理的
1.configurationProperties(prefix = "")+component
2.configurationProperties(prefix = "")+EnableConfigurationProperties(类.class)
这都是 javaBean绑定 主配置文件的两种方式,第二种enable注解也是将bean加入到容器中
3.自动配置底层都是用的这个configurationProperties(prefix = "")注解+EnableConfigurationProperties(类.class)
xxxproperties类 拿到 主配置文件中的值并且 ioc, 然后组件 就根据 这个 xxxProperties拿值
1.绑定 xxxProerpties,或者自己定义的javaBean(),来做到 bean和 主配置文件的绑定
也就是用bean 来获取 主配置文件的值,从而 组件 从 bean中获取值
2. @Value(${key}) 用来获取 主配置文件的值
1.@AutoConfigurationPackge
import(AutoConfigurationPackages.Register.class)
BeanRegistry注册大量组件
register(registry , 这个注解修饰的类是在的 包路径)
这就是,扫描主配置类所在包 的 bean的 原因
2.Import(AutoConfigurationImportSelector.class)
String[]
classLoaderGetResource(MEF......)
springboot-auto-configurauiton包下的 MEF....文件里
有 127个 自动配置类 类名
这样就是,要注册这127个自动配置类
但是 要根据 conditional 才能判断 是否 生成 beanDefinition
1.依赖是否 导入
2......
3....
conditional有各种各样的原因,所以不是127个 自动配置类都会生成 beanDefinition
这就是 按需加载
所以最基本的,如果我们想用哪个功能,首先先把 依赖导入,才有可能
因为基本所有的 127配置类都有 conditionalOnClass(),判断是否导入某依赖
1.springboot先 加载所有 自动配置类
2.每个自动配置类 按照 条件 生成 beanDefinition
3.生效的 自动配置类 会 给 容器 配置 很多 组件
4.只要 容器中有这些组件,相当于 这些 功能就有
5.组件默认会绑定 对应的 xxxProperties类,而xxxProperties里面的值,就是从主配置文件里面来的
所以可以改变主配置文件的值 去改变 xxxProperties的值,从而改变 组件的属性值
6.只要用户有自己 配置的,那么用用户的(不管id 是否相同)
7.xxxAutoConfigutation -> 组件 -> xxxProperties 里面拿值-> application.proerties
8.定制化配置
1.替换默认组件
2.修改默认组件的值(通过改变主配置文件)
1.引入 场景 依赖
starter里面包含 autoconfigure包 + 其他依赖
2.查看 自动配置了 哪些
1.自己分析 引入场景依赖对应的 自动配置 一般都生效了
2.配置文件中的 debug = true, 开启自动配置报告。Negative(不生效)/positive(生效)
3.是否需要修改
1.参照文档修改配置项
自己分析 xxxProperties绑定了配置文件的哪些
2.自定义加入 或 替换 组件
4.除了这些,可能得自己 引入 功能的组件,autoConfiguration不是所有功能相关的
组件都会帮你注册
例如@enable注解,很多都是直接注册了,和功能相关的 组件
例如aop,client,
当然,enable注册的组件,可能在autoconfiguration也得用到,这样autoconfiguration
才能生效
5.还有一些,springboot没有的 autoConfiguration, 需要自己手动注入,然后手动使用
例如es,除了添加依赖,还得手动将 es客户端 注入ioc容器
1.引入dev-tools 依赖
2.添加插件 maven-plugin 在父工程
3.在 settings-compiler里面 将ABCD 四个框打上√
4.快捷键 ctrl+shift+alt+/ - registry - 将
compiler.automake.allow.when.app.running
actionSystem.assertFocusAccessFromEdt
打上√
5.重启
实际不是真正的热部署,只是快捷键,最后还是会重新编译,运行
1.kay value形式的
2.value默认 不加 "" ''
3."" 不会转义,'' 会转义
1.导入提示依赖
2.在 打包插件的时候,将这个依赖去除
1.与mvc处理 静态资源的 servlet一样 也是handler处理映射
2.配置文件中 改 静态资源访问的 路径
1.Spring
mvc:
static-path-pattern: /res/** 加前缀
resource:
static-locations:classpath/hh 只能访问这个文件夹下的 静态资源
3.springboot最大的特性就是组件化,组件对应 xxxProerpties,xxxProperties对应主配置文件
而静态资源处理的,也有一个组件,和properties,我们改配置文件就行
1.以前说的是,beanDefinition的覆盖
2.而 springboot直接 在组件处加了一个 conditionalOnMissingBean()
如果我们的组件 注册了 beanDefinitionMap了,那么 直接这个 beanDefinition都不创建了
更不提后面覆盖了,直接没有了这个beanDefinition
3.这一处,跟我们之前的理解 说 spring的默认组件先注册,但是springboot里面
是自己的组件先注册,然后 加条件 conditionalOnMissingBean()判断有没有
自己注册的组件,有 默认组件 不生成 beanDefinition,这样也解决了,如果自己的
组件先注册,再注册spring的,那么spring会覆盖我们自己注册的问题了
1.实现同名一定是覆盖的
2.为什么启动项目会冲突呢?
不知道,反正又是后 beanName重复了,会报错
1.content-type 是指 数据在 http网络通信的时候,字符串的类型
2.请求有 发送数据的 content-type,有 可以接收的content-type
3.而 编码格式,只是 这个字符串里面的 字符的 编码格式,content-type是这个字符串的 类型
4.而有些特定的 需求 例如文件,得用特定的 content-type
5.前端 将数据 写入 请求体中的时候,要么将数据 用 messageConverter转换 成 对应的 类型的 字符串,要么 直接传对应类型的字符串
6.到了后台,首先,tomcat会用 application/www… 或者 Multiplication…,这两种 消息转换器,将字符串 转换成对应数据,封装在request.param中,然后tomcat这两种 消息转换器以外 类型的 字符串类型 就无法转换,只能在请求体中,无法被转换,而这两种消息转换器,也是依次判断,是否 canRead,如果可以read,那么 read
7.请求体中的 字符串,mvc 用 requestBody注解,一个requestBody请求处理器处理,其中 有 10多个 消息转换器,canRead(),这10多个都可以 read , 但是内容协商,看 请求的content-Type是什么,那么我们就用 那个 消息转换器 将消息转换 成 对应的数据类型
8.RequestBody和 responseBody的 请求值和返回值处理器,里面有很多 消息转换器,但是有两个 json,和 xml的,有一个条件才能加入ioc容器中,那就是 conditionalOnClass() , 也就是必须得有 这两个依赖才行
9.现在我们谈论 返回值(Person类型),在返回值的时候,有很多个 返回值处理器,来判断到底要用那个返回值处理器,我们就用 responseBody返回值处理器来做讲解。responseBody拿到 返回值后,用里面的 10多个 httpMessageConverter,来 处理 返回值, 然后遍历 这10多个 消息转换器(如果在之前,已经确定了 响应的 setContent-Type了,那么直接选择,能处理这个 mediaType的消息转换器,就不用遍历了)
canWrite(class,Media Type),看能不能把这个数据类型,以这种mediaType写出去
这时候canwrite 调用 support(class),看这个Person,能不能支持这种类型的class,如果能(这个是先判断 数据类型)
然后,看这个 class 能不能 以 Media Type写出去,如果这时候没有指定 Media Type = null , 那么会调用 消息转换器 的 getSupportMediaTypes,拿到这个 消息转换器支持的 写出的 media-Type,如果这时候指定了 Media Type,那么 这时候 消息转换器能不能处理这个class 以及 消息转换器 有哪些 media type也确定了,那么这时候回到 canWrite(class,Media Type),然后 再 看 能不能 把 这个 class
转换成 这几个(或者 指定的)Media Type中的 一个,如果可以,那么return True
canWrite(class,MediaType)的流程
1.support(class) 是否支持 这个 class类型 return true 下一步
(这一步其实 就是 确定能用的 class类型)
2.getSupportMediaType(),获取这个消息转换器支持的 mediaType(如果MediaType已经确定了,就不用这个方法了)
(这一步 其实就是 确定 能用的 MediaType类型)
3.判断 这个消息转换器 能不能 把 这个 支持的class , 转换 成 支持的MediaType(其中的一个),这时候一般 一个class类型 会对应一个 mediaType , 或者 一个都不行,如果有一个 那么 return true
(这一步就是确定,class和mediaType的对应关系,如果有一个可以对应起来的,那么就是可以写,消息转换器默认只能让一种class 对应 一种 mediaType)
如果 canWrite return true,那么就是这个消息转换器可以写,10多个消息转换器依次判断,筛选出可以写的 消息转换器
这时候的 不同的消息转换器,基本对应不同的 写出的 MediaType(如果之前已经确定了 MediaType,那么 肯定只能 有 一种消息转换器 可以支持 这个class 和 这个确定的 MediaType,意思就是说 如果已经确定了响应的 Media Type,那么 最后 一定只剩下一个 消息转换器)
这时候 看 请求的 accept-MediaType, 然后跟 我们服务器可以写的 MediaType 做内容协商,等于 两个 list《Media-Type》做比较
10.内容协商(内容协商管理器)
内容协商管理器里面默认有一个策略,就是基于请求头的
然后你可以再配置文件中开启,基于 参数列表的策略(只支持json/xml)
当然,你可以自定义内容协商策略(注册一个bean),基于参数列表但是(支持 自定义 content-type的),这时你还需要,自定义一个 httpMessageCoverter
1.accept-MediaType有 多种(一种),服务器 mediaType有多种(一种),那么这时候 这两个 list
11.自定义 httpMessageConverter 创建了,然后就能 自定义 class 和 Media-Type类型了,比如 Media-Type = gg,那么 你只需要再 write方法里面(将 class -> gg类型),Media-Type = gg,只是说 将class类型转换成 gg类型的字符串,至于gg是什么类型的字符串,那是你自己定义的,然后得 创建一个 内容协商策略,可以让浏览器 可以接收 media-Type = gg,类型的字符串,这时候你服务器 返回了 gg类型的字符串,然后内容协商策略,可以接收这个 gg类型的 字符串,然后你只需要 再浏览器端 可以接收 gg类型的就行,浏览器也不认识 gg类型是什么,只是看 浏览器 gg,内容协商gg,以及服务器返回的 gg,能不能对应就行,至于gg到底是个 什么类型的字符串不管
1.首先 springboot 自动注册了很多组件
2.你需要定制的有这几种情况
1.改变 自动注册的 组件的 默认属性
这里一般改配置文件
但是改配置文件的属性,也可能导致一些组件生效
2.覆盖 自动注册的 组件
1.自己写一个 id相同的 组件
2.自己注册了 一个组件,导致 Conditional 条件生效,自动配置的组件不生效
.....
3.不覆盖 自动 注册的组件,而是扩展组件
1.注册 不同名的 组件,但是 是这个类型相关的,
因为有些组件的注册,可能不是用这个组件,而是掉用这个类型组件的 方法,去注册一些组件
例如 webConfigurer,注册 这个组件,实际是调用了里面的 方法 去创建了一些组件
4.让自动配置里面 没有生效的组件 生效
1.可能是 缺 class依赖
2.可能是 缺 bean
3.可能是 缺 某个属性
......
5.注册 bean ,不一定是用这个bean,可能是 spirng底层调用了这个类型 bean的 方法去 注册相关功能的组件
6.总结:反正 情况很多,最主要是关注,自动配置类里面的信息,真正知道
这个自动配置的,每一步是干嘛的,这样我们才好采取特定的 自定义的配置
1.@enableMvc实际上 是 注册了 DelegatingWebMvcConfiguration.class
2.有了这个组件,在WebMvcAutoConfiguration自动配置类中,@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
这个条件就生效了,那么整个webmvc的 自动配置类 就gg了,
那么你 如果 写了 扩展的配置类,那只有扩展的配置类有功能了,99的自动配置类完全失效了
3.如果你想用,那么你就得,自己注册一个 webMvcAutoConfiguraion的配置类
1.在webMvcAutoConfiguraiton中有一个 内部 configuration Class
WebMvcAutoConfigurationAdapter implements WebMvcConfigurer
有关 mvc功能的 组件 都是在这个 bean里面的,当这个bean注册了后,会调用这个bean
里面的所有 方法,去 生成其他bean
2.你如果想要扩展,那么根据原理,springboot会调用 webMvcConfigurer里面的所有
方法,去生成其他的 bean,例如生成一个 interceptor,然后 add进 list
所以你只需要注册一个 webMvcConfigurer接口的实现类,实现你想要实现的方法
那么,springboot就会,调用这个 实现类的 所有 方法 去 注册 一些 bean,
并完成一些功能
3.这也体现了 ,不是所有的 bean的 注册,都是@bean方法的,可能是springboot
底层会调用 已经有的 bean的 方法,去生成其他bean
4.这样既不会 覆盖掉 原来的 webMvcConfigurer
1.只需要注册 对应的 ...RegistrationBean(new 组件(), pattern (如果是servlet或者filter的话))
2...RegistrationBean其实就相当与,web.xml了,而传过啦的 组件 经过它包装后,
就相当于 在 web.xml中注册了 组件
3.类型有,ServletRegistrationBean(new Servlet(),pattern = )
ListenerRegistrationBean(new Listener());
FilterRegistrationBean(new filter(),pattern = )
4.spring自动配置中 ,dispatchServlet也是这样注册的
new ServletRegistrationBean(new DispatchServlet(),pattern = )
1.以前,有个大的web.xml,和一个小的web.xml,dispatchServlet和DefaultServlet
的pattern 都是 /,但是用 小web.xml,中的 DispatchServlet
2.现在,注册了一个普通的Servlet和 DispatchServlet,这个都是在 一个小web.xml中注册的
那么。就得用精确规则了,/my 肯定选 更精确的 普通servlet
1.springBoot应用启动 发现 当前是web应用。web场景包 --- 导入 tomcat
2.web应用会创建一个web版的ioc容器 ServletWebServerApplicaitionContext
3.ServletWebServerApplicationContext启动时寻找 servletWebServerFactory
(servlet的服务器工厂 ---> servlet的web服务器)
4.spring底层默认与很多WebServer工厂
TomcatServletWebServerFactory
JettyServletWebServerFactory
UndertowServletWebServerFactory
5.底层直接会有一个自动配置类,servletWebServerFactoryAutoConfiguration
6.ServletWebServerFactoryAutoConfiguration 导入了 ServletWebServerFactoryConfiguration(配置类)
7.ServletWebServerFactoryConfiguraiton配置类,根据 动态判断 系统中 到底导入了 哪个 web服务器的 包
(默认是 web-starter 导入了 tomcat的包),容器中就有了TomcatServletWebServerFactory
8.TomcatServletWebServerFactory 创建出 Tomcat服务器;TomcatWebServer的构造器有初始化方法
initialize -- this.tomcat.start()
9.内嵌 服务器,就是手动把启动服务器的代码调用(tomcat核心jar包存在)
1.定制化创建的方式
1.修改配置文件
2.xxxCustomizer 定制化器
3.编写 自定义配置类 xxxconfiguraiton: +@bean替换,增加容器中的 默认组件
4.Web应用相关: 编写一个 配置类实现 webMvcConfigurer 即可定制化 web功能;
+@bean,给容器中 再扩展一些组件
5.EnableWebMvc + WebMvcConfigurer - @bean 可以全面接管 Springmvc
所有规则全部自己 重新配置,实现定制化和扩展功能
原理
1. webMvcAotoConfiguraiton 默认的 Springmvc的 自动 配置功能类
2. 一旦 使用 @EnableWebMvc 会import(DelegatingWebConfiguraiton.class)
3. DelegatingWebMvcConfiguraiton 的 作用,只保证 springmvc最基本的使用
1.把 所有系统中的 WebMvcConfiguration拿过来,所有功能定制都是这些WebMvcConfigurer
和起来一起生效
2.自动配置了一些非常底层的组件。RequestMappingHanderlerMapping,这些组件
依赖的组件都是从容器中拿的
3.public class DelegatingWebConfiguration extends WebMvcConfigurationSuppot
4.WebMvcAotoConfiguraiton 里面的配置要能生效 必须
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
5.@EnableWebMvc 导致了 WebMvcAutoConfiguration 没有生效
2.原理分析套路
场景starter -- xxxAutoConfiguration -- 导入xxx组件 -- 绑定xxxProperties --- 绑定配置文件项
除了这些,可能得自己 引入 功能的组件,autoConfiguration不是所有功能相关的
组件都会帮你注册
例如@enable注解,很多都是直接注册了,和功能相关的 组件
例如aop,client,
当然,enable注册的组件,可能在autoconfiguration也得用到,这样autoconfiguration
才能生效
1.要么用 全局配置文件
2.要么用 MybatisProperties
Mybatis:
#config-location:classpath:mybatis/.... (全局配置文件)
mapper-locations:classpath:mybatis/mapper/....
configuration: (mybatisProerties)
import({LettuceConnectionConfiguration.class,Jedis...class})
1.RedisAutoConfiguration(RedisProerties)
导入了 2个 redis客户端,用来获取 redis连接的
redisTemplate 重操作
StringRedisTemplate 重操作
2.LettuceConnectionConfiguration.class
JedisConnectionConfiguration.class
重连接
根据 conditional(),来判断哪个生效
生效后
会注入 JedisConnectionFactory
或LettureConnectionFactory
1.导入依赖
2.手动将 RestHighLevelClient 注入 ioc容器中
● @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
● @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
● @RepeatedTest :表示方法可重复执行,下方会有详细介绍
● @DisplayName :为测试类或者测试方法设置展示名称
● @BeforeEach :表示在每个单元测试之前执行
● @AfterEach :表示在每个单元测试之后执行
● @BeforeAll :表示在所有单元测试之前执行
● @AfterAll :表示在所有单元测试之后执行
● @Tag :表示单元测试类别,类似于JUnit4中的@Categories
● @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
● @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
● @repeatedText :表示可以重复测试多次
● @ExtendWith :为测试类或测试方法提供扩展类引用
● @SpringText = @ExtendWith(SpringExtension.class) + @BootstrapWith()
可以用spring的自动注入功能
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit 5 内置的断言可以分成如下几个类别:
检查业务逻辑返回的数据是否合理。
所有的测试运行结束以后,会有一个详细的测试报告;
如果断言不匹配,那么会抛异常
1、简单断言
用来对单个值进行简单的验证。如:
方法 说明
assertEquals 判断两个对象或两个原始类型是否相等
assertNotEquals 判断两个对象或两个原始类型是否不相等
assertSame 判断两个对象引用是否指向同一个对象
assertNotSame 判断两个对象引用是否指向不同的对象
assertTrue 判断给定的布尔值是否为 true
assertFalse 判断给定的布尔值是否为 false
assertNull 判断给定的对象引用是否为 null
assertNotNull 判断给定的对象引用是否不为 null
2、数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
3、组合断言
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}
4、异常断言
在JUnit4时期,想要测试方法的异常情况时,需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用。
@Test
@DisplayName("异常测试")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(
//扔出断言异常
ArithmeticException.class, () -> System.out.println(1 % 0));
}
5、超时断言
Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间
@Test
@DisplayName("超时测试")
public void timeoutTest() {
//如果测试方法时间超过1s将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
6、快速失败
通过 fail 方法直接使得测试失败
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
@DisplayName("前置条件")
public class AssumptionsTest {
private final String environment = "DEV";
@Test
@DisplayName("simple")
public void simpleAssume() {
assumeTrue(Objects.equals(this.environment, "DEV"));
assumeFalse(() -> Objects.equals(this.environment, "PROD"));
}
@Test
@DisplayName("assume then do")
public void assumeThenDo() {
assumingThat(
Objects.equals(this.environment, "DEV"),
() -> System.out.println("In DEV")
);
}
}
assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止。
JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。
@DisplayName("A stack")
class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用 @ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@NullSource: 表示为参数化测试提供一个null的入参
@EnumSource: 表示为参数化测试提供一个枚举入参
@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。
让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。
如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。
只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(String string) {
System.out.println(string);
Assertions.assertTrue(StringUtils.isNotBlank(string));
}
@ParameterizedTest
@MethodSource("method") //指定方法名
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(String name) {
System.out.println(name);
Assertions.assertNotNull(name);
}
static Stream<String> method() {
return Stream.of("apple", "banana");
}
未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能
如何使用
● 引入场景
● 访问 http://localhost:8080/actuator/**
● 暴露所有监控信息为HTTP
management:
endpoints:
enabled-by-default: true #暴露所有端点信息
web:
exposure:
include: '*' #以web方式暴露
Actuator Endpoint(最常使用的端点)
ID 描述
auditevents 暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件。
beans 显示应用程序中所有Spring Bean的完整列表。
caches 暴露可用的缓存。
conditions 显示自动配置的所有条件信息,包括匹配或不匹配的原因。
configprops 显示所有@ConfigurationProperties。
env 暴露Spring的属性ConfigurableEnvironment
flyway 显示已应用的所有Flyway数据库迁移。需要一个或多个Flyway组件。
(最常用的三个)
health 显示应用程序运行状况信息。
metrics 显示当前应用程序的“指标”信息。
loggers 显示和修改应用程序中日志的配置。
httptrace 显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository组件。
info 显示应用程序信息。
integrationgraph 显示Spring integrationgraph 。需要依赖spring-integration-core。
liquibase 显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。
mappings 显示所有@RequestMapping路径列表。
scheduledtasks 显示应用程序中的计划任务。
sessions 允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。
shutdown 使应用程序正常关闭。默认禁用。
startup 显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup。
threaddump 执行线程转储。
(Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点)
heapdump 返回hprof堆转储文件。
jolokia 通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core。
logfile 返回日志文件的内容(如果已设置logging.file.name或logging.file.path属性)。支持使用HTTPRange标头来检索部分日志文件的内容。
prometheus 以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus。
Health Endpoint
健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。
重要的几点:
● health endpoint返回的结果,应该是一系列健康检查后的一个汇总报告
● 很多的健康检查默认已经自动配置好了,比如:数据库、redis等
● 可以很容易的添加自定义的健康检查机制
Metrics Endpoint
提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到;
● 通过Metrics对接多种监控系统
● 简化核心Metrics开发
● 添加自定义Metrics或者扩展已有Metrics
管理Endpoints
1、开启与禁用Endpoints
● 默认所有的Endpoint除过shutdown都是开启的。
● 需要开启或者禁用某个Endpoint。配置模式为 management.endpoint..enabled = true
management:
endpoint:
beans:
enabled: true
● 或者禁用所有的Endpoint然后手动开启指定的Endpoint
management:
endpoints:
enabled-by-default: false
endpoint:
beans:
enabled: true
health:
enabled: true
2、暴露Endpoints
支持的暴露方式
● HTTP:默认只暴露health和info Endpoint
● JMX:默认暴露所有Endpoint
● 除过health和info,剩下的Endpoint都应该进行保护访问。如果引入SpringSecurity,则会默认配置安全访问规则
1、application-profile功能
● 默认配置文件 application.yaml;任何时候都会加载
● 指定环境配置文件 application-{env}.yaml
● 激活指定环境
○ 配置文件激活
○ 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
■ 修改配置文件的任意值,命令行优先
● 默认配置与环境配置同时生效
● 同名配置项,激活的环境优先,如果激活了多个环境,那后面的覆盖前面的
2、@Profile条件装配功能
1.@profile注解可以作用 在 类上和 @Bean 方法上,作用在@Bean上,只有在这个环境被激活的时候,@bean才生效
作用在类上,只有在这个环境被激活的时候,整个类的注解才生效
2.profile的默认 是 “default”环境
3.不指定 profile注解的,在任何环境下都可以注册这个组件,这些注解都生效
4.修改 spring运行环境的 2种方式
1.使用 命令行 动态参数:在VM 参数位置,加载 —Dspring.profiles.active = 环境标识(可以是多个)
2.1.在创建 ioc容器的 时候 使用无参构造 ac = new ApplicationContext()
2.设置需要激活的 环境applicationContext.getEnvironment().setActiveProfiles("") 环境可以设置多个
3.注册主配置类 ac.register()
4.刷新日期 ac.refresh()
3.如果是springboot,那么 运行环境的值 就是 激活的 配置文件的 环境值
例如 application -test.yaml 这样 运行环境值就是 test
3、profile分组
就是将 多个环境加到一个组里面,组名是新的运行环境名,这样如果激活 组名,那么属于
这个组下的所有配置文件都会被激活
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
在主配置文件里使用:spring.profiles.active=production 激活
组名自己起,
这样此时的 运行环境是 production
applicaition.yaml , application-proddb.yaml , application-prodmq.yaml
这三个配置文件都会生效,出现同名的话,就优先选择 激活环境的production的
如果production出现相同的,后面覆盖前面的
1、外部配置源
常用:Java属性文件、YAML文件、环境变量、命令行参数;
2、配置文件查找位置
(后面的覆盖前面的)
(1) classpath 根路径
(2) classpath 根路径下config目录
===============这是当前 jar包内部的,下面的是jar包外部的
(3) jar包当前目录
(4) jar包当前目录的config目录
(5) /config子目录的直接子目录
3、配置文件加载顺序:
4、指定环境优先,外部优先,后面的可以覆盖前面的同名配置项
1.bootstrap.yml 是系统级的,优先加载
2.Application.yml 是用户级的资源配置项
如果这两种配置文件都有的会,那么都会加载
而且bootstrap.yml的优先级更高,即出现同名配置,以bootstrap.yml优先
3.bootstrap.yml一般 用来读取 配置中心的 公共配置
applicaiton.yml 一般 用来 填充 本系统的 定制配置
starter里面包含 autoconfigure包 + 其他依赖
我们可以自己定义starter(里面包含一些依赖和autoconfigure依赖),这样我们引入自定义starter后,autoconfigure包中配置使用 META-INF/spring.factories 中 EnableAutoConfiguration 的值,使得项目启动加载指定的自动配置类
这些自动配置类我们可以自己写
这样达到的效果就是,我们可以在springboot启动,加载我们想要的 bean
这样就可以,不适用springboot提供的,starter,可以扩展一些starter
引入starter --- xxxAutoConfiguration --- 容器中放入组件 ---- 绑定xxxProperties ---- 配置项
自定义starter
atguigu-hello-spring-boot-starter(启动器)
atguigu-hello-spring-boot-starter-autoconfigure(自动配置包)
Spring原理【Spring注解】、SpringMVC原理、自动配置原理、SpringBoot原理
1.SpringBoot启动过程
● 创建 SpringApplication
1.保存一些信息。(重要,因为后面运行springbootApplication要用到)
2.判定当前应用的类型。ClassUtils。Servlet
3.bootstrappers:初始启动引导器(List):
去spring.factories文件中找 org.springframework.boot.Bootstrapper
4.找 ApplicationContextInitializer;去spring.factories找 ApplicationContextInitializer
■ List> initializers
5.找 ApplicationListener ;应用监听器。去spring.factories找 ApplicationListener
■ List> listeners
======================================================
这三个 后面都要用到的
● 运行 SpringApplication
1.StopWatch
2.记录应用的启动时间
3.创建引导上下文(Context环境)createBootstrapContext()
■ 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
(这里就用到了之前保存的 bootstrappers)
4.让当前应用进入headless模式。java.awt.headless(自己更生模式)
5.获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】
■ getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
(这些个listener 不是上面保存的 ApplicationListener
下面用的 也是这些个 RunListener)
6.遍历 SpringApplicationRunListener 调用 starting 方法;
■ 相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
7.保存命令行参数;ApplicationArguments
8.准备环境 prepareEnvironment();
■ 返回或者创建基础环境信息对象。StandardServletEnvironment
■ 配置环境信息对象。
● 读取所有的配置源的配置属性值。
■ 绑定环境信息
■ 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
9.创建IOC容器(createApplicationContext())
■ 根据项目类型(Servlet)创建容器,
■ 当前会创建 AnnotationConfigServletWebServerApplicationContext
10.准备ApplicationContext IOC容器的基本信息 prepareContext()
■ 保存环境信息
■ IOC容器的后置处理流程。
■ 应用初始化器;applyInitializers;
● 遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
● 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器contextPrepared
■ 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded;
(这里用到了之前的 ApplicationContextInitializer)
11.刷新IOC容器。refreshContext
■ 创建容器中的所有组件(Spring注解)
12.容器刷新完成后工作?afterRefresh
13.所有监听器 调用 listeners.started(context); 通知所有的监听器 started
14.调用所有runners;callRunners()
■ 获取容器中的 ApplicationRunner
■ 获取容器中的 CommandLineRunner
■ 合并所有runner并且按照@Order进行排序
■ 遍历所有的runner。调用 run 方法
15.如果以上有异常,
■ 调用Listener 的 failed
16.调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
17.running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
总结:上面获取 很多组件,都是通过 spring.factories配置文件中去找的
包括,autoConfiguration也是
SpringApplicationRunListener
ApplicationContextInitializer
ApplicationListener
(这三个是在 META/spring.factories中找的,并且加载的,所以要自定义他们,必须在 这个配置文件中,添上我们自定义的)
ApplicationRunner
CommandLineRunner
(这两个是从容器中获得的,所以要自定义他们,必须把它们加入ioc容器中)
上面springbootApplicaition的启动过程,就是这5个组件
这些组件在 springboot启动的 各个时机
介入 调用不同的方法
so,我们可以自定义一些这 5个 类型的 组件,就可以监听到整个springboot启动的过程,并在这些自定义的 组件中,在springboot启动完成前 ,do something
1. html页面都得放在 template目录中(放在其他目录下,无法识别)
2. spirng:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mvc视图的前缀和后缀