springboot的学习(2)
转载于:(2条消息) Spring Boot 2 学习笔记(2 / 2)_KISS-CSDN博客
Spring Boot 2 学习笔记(1 / 2)
- | - | - |
---|---|---|
45、web实验-抽取公共页面 | 46、web实验-遍历数据与页面bug修改 | 47、视图解析-【源码分析】-视图解析器与视图 |
48、拦截器-登录检查与静态资源放行 | 49、拦截器-【源码分析】-拦截器的执行时机和原理 | 50、文件上传-单文件与多文件上传的使用 |
51、文件上传-【源码流程】文件上传参数解析器 | 52、错误处理-SpringBoot默认错误处理机制 | 53、错误处理-【源码分析】底层组件功能分析 |
54、错误处理-【源码流程】异常处理流程 | 55、错误处理-【源码流程】几种异常处理原理 | 56、原生组件注入-原生注解与Spring方式注入 |
57、原生组件注入-【源码分析】DispatcherServlet注入原理 | 58、嵌入式Servlet容器-【源码分析】切换web服务器与定制化 | 59、定制化原理-SpringBoot定制化组件的几种方式(小结) |
60、数据访问-数据库场景的自动配置分析与整合测试 | 61、数据访问-自定义方式整合druid数据源 | 62、数据访问-druid数据源starter整合方式 |
63、数据访问-整合MyBatis-配置版 | 64、数据访问-整合MyBatis-注解配置混合版 | 65、数据访问-整合MyBatisPlus操作数据库 |
66、数据访问-CRUD实验-数据列表展示 | 67、数据访问-CRUD实验-分页数据展示 | 68、数据访问-CRUD实验-删除用户完成 |
69、数据访问-准备阿里云Redis环境 | 70、数据访问-Redis操作与统计小实验 | 71、单元测试-JUnit5简介 |
72、单元测试-常用测试注解 | 73、单元测试-断言机制 | 74、单元测试-前置条件 |
75、单元测试-嵌套测试 | 76、单元测试-参数化测试 | 77、指标监控-SpringBoot Actuator与Endpoint |
78、指标监控-常使用的端点及开启与禁用 | 79、指标监控-定制Endpoint | 80、指标监控-Boot Admin Server |
81、高级特性-Profile环境切换 | 82、高级特性-配置加载优先级 | 83、高级特性-自定义starter细节 |
84、原理解析-SpringApplication创建初始化流程 | 85、原理解析-SpringBoot完整启动过程 | 86、原理解析-自定义事件监听组件 |
87、后会有期 | - | - |
/templates/table/basic_table.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"><meta name="description" content=""><meta name="author" content="ThemeBucket"><link rel="shortcut icon" href="#" type="image/png"><title>Basic Table</title><div th:include="common :: commonheader"> </div><!--将common.html的代码段 插进来-->
</head><body class="sticky-header"><section>
<div th:replace="common :: #leftmenu"></div><!-- main content start--><div class="main-content" ><div th:replace="common :: headermenu"></div>...</div><!-- main content end-->
</section><!-- Placed js at the end of the document so the pages load faster -->
<div th:replace="common :: #commonscript"></div></body>
</html>
Difference between th:insert
and th:replace
(and th:include
)
46、web实验-遍历数据与页面bug修改
#numbers
表示methods for formatting numeric objects.link
@GetMapping("/user/delete/{id}")
public String deleteUser(@PathVariable("id") Long id,@RequestParam(value = "pn",defaultValue = "1")Integer pn,RedirectAttributes ra){userService.removeById(id);ra.addAttribute("pn",pn);return "redirect:/dynamic_table";
}@GetMapping("/dynamic_table")
public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){//表格内容的遍历//从数据库中查出user表中的用户进行展示//构造分页参数Page<User> page = new Page<>(pn, 2);//调用page进行分页Page<User> userPage = userService.page(page, null);model.addAttribute("users",userPage);return "table/dynamic_table";
}
69、数据访问-准备阿里云Redis环境
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!--导入jedis-->
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>
RedisAutoConfiguration
自动配置类,RedisProperties 属性类 --> spring.redis.xxx是对redis的配置。- 连接工厂
LettuceConnectionConfiguration
、JedisConnectionConfiguration
是准备好的。 - 自动注入了
RedisTemplate<Object, Object>
,xxxTemplate
。 - 自动注入了
StringRedisTemplate
,key,value都是String - 底层只要我们使用
StringRedisTemplate
、RedisTemplate
就可以操作Redis。
外网Redis环境搭建:
阿里云按量付费Redis,其中选择经典网络。
申请Redis的公网连接地址。
修改白名单,允许
0.0.0.0/0
访问。
70、数据访问-Redis操作与统计小实验
spring:redis:
# url: redis://lfy:Lfy123456@r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com:6379host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.comport: 6379password: lfy:Lfy123456client-type: jedisjedis:pool:max-active: 10
# lettuce:# 另一个用来连接redis的java框架
# pool:
# max-active: 10
# min-idle: 5
测试Redis连接:
@SpringBootTest
public class Boot05WebAdminApplicationTests {@AutowiredStringRedisTemplate redisTemplate;@AutowiredRedisConnectionFactory redisConnectionFactory;@Testvoid testRedis(){ValueOperations<String, String> operations = redisTemplate.opsForValue();operations.set("hello","world");String hello = operations.get("hello");System.out.println(hello);System.out.println(redisConnectionFactory.getClass());}}
Redis Desktop Manager:可视化Redis管理软件。
URL统计拦截器:
@Component
public class RedisUrlCountInterceptor implements HandlerInterceptor {@AutowiredStringRedisTemplate redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String uri = request.getRequestURI();//默认每次访问当前uri就会计数+1redisTemplate.opsForValue().increment(uri);return true;}
}
注册URL统计拦截器:
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{@AutowiredRedisUrlCountInterceptor redisUrlCountInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(redisUrlCountInterceptor).addPathPatterns("/**").excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**","/aa/**");}
}
Filter、Interceptor 几乎拥有相同的功能?
- Filter是Servlet定义的原生组件,它的好处是脱离Spring应用也能使用。
- Interceptor是Spring定义的接口,可以使用Spring的自动装配等功能。
调用Redis内的统计数据:
@Slf4j
@Controller
public class IndexController {@AutowiredStringRedisTemplate redisTemplate;@GetMapping("/main.html")public String mainPage(HttpSession session,Model model){log.info("当前方法是:{}","mainPage");ValueOperations<String, String> opsForValue =redisTemplate.opsForValue();String s = opsForValue.get("/main.html");String s1 = opsForValue.get("/sql");model.addAttribute("mainCount",s);model.addAttribute("sqlCount",s1);return "main";}
}
71、单元测试-JUnit5简介
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库
作为最新版本的JUnit框架,JUnit5与之前版本的JUnit框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部包含了一个测试引擎,用于在Junit Platform上运行。
JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,JUnit3.x的测试引擎。
注意:
SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容JUnit4需要自行引入(不能使用JUnit4的功能 @Test)
JUnit 5’s Vintage已经从
spring-boot-starter-test
从移除。如果需要继续兼容Junit4需要自行引入Vintage依赖:
<dependency><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.hamcrest</groupId><artifactId>hamcrest-core</artifactId></exclusion></exclusions>
</dependency>
- 使用添加JUnit 5,添加对应的starter:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
- Spring的JUnit 5的基本单元测试模板(Spring的JUnit4的是
@SpringBootTest
+@RunWith(SpringRunner.class)
):
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;//注意不是org.junit.Test(这是JUnit4版本的)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class SpringBootApplicationTests {@Autowiredprivate Component component;@Test//@Transactional 标注后连接数据库有回滚功能public void contextLoads() {Assertions.assertEquals(5, component.getFive());}
}
Jupiter
英 [ˈdʒuːpɪtə®] 美 [ˈdʒuːpɪtər]
n. 木星(太阳系中最大的行星)
vintage
英 [ˈvɪntɪdʒ] 美 [ˈvɪntɪdʒ]
n. 特定年份(或地方)酿制的酒;酿造年份;采摘葡萄酿酒的期间(或季节);葡萄收获期(或季节)
adj. (指葡萄酒)优质的,上等的,佳酿的;古色古香的(指1917–1930年间制造,车型和品味受人青睐的);(过去某个时期)典型的,优质的;(某人的)最佳作品的
72、单元测试-常用测试注解
- @Test:表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
- @ParameterizedTest:表示方法是参数化测试。
- @RepeatedTest:表示方法可重复执行。
- @DisplayName:为测试类或者测试方法设置展示名称。
- @BeforeEach:表示在每个单元测试之前执行。
- @AfterEach:表示在每个单元测试之后执行。
- @BeforeAll:表示在所有单元测试之前执行。
- @AfterAll:表示在所有单元测试之后执行。
- @Tag:表示单元测试类别,类似于JUnit4中的@Categories。
- @Disabled:表示测试类或测试方法不执行,类似于JUnit4中的@Ignore。
- @Timeout:表示测试方法运行如果超过了指定时间将会返回错误。
- @ExtendWith:为测试类或测试方法提供扩展类引用。
import org.junit.jupiter.api.*;@DisplayName("junit5功能测试类")
public class Junit5Test {@DisplayName("测试displayname注解")@Testvoid testDisplayName() {System.out.println(1);System.out.println(jdbcTemplate);}@ParameterizedTest@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })void palindromes(String candidate) {assertTrue(StringUtils.isPalindrome(candidate));}@Disabled@DisplayName("测试方法2")@Testvoid test2() {System.out.println(2);}@RepeatedTest(5)@Testvoid test3() {System.out.println(5);}/*** 规定方法超时时间。超出时间测试出异常** @throws InterruptedException*/@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)@Testvoid testTimeout() throws InterruptedException {Thread.sleep(600);}@BeforeEachvoid testBeforeEach() {System.out.println("测试就要开始了...");}@AfterEachvoid testAfterEach() {System.out.println("测试结束了...");}@BeforeAllstatic void testBeforeAll() {System.out.println("所有测试就要开始了...");}@AfterAllstatic void testAfterAll() {System.out.println("所有测试以及结束了...");}}
73、单元测试-断言机制
简单断言
方法 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
@Test
@DisplayName("simple assertion")
public void simple() {assertEquals(3, 1 + 2, "simple math");assertNotEquals(3, 1 + 1);assertNotSame(new Object(), new Object());Object obj = new Object();assertSame(obj, obj);assertFalse(1 > 2);assertTrue(1 < 2);assertNull(null);assertNotNull(new Object());
}
数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等。
@Test
@DisplayName("array assertion")
public void array() {assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
组合断言
assertAll()
方法接受多个 org.junit.jupiter.api.Executable
函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言。
@Test
@DisplayName("assert all")
public void all() {assertAll("Math",() -> assertEquals(2, 1 + 1),() -> assertTrue(1 > 0));
}
异常断言
@Test
@DisplayName("异常测试")
public void exceptionTest() {ArithmeticException exception = Assertions.assertThrows(//扔出断言异常ArithmeticException.class, () -> System.out.println(1 % 0));
}
超时断言
JUnit5还提供了Assertions.assertTimeout()为测试方法设置了超时时间。
@Test
@DisplayName("超时测试")
public void timeoutTest() {//如果测试方法时间超过1s将会异常Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
快速失败
@Test
@DisplayName("fail")
public void shouldFail() {fail("This should fail");
}
74、单元测试-前置条件
Unit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言assertions会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。
前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
@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
对象才会被执行;当条件不满足时,测试执行并不会终止。
75、单元测试-嵌套测试
@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 {@BeforeEachvoid 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";@BeforeEachvoid 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());}}}
}
76、单元测试-参数化测试
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
利用**@ValueSource**等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
- @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
- @NullSource: 表示为参数化测试提供一个null的入参
- @EnumSource: 表示为参数化测试提供一个枚举入参
- @CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
- @MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
@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");
}
迁移指南
- 注解在
org.junit.jupiter.api
包中,断言在org.junit.jupiter.api.Assertions
类中,前置条件在org.junit.jupiter.api.Assumptions
类中。 - 把
@Before
和@After
替换成@BeforeEach
和@AfterEach
。 - 把
@BeforeClass
和@AfterClass
替换成@BeforeAll
和@AfterAll。 - 把
@Ignore
替换成@Disabled
。 - 把
@Category
替换成@Tag
。 - 把
@RunWith
、@Rule
和@ClassRule
替换成@ExtendWith
。
77、指标监控-SpringBoot Actuator与Endpoint
未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。
官方文档 - Spring Boot Actuator: Production-ready Features
SpringBoot Actuator 1.x
- 支持SpringMVC
- 基于继承方式进行扩展
- 层级Metrics配置
- 自定义Metrics收集
- 默认较少的安全策略
SpringBoot Actuator 2.x
- 支持SpringMVC、JAX-RS以及Webflux
- 注解驱动进行扩展
- 层级&名称空间Metrics
- 底层使用MicroMeter,强大、便捷默认丰富的安全策略
如何使用
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 访问
http://localhost:8080/actuator/**
。 - 暴露所有监控信息为HTTP。
management:endpoints:enabled-by-default: true #暴露所有端点信息web:exposure:include: '*' #以web方式暴露
- 测试例子
- http://localhost:8080/actuator/beans
- http://localhost:8080/actuator/configprops
- http://localhost:8080/actuator/metrics
- http://localhost:8080/actuator/metrics/jvm.gc.pause
- http://localhost:8080/actuator/metrics/endpointName/detailPath
actuator
英 [ˈæktjʊeɪtə] 美 [ˈæktjuˌeɪtər]
n. 致动(促动,激励,调节)器;传动(装置,机构);拖动装置;马达;操作机构;执行机构(元件);(电磁铁)螺线管;操纵装置(阀门);调速控制器;往复运动油(气)缸;作动筒
metric
英 [ˈmetrɪk] 美 [ˈmetrɪk]
adj. 米制的;公制的;按公制制作的;用公制测量的
n. 度量标准;[数学]度量;诗体;韵文;诗韵
78、指标监控-常使用的端点及开启与禁用
常使用的端点
ID | 描述 |
---|---|
auditevents
|
暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件 。
|
beans
|
显示应用程序中所有Spring Bean的完整列表。 |
caches
|
暴露可用的缓存。 |
conditions
|
显示自动配置的所有条件信息,包括匹配或不匹配的原因。 |
configprops
|
显示所有@ConfigurationProperties 。
|
env
|
暴露Spring的属性ConfigurableEnvironment
|
flyway
|
显示已应用的所有Flyway数据库迁移。 需要一个或多个Flyway 组件。
|
health
|
显示应用程序运行状况信息。 |
httptrace
|
显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository 组件。
|
info
|
显示应用程序信息。 |
integrationgraph
|
显示Spring integrationgraph 。需要依赖spring-integration-core 。
|
loggers
|
显示和修改应用程序中日志的配置。 |
liquibase
|
显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase 组件。
|
metrics
|
显示当前应用程序的“指标”信息。 |
mappings
|
显示所有@RequestMapping 路径列表。
|
scheduledtasks
|
显示应用程序中的计划任务。 |
sessions
|
允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。 |
shutdown
|
使应用程序正常关闭。默认禁用。 |
startup
|
显示由ApplicationStartup 收集的启动步骤数据。需要使用SpringApplication 进行配置BufferingApplicationStartup 。
|
threaddump
|
执行线程转储。 |
如果您的应用程序是Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点:
ID | 描述 |
---|---|
heapdump
|
返回hprof 堆转储文件。
|
jolokia
|
通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core 。
|
logfile
|
返回日志文件的内容(如果已设置logging.file.name 或logging.file.path 属性)。支持使用HTTPRange 标头来检索部分日志文件的内容。
|
prometheus
|
以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus 。
|
其中最常用的Endpoint:
- Health:监控状况
- Metrics:运行时指标
- Loggers:日志记录
Health Endpoint
健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。
Metrics Endpoint
提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到:
开启与禁用Endpoints
- 默认所有的Endpoint除过shutdown都是开启的。
- 需要开启或者禁用某个Endpoint。配置模式为
management.endpoint.<endpointName>.enabled = true
management:endpoint:beans:enabled: true
- 或者禁用所有的Endpoint然后手动开启指定的Endpoint。
management:endpoints:enabled-by-default: falseendpoint:beans:enabled: truehealth:enabled: true
暴露Endpoints
- HTTP:默认只暴露health和info。
- JMX:默认暴露所有Endpoint。
- 除过health和info,剩下的Endpoint都应该进行保护访问。如果引入Spring Security,则会默认配置安全访问规则。
ID | JMX | Web |
---|---|---|
auditevents
|
Yes | No |
beans
|
Yes | No |
caches
|
Yes | No |
conditions
|
Yes | No |
configprops
|
Yes | No |
env
|
Yes | No |
flyway
|
Yes | No |
health
|
Yes | Yes |
heapdump
|
N/A | No |
httptrace
|
Yes | No |
info
|
Yes | Yes |
integrationgraph
|
Yes | No |
jolokia
|
N/A | No |
logfile
|
N/A | No |
loggers
|
Yes | No |
liquibase
|
Yes | No |
metrics
|
Yes | No |
mappings
|
Yes | No |
prometheus
|
N/A | No |
scheduledtasks
|
Yes | No |
sessions
|
Yes | No |
shutdown
|
Yes | No |
startup
|
Yes | No |
threaddump
|
Yes | No |
若要更改公开的Endpoint,请配置以下的包含和排除属性:
Property | Default |
---|---|
management.endpoints.jmx.exposure.exclude
|
|
management.endpoints.jmx.exposure.include
|
*
|
management.endpoints.web.exposure.exclude
|
|
management.endpoints.web.exposure.include
|
info, health
|
官方文档 - Exposing Endpoints
79、指标监控-定制Endpoint
定制 Health 信息
management:health:enabled: trueshow-details: always #总是显示详细信息。可显示每个模块的状态信息
通过实现HealthIndicator
接口,或继承MyComHealthIndicator
类。
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;@Component
public class MyHealthIndicator implements HealthIndicator {@Overridepublic Health health() {int errorCode = check(); // perform some specific health checkif (errorCode != 0) {return Health.down().withDetail("Error Code", errorCode).build();}return Health.up().build();}}/*
构建Health
Health build = Health.down().withDetail("msg", "error service").withDetail("code", "500").withException(new RuntimeException()).build();
*/
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {/*** 真实的检查方法* @param builder* @throws Exception*/@Overrideprotected void doHealthCheck(Health.Builder builder) throws Exception {//mongodb。 获取连接进行测试Map<String,Object> map = new HashMap<>();// 检查完成if(1 == 2){
// builder.up(); //健康builder.status(Status.UP);map.put("count",1);map.put("ms",100);}else {
// builder.down();builder.status(Status.OUT_OF_SERVICE);map.put("err","连接超时");map.put("ms",3000);}builder.withDetail("code",100).withDetails(map);}
}
定制info信息
info:appName: boot-adminversion: 2.0.1mavenProjectName: @project.artifactId@ #使用@@可以获取maven的pom文件值mavenProjectVersion: @project.version@
- 编写InfoContributor
import java.util.Collections;import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;@Component
public class ExampleInfoContributor implements InfoContributor {@Overridepublic void contribute(Info.Builder builder) {builder.withDetail("example",Collections.singletonMap("key", "value"));}}
http://localhost:8080/actuator/info 会输出以上方式返回的所有info信息
定制Metrics信息
class MyService{Counter counter;public MyService(MeterRegistry meterRegistry){counter = meterRegistry.counter("myservice.method.running.counter");}public void hello() {counter.increment();}
}//也可以使用下面的方式
@Bean
MeterBinder queueSize(Queue queue) {return (registry) -> Gauge.builder("queueSize", queue::size).register(registry);
}
定制Endpoint
@Component
@Endpoint(id = "container")
public class DockerEndpoint {@ReadOperationpublic Map getDockerInfo(){return Collections.singletonMap("info","docker started...");}@WriteOperationprivate void restartDocker(){System.out.println("docker restarted....");}}
场景:
- 开发ReadinessEndpoint来管理程序是否就绪。
- 开发LivenessEndpoint来管理程序是否存活。
80、指标监控-Boot Admin Server
What is Spring Boot Admin?
codecentric’s Spring Boot Admin is a community project to manage and monitor your Spring Boot ® applications. The applications register with our Spring Boot Admin Client (via HTTP) or are discovered using Spring Cloud ® (e.g. Eureka, Consul). The UI is just a Vue.js application on top of the Spring Boot Actuator endpoints.
开始使用方法
81、高级特性-Profile环境切换
为了方便多环境适配,Spring Boot简化了profile功能。
- 默认配置文件
application.yaml
任何时候都会加载。 - 指定环境配置文件
application-{env}.yaml
,env
通常替代为test
, - 激活指定环境
- 配置文件激活:
spring.profiles.active=prod
- 命令行激活:
java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
(修改配置文件的任意值,命令行优先)
- 配置文件激活:
- 默认配置与环境配置同时生效
- 同名配置项,profile配置优先
@Profile条件装配功能
@Data
@Component
@ConfigurationProperties("person")//在配置文件中配置
public class Person{private String name;private Integer age;
}
application.propertiesperson: name: lunage: 8
public interface Person {String getName();Integer getAge();}@Profile("test")//加载application-test.yaml里的
@Component
@ConfigurationProperties("person")
@Data
public class Worker implements Person {private String name;private Integer age;
}@Profile(value = {"prod","default"})//加载application-prod.yaml里的
@Component
@ConfigurationProperties("person")
@Data
public class Boss implements Person {private String name;private Integer age;
}
application-test.yamlperson:name: test-张三server:port: 7000
application-prod.yamlperson:name: prod-张三server:port: 8000
application.properties
# 激活prod配置文件
spring.profiles.active=prod
@Autowired
private Person person;@GetMapping("/")
public String hello(){//激活了prod,则返回Boss;激活了test,则返回Workerreturn person.getClass().toString();
}
@Profile还可以修饰在方法上:
class Color {
}@Configuration
public class MyConfig {@Profile("prod")@Beanpublic Color red(){return new Color();}@Profile("test")@Beanpublic Color green(){return new Color();}
}
可以激活一组:
spring.profiles.active=productionspring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
82、高级特性-配置加载优先级
外部化配置
官方文档 - Externalized Configuration
- Default properties (specified by setting
SpringApplication.setDefaultProperties
). @PropertySource
annotations on your@Configuration
classes. Please note that such property sources are not added to theEnvironment
until the application context is being refreshed. This is too late to configure certain properties such aslogging.*
andspring.main.*
which are read before refresh begins.- Config data (such as
application.properties
files) - A
RandomValuePropertySource
that has properties only inrandom.*
. - OS environment variables.
- Java System properties (
System.getProperties()
). - JNDI attributes from
java:comp/env
. ServletContext
init parameters.ServletConfig
init parameters.- Properties from
SPRING_APPLICATION_JSON
(inline JSON embedded in an environment variable or system property). - Command line arguments.
properties
attribute on your tests. Available on@SpringBootTest
and the test annotations for testing a particular slice of your application.@TestPropertySource
annotations on your tests.- Devtools global settings properties in the
$HOME/.config/spring-boot
directory when devtools is active.
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;@Component
public class MyBean {@Value("${name}")//以这种方式可以获得配置值private String name;// ...}
- 外部配置源
- Java属性文件。
- YAML文件。
- 环境变量。
- 命令行参数。
- 配置文件查找位置
- classpath 根路径。
- classpath 根路径下config目录。
- jar包当前目录。
- jar包当前目录的config目录。
- /config子目录的直接子目录。
- 配置文件加载顺序:
- 当前jar包内部的
application.properties
和application.yml
。 - 当前jar包内部的
application-{profile}.properties
和application-{profile}.yml
。 - 引用的外部jar包的
application.properties
和application.yml
。 - 引用的外部jar包的
application-{profile}.properties
和application-{profile}.yml
。
- 当前jar包内部的
- 指定环境优先,外部优先,后面的可以覆盖前面的同名配置项。
83、高级特性-自定义starter细节
starter启动原理
autoconfigure包中配置使用
META-INF/spring.factories
中EnableAutoConfiguration
的值,使得项目启动加载指定的自动配置类编写自动配置类
xxxAutoConfiguration
->xxxxProperties
@Configuration
@Conditional
@EnableConfigurationProperties
@Bean
- …
引入starter —
xxxAutoConfiguration
— 容器中放入组件 ----绑定xxxProperties
---- 配置项
自定义starter
目标:创建
HelloService
的自定义starter。创建两个工程,分别命名为
hello-spring-boot-starter
(普通Maven工程),hello-spring-boot-starter-autoconfigure
(需用用到Spring Initializr创建的Maven工程)。hello-spring-boot-starter
无需编写什么代码,只需让该工程引入hello-spring-boot-starter-autoconfigure
依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter</artifactId><version>1.0.0-SNAPSHOT</version><dependencies><dependency><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter-autoconfigure</artifactId><version>1.0.0-SNAPSHOT</version></dependency></dependencies></project>
hello-spring-boot-starter-autoconfigure
的pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter-autoconfigure</artifactId><version>1.0.0-SNAPSHOT</version><name>hello-spring-boot-starter-autoconfigure</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies>
</project>
- 创建4个文件:
com/lun/hello/auto/HelloServiceAutoConfiguration
com/lun/hello/bean/HelloProperties
com/lun/hello/service/HelloService
src/main/resources/META-INF/spring.factories
import com.lun.hello.bean.HelloProperties;
import com.lun.hello.service.HelloService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@ConditionalOnMissingBean(HelloService.class)
@EnableConfigurationProperties(HelloProperties.class)//默认HelloProperties放在容器中
public class HelloServiceAutoConfiguration {@Beanpublic HelloService helloService(){return new HelloService();}}import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties("hello")
public class HelloProperties {private String prefix;private String suffix;public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}public String getSuffix() {return suffix;}public void setSuffix(String suffix) {this.suffix = suffix;}
}
import com.lun.hello.bean.HelloProperties;
import org.springframework.beans.factory.annotation.Autowired;/*** 默认不要放在容器中*/
public class HelloService {@Autowiredprivate HelloProperties helloProperties;public String sayHello(String userName){return helloProperties.getPrefix() + ": " + userName + " > " + helloProperties.getSuffix();}
}# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lun.hello.auto.HelloServiceAutoConfiguration
用maven插件,将两工程install到本地。
接下来,测试使用自定义starter,用Spring Initializr创建名为
hello-spring-boot-starter-test
工程,引入hello-spring-boot-starter
依赖,其pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter-test</artifactId><version>1.0.0-SNAPSHOT</version><name>hello-spring-boot-starter-test</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 引入`hello-spring-boot-starter`依赖 --><dependency><groupId>com.lun</groupId><artifactId>hello-spring-boot-starter</artifactId><version>1.0.0-SNAPSHOT</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
- 添加配置文件
application.properties
:
hello.prefix=hello
hello.suffix=666
- 添加单元测试类:
import com.lun.hello.service.HelloService;//来自自定义starter
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class HelloSpringBootStarterTestApplicationTests {@Autowiredprivate HelloService helloService;@Testvoid contextLoads() {// System.out.println(helloService.sayHello("lun"));Assertions.assertEquals("hello: lun > 666", helloService.sayHello("lun"));}}
84、原理解析-SpringApplication创建初始化流程
SpringBoot启动过程
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class HelloSpringBootStarterTestApplication {public static void main(String[] args) {SpringApplication.run(HelloSpringBootStarterTestApplication.class, args);}}
public class SpringApplication {...public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}//先看看new SpringApplication(primarySources),下一节再看看run()public SpringApplication(Class<?>... primarySources) {this(null, primarySources);}public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//WebApplicationType是枚举类,有NONE,SERVLET,REACTIVE,下行webApplicationType是SERVLETthis.webApplicationType = WebApplicationType.deduceFromClasspath();//初始启动引导器,去spring.factories文件中找org.springframework.boot.Bootstrapper,但我找不到实现Bootstrapper接口的类this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));//去spring.factories找 ApplicationContextInitializersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//去spring.factories找 ApplicationListenersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;}...}
spring.factories:
...# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener...
85、原理解析-SpringBoot完整启动过程
继续上一节,接着讨论return new SpringApplication(primarySources).run(args)
的run
方法
public class SpringApplication {...public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();//开始计时器stopWatch.start();//开始计时//1.//创建引导上下文(Context环境)createBootstrapContext()//获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置DefaultBootstrapContext bootstrapContext = createBootstrapContext();//2.到最后该方法会返回这contextConfigurableApplicationContext context = null;//3.让当前应用进入headless模式configureHeadlessProperty();//4.获取所有 RunListener(运行监听器),为了方便所有Listener进行事件感知SpringApplicationRunListeners listeners = getRunListeners(args);//5. 遍历 SpringApplicationRunListener 调用 starting 方法;// 相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。listeners.starting(bootstrapContext, this.mainApplicationClass);try {//6.保存命令行参数 ApplicationArgumentsApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//7.准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);/*打印标志. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.4.2)*/Banner printedBanner = printBanner(environment);// 创建IOC容器(createApplicationContext())// 根据项目类型webApplicationType(NONE,SERVLET,REACTIVE)创建容器,// 当前会创建 AnnotationConfigServletWebServerApplicationContextcontext = createApplicationContext();context.setApplicationStartup(this.applicationStartup);//8.准备ApplicationContext IOC容器的基本信息prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//9.刷新IOC容器,创建容器中的所有组件,Spring框架的内容refreshContext(context);//该方法没内容,大概为将来填入afterRefresh(context, applicationArguments);stopWatch.stop();//停止计时if (this.logStartupInfo) {//this.logStartupInfo默认是truenew StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//10.listeners.started(context);//11.调用所有runnerscallRunners(context, applicationArguments);}catch (Throwable ex) {//13.handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {//12.listeners.running(context);}catch (Throwable ex) {//13.handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;}//1. private DefaultBootstrapContext createBootstrapContext() {DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));return bootstrapContext;}//3.private void configureHeadlessProperty() {//this.headless默认为trueSystem.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));}private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";//4.private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };//getSpringFactoriesInstances 去 spring.factories 找 SpringApplicationRunListenerreturn new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),this.applicationStartup);}//7.准备环境private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// Create and configure the environment//返回或者创建基础环境信息对象,如:StandardServletEnvironment, StandardReactiveWebEnvironmentConfigurableEnvironment environment = getOrCreateEnvironment();//配置环境信息对象,读取所有的配置源的配置属性值。configureEnvironment(environment, applicationArguments.getSourceArgs());//绑定环境信息ConfigurationPropertySources.attach(environment);//7.1 通知所有的监听器当前环境准备完成listeners.environmentPrepared(bootstrapContext, environment);DefaultPropertiesPropertySource.moveToEnd(environment);configureAdditionalProfiles(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;}//8.private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {//保存环境信息context.setEnvironment(environment);//IOC容器的后置处理流程postProcessApplicationContext(context);//应用初始化器applyInitializers(context);//8.1 遍历所有的 listener 调用 contextPrepared。//EventPublishRunListenr通知所有的监听器contextPreparedlisteners.contextPrepared(context);bootstrapContext.close(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));//8.2listeners.contextLoaded(context);}//11.调用所有runnersprivate void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<>();//获取容器中的 ApplicationRunnerrunners.addAll(context.getBeansOfType(ApplicationRunner.class).values());//获取容器中的 CommandLineRunnerrunners.addAll(context.getBeansOfType(CommandLineRunner.class).values());//合并所有runner并且按照@Order进行排序AnnotationAwareOrderComparator.sort(runners);//遍历所有的runner。调用 run 方法for (Object runner : new LinkedHashSet<>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}}//13.private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,SpringApplicationRunListeners listeners) {try {try {handleExitCode(context, exception);if (listeners != null) {//14.listeners.failed(context, exception);}}finally {reportFailure(getExceptionReporters(context), exception);if (context != null) {context.close();}}}catch (Exception ex) {logger.warn("Unable to close ApplicationContext", ex);}ReflectionUtils.rethrowRuntimeException(exception);}...
}
//2. new SpringApplication(primarySources).run(args) 最后返回的接口类型
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {String CONFIG_LOCATION_DELIMITERS = ",; \t\n";String CONVERSION_SERVICE_BEAN_NAME = "conversionService";String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";String ENVIRONMENT_BEAN_NAME = "environment";String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";String APPLICATION_STARTUP_BEAN_NAME = "applicationStartup";String SHUTDOWN_HOOK_THREAD_NAME = "SpringContextShutdownHook";void setId(String var1);void setParent(@Nullable ApplicationContext var1);void setEnvironment(ConfigurableEnvironment var1);ConfigurableEnvironment getEnvironment();void setApplicationStartup(ApplicationStartup var1);ApplicationStartup getApplicationStartup();void addBeanFactoryPostProcessor(BeanFactoryPostProcessor var1);void addApplicationListener(ApplicationListener<?> var1);void setClassLoader(ClassLoader var1);void addProtocolResolver(ProtocolResolver var1);void refresh() throws BeansException, IllegalStateException;void registerShutdownHook();void close();boolean isActive();ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
#4.
#spring.factories
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
class SpringApplicationRunListeners {private final Log log;private final List<SpringApplicationRunListener> listeners;private final ApplicationStartup applicationStartup;SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners,ApplicationStartup applicationStartup) {this.log = log;this.listeners = new ArrayList<>(listeners);this.applicationStartup = applicationStartup;}//5.遍历 SpringApplicationRunListener 调用 starting 方法;//相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});}//7.1void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {doWithListeners("spring.boot.application.environment-prepared",(listener) -> listener.environmentPrepared(bootstrapContext, environment));}//8.1void contextPrepared(ConfigurableApplicationContext context) {doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context));}//8.2void contextLoaded(ConfigurableApplicationContext context) {doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context));}//10.void started(ConfigurableApplicationContext context) {doWithListeners("spring.boot.application.started", (listener) -> listener.started(context));}//12.void running(ConfigurableApplicationContext context) {doWithListeners("spring.boot.application.running", (listener) -> listener.running(context));}//14.void failed(ConfigurableApplicationContext context, Throwable exception) {doWithListeners("spring.boot.application.failed",(listener) -> callFailedListener(listener, context, exception), (step) -> {step.tag("exception", exception.getClass().toString());step.tag("message", exception.getMessage());});}private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,Consumer<StartupStep> stepAction) {StartupStep step = this.applicationStartup.start(stepName);this.listeners.forEach(listenerAction);if (stepAction != null) {stepAction.accept(step);}step.end();}...}
86、原理解析-自定义事件监听组件
MyApplicationContextInitializer.java
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;public class MyApplicationContextInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("MyApplicationContextInitializer ....initialize.... ");}
}
MyApplicationListener.java
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;public class MyApplicationListener implements ApplicationListener {@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println("MyApplicationListener.....onApplicationEvent...");}
}
MyApplicationRunner.java
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Order(1)
@Component//放入容器
public class MyApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("MyApplicationRunner...run...");}
}
MyCommandLineRunner.java
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/*** 应用启动做一个一次性事情*/
@Order(2)
@Component//放入容器
public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("MyCommandLineRunner....run....");}
}
MySpringApplicationRunListener.java
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;public class MySpringApplicationRunListener implements SpringApplicationRunListener {private SpringApplication application;public MySpringApplicationRunListener(SpringApplication application, String[] args){this.application = application;}@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {System.out.println("MySpringApplicationRunListener....starting....");}@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {System.out.println("MySpringApplicationRunListener....environmentPrepared....");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener....contextPrepared....");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener....contextLoaded....");}@Overridepublic void started(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener....started....");}@Overridepublic void running(ConfigurableApplicationContext context) {System.out.println("MySpringApplicationRunListener....running....");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("MySpringApplicationRunListener....failed....");}
}
注册MyApplicationContextInitializer
,MyApplicationListener
,MySpringApplicationRunListener
:
resources / META-INF / spring.factories
:
org.springframework.context.ApplicationContextInitializer=\com.lun.boot.listener.MyApplicationContextInitializerorg.springframework.context.ApplicationListener=\com.lun.boot.listener.MyApplicationListenerorg.springframework.boot.SpringApplicationRunListener=\com.lun.boot.listener.MySpringApplicationRunListener
87、后会有期
路漫漫其修远兮,吾将上下而求索。
纸上得来终觉浅,绝知此事要躬行。
Spring Boot 2 场景整合篇
- 虚拟化技术
- 安全控制
- 缓存技术
- 消息中间件
- 对象存储
- 定时调度
- 异步任务
- 分布式系统
Spring Boot 2 响应式编程
- 响应式编程基础
- Webflux开发Web应用
- 响应式访问持久化层
- 响应式安全开发
- 响应式原理
springboot的学习(2)相关推荐
- 8. SpringBoot基础学习笔记
SpringBoot基础学习笔记 课程前置知识说明 1 SpringBoot基础篇 1.1 快速上手SpringBoot SpringBoot入门程序制作 1.2 SpringBoot简介 1.2.1 ...
- SpringBoot基础学习之SpringBoot配置(上篇)
前言: 小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师. 这个SpringB ...
- SpringBoot基础学习之整合Swagger框架(上篇)
前言: 小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师. 这个SpringB ...
- SpringBoot入门学习(六)之云收藏项目
目录 1.Spring Boot的思维导图分析 2.项目介绍 3.项目分析 4.创建SpringBoot项目,添加依赖 5.数据库 6.特别说明 7.效果展示 8.源码下载地址 本篇文章是Spring ...
- 《SpringBoot框架学习二之HTTP协议》
<SpringBoot框架学习二之HTTP协议> 文章目录 <SpringBoot框架学习二之HTTP协议> 一.HTTP介绍 (1)概述 (2)HTTP版本协议 1.HTTP ...
- 【SpringBoot】最新版2019Spring Boot配置解析,源码解析(速成SpringBoot)——学习笔记版【2】
SpringBoot配置文件 文章目录 SpringBoot配置文件 四.配置文件 1.简介 2.YAML用法 2.1 简介 2.2语法 3.为属性注入值 3.1使用.yml配置文件 3.1编写.ym ...
- SpringBoot入门学习(五)之旅游网站项目
目录 1.项目需求 2.技术需求 3.pom.xml 4.application.yml 4.MybatisPlus的配置文件 5.springboot中配置日期类型转换器 6.用户管理 7.线路管理 ...
- SpringBoot基础学习之整合SpringSercurity框架
前言: 小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师. 这个SpringB ...
- 项目记录(1)---SpringBoot的学习
SpringBoot 0.学习目标 了解SpringBoot的作用 掌握java配置的方式 了解SpringBoot自动配置原理 掌握SpringBoot的基本使用 了解Thymeleaf的基本使用 ...
- SpringBoot+Shiro学习(一):主要模块介绍
这篇文章是我最近对SpringBoot+Shiro+mybatis+redis一个练手项目的记录. 我是按照慕课网的一篇课程+百度进行练手的 慕课课程 练手项目Github地址 跟着开涛学Shiro ...
最新文章
- 使用Rainbow tables和Ophcrack的组合工具破解Windows密码
- 警笛声c语言程序,PIC警笛声音的程序
- 数学界的花木兰——苏菲﹒热尔曼
- 感知器模型为什么不能解决异或问题
- 基于RTP协议的数据通讯程序
- [Ext JS6] Grid不同列的关联编辑
- 计算机科学与技术python方向_专业解读丨计算机科学与技术
- 商品的SPU与SKU的区别
- Android Studio 使用魅族手机调试时,不显示 Log 的解决方法
- linux使用定时器回调函数示例
- parallel循环java_使用Java8新特性parallelStream遇到的坑
- 如何启用计算机的休眠,win7休眠-win7如何启用休眠,我已经google过了,没用,请大家帮忙我? 爱问知识人...
- 高质量的博客评论外链有用么?
- neo4j数据可视化_我如何使用neo4j可视化来自半结构化数据的连接
- 电脑键盘上各个按键功能详解
- ARFoundation多图切换识别
- C#_打包发布变成一个绿色版本软件——Costura.Fody合并DLL和EXE
- 周年更名,元宇宙产业委再上新台阶
- 22-23 - 页式内存管理
- 如何将多个excel表格合并成一个_如何将多个PDF文档合并为一个