SpringBoot原理解析

目录

        • 依赖管理
          • 父项目依赖
          • starter场景启动器
        • 自动配置
          • @Bean 和@Component 的区别:
          • 容器功能注解
        • 简化开发
        • web场景开发
          • 请求处理
          • 响应请求
        • thymeleaf
          • 视图解析原理流程
        • HandlerInterceptor拦截器
        • 文件上传
        • 异常处理
          • 错误处理默认规则
          • 定制错误处理逻辑
          • 异常处理自动配置
          • 异常处理步骤流程
        • Web原生组件注入(Servlet、Filter、Listener)
        • Druid数据源
        • 整合Redis
        • 单元测试
          • JUnit5常用注解
          • 断言assertions
          • 前置条件(assumptions)
          • 嵌套测试
          • 参数化测试
        • 指标监控Actuator
          • Actuator Endpoint
          • 管理Endpoints
          • 定制 Endpoint
          • 可视化应用监控服务
        • 高级特性与原理解析
          • Profile功能
          • 外部化配置
          • springboot启动过程

springboot是一种简化springweb开发的框架,类似springmvc,他提供各种默认配置,达到开箱即用、敏捷开发的效果。本文主要介绍springboot的依赖管理、自动配置、web开发、thymeleaf与视图解析、拦截器、文件上传、异常处理、web原生组件注入、整合数据源Druid与Redis、Junit单元测试、spring Acutuator性能监控、高级特性与springboot启动原理。

依赖管理
父项目依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.1</version>
</parent>

作用:依赖管理及版本管理。

父项目的父项目:

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.7.1</version>
</parent>

其中默认引入了许多依赖,自动版本仲裁无需关注版本,我们可以不用配置某些依赖的版本,但是第三方需要。根据maven就近原则,默认使用本项目pom.xml配置好的依赖,若需要修改依赖版本号:

<properties><java.version>8</java.version><mysql.version>5.2.41</mysql.version>
</properties>
starter场景启动器

starter也叫依赖管理器,spring-boot-starter-*是用来开发某一场景的一组依赖,引入starter就导入了相关的开发依赖。

starter基本依赖为pring-boot-starter,web开发还需要引入spring-boot-starter-json、spring-boot-starter-tomcat、spring-web、spring-webmvc。

自动配置

启动时加载所有自动配置和组件,条件装配按需配置。自动配置tomcat、自动配置springMVC、自动配置web常见功能、自动扫描springboot主程序所在包及子包下的bean组件。

@SpringBootApplication注解相当于@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan注解。

自动配置按需加载,pom.xml引入依赖后该依赖的自动配置才会生效。配置会映射到某个配置类中,springboot默认配置会映射到名为xxProperties.class配置类中,配置类也是组件。

@Configuration声明配置类,参数proxyBeanMethods默认为true,开启代理,即使用cglib生成代理对象,只会在容器中生成一个组件示例。若为false,则不会生成代理对象,每次调用组件都会创建一个实例对象。

@Bean给容器中添加组件,以类名或方法名做id,返回类型为组件类型,返回值即组件在容器中的实例。@Bean注解的方法名默认作为对象的名字,也可以用name属性定义对象的名字。@bean分为两种模式,一种是Lite Mode(轻量模式),这种模式下被@bean定义的方法需要在@Component下或者原生类下,效果类似于@Component注册在类上的效果。@Configuration下的@bean被称为是Full mode,bean的创建是通过cglib代理生成的被@Configuration定义的类的增强类,因为@Configuration定义的类的bean,默认都是被Spring通过Cglib增强的子类。

@Bean注解参数:

value:定义bean在IOC容器中的id属性。
name :定义bean在IOC容器中的id属性。
autowire:装配方式
Autowire.NO (默认设置)
Autowire.BY_NAME
Autowire.BY_TYPE
initMethod:指定初始化方法 相当于xml文件中 init-method
destroyMethod:指定销毁的方法 相当于xml文件中 destroy-method

@Bean 和@Component 的区别:

@Bean@Component都是将Spring Bean添加到Spring Context 中。

1)作用域

@Component注解表明一个类会作为组件类,并告知 Spring 要为这个类创建 bean。@Bean不能作用在类上,只能作用于方法。

@Bean注解告诉 Spring 这个方法将会返回一个对象,这个对象要注册为 Spring 应用上下文中的 bean。要获取这个 bean 的时候,Spring 要按照这种方式、去获取这个 bean。

2)注册方式

@Component注解表明一个类会作为组件类,并告知 Spring 要为这个类创建 bean。@Bean注解告诉 Spring 这个方法将会返回一个对象,这个对象要注册为 Spring 应用上下文中的 bean。通常方法体中包含了最终产生bean实例的逻辑。

当我们引用第三方库中的类需要装配到 Spring 容器时,则只能通过@Bean来实现。

3)使用方式

@Component@Controller@Service@Repository)通常是通过类路径扫描来侦测及自动装配到 Spring 容器中。@Bean一般结合@Configuration一起使用,也可以配置在类的方法中。

容器功能注解

组件添加:

1、@Configuration

2、@Bean、@Component、@Controller、@Service

3、@ComponentScan

4、@Conditional

原生配置文件导入:

@ImportResource

配置绑定:

1、@ConfigurationProperties

2、@EnableConfigurationProperties+@ConfigurationProperties

3、@Component+@ConfigurationProperties

@Import({xx.class,xx.class}),通过类型在容器中创建组件实例,组件名为全类名。

@Conditional,条件装配注解,满足指定条件进行组件注册。参数value为一个class泛型数组。@ConditionalOnBean(name = “xxx”),当ioc容器中存在该对象时,为该组件注册实例。

@ImportResource("classpath:beans.xml"),导入spring原生配置文件,支持xml配置。

@EnableConfigurationProperties(xx.class),为xx.class开启配置属性绑定。

@AutoConfigurationPackage,自动配置包原则,利用Register批量地将主程序所在包下的所有组件批量注册进容器。

@ConfigurationProperties(prefix="spring"),为核心配置文件中的属性绑定前缀,在映射的实体类上添加注解,表示将该类注册成bean并配置属性值。

简化开发

lombok开发

1、在maven项目的pom.xml文件中添加lombok依赖

2、执行maven导入依赖

3、添加@Data注解,免去get和set方法

4、添加@ToString重写toString方法。

spring Initailizr开发,再idea中使用spring Initailizr创建初始化项目,勾选依赖配置和版本,maven自动导入,实现快速初始化项目。

yaml(yaml ain’t markup language)配置,key-value写法,大小写敏感,缩进表层级,#为注释。字符串值用单引号或双引号包围,单引号将\n作为字符串输出,双引号将\n作为换行输出。

注:标记语言是一种将标记以及文本相关的其他信息结合起来,展现出关于文档结构和数据处理细节的文字编码。

spring-boot-configuration-processor配置处理器,显示配置提示信息,添加依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional>
</dependency>
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!--在打包项目的时候排除以下简化开发的插件--><excludes><exclude><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></exclude></excludes></configuration></plugin></plugins>
</build>
web场景开发

src/main/java;src/main/resources都是classes类路径的根路径,下面的所有文件都在该类路径下。当请求来时,java先动态处理,无法处理后寻找静态资源。默认的静态资源路径有[/static,/resources,/META-INF,/public]

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/" };

欢迎页处理:

WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {//要用欢迎页功能,必须是/**logger.info("Adding welcome page: " + welcomePage.get());setRootViewName("forward:index.html");}else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {// 调用Controller  /indexlogger.info("Adding welcome page template: index");setRootViewName("index");}
}
请求处理
<form action="/user" method="post"><input name="_method" type="hidden" value="delete"/><input type="submit" value="delete提交"/>
</form>

核心Filter:HiddenHttpMethodFilter;用法: 表单method=post,隐藏域 _method=put,SpringBoot中手动开启。

spring:mvc:hiddenmethod:filter:enabled: true   #开启页面表单的Rest功能

设置自定义的methodFilter,编写webconfig配置类,创建filter对象,调用HiddenHttpMethodFilter的setMethodParam方法。

Rest原理

  • 表单提交会带上_method=PUT

  • 请求过来被HiddenHttpMethodFilter拦截

  • 请求是否正常,并且是POST

  • 获取到_method的值。

    • 兼容以下请求;PUT、DELETE、PATCH
    • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
    • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper。

Rest使用客户端工具,PostMan直接发送Put、delete等方式请求,无需Filter。

请求映射原理:

SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet->doDispatch()

RequestMappingHandlerMapping:保存了所有@RequestMapping和handler的映射规则。

所有的请求映射都在HandlerMapping中。

  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping ,访问 /能访问到index.html
  • SpringBoot自动配置了默认的RequestMappingHandlerMapping
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。如果有就找到这个请求对应的handler,如果没有就是下一个 HandlerMapping。

基本注解:

路径变量@PathVariable、获取请求头@RequestHeader、模型属性@ModelAttribute、声明请求参数@RequestParam、获取post请求体@RequestBody、声明请求属性@RequestAttribute、矩阵变量@MatrixVariable、获取cookie值@CookieValue、请求处理映射路径@RequestMapping

package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@SpringBootApplication
@RestController
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@GetMapping("/hello")public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {return String.format("Hello %s!", name);}
}

Servlet API:

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId。

复杂参数

MapModel(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

自定义对象参数:

可以自动类型转换与格式化,可以级联封装

@Data
public class Person { private String userName;private Integer age;private Pet pet; 
}
@Data
public class Pet {private String name;private String age;}

参数处理原理

  • HandlerMapping中找到能处理请求的Handler(Controller.method())
  • 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值

参数解析器-HandlerMethodArgumentResolver

确定将要执行的目标方法的每一个参数的值是什么,SpringMVC目标方法能写多少种参数类型。取决于参数解析器。

响应请求

响应JSON , jackson+@ResponseBody(返回数据不返回页面视图)

SpringMVC到底支持以下返回值:

ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
@ResponseBody---> RequestResponseBodyMethodProcessor;

返回值解析器原理

  • 返回值处理器判断是否支持这种类型返回值 supportsReturnType

  • 返回值处理器调用 handleReturnValue 进行处理

  • RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的

  • 底层利用 MessageConverters 进行处理 ,将数据写为json

  • 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)

    2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,

    3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理

  • 得到MappingJackson2HttpMessageConverter可以将对象写为json

根据客户端接收能力不同,返回不同媒体类型的数据,只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

导入了jackson处理xml的包,xml的converter就会自动进来:

 <dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
</dependency>

开启浏览器参数方式内容协商功能:

spring:contentnegotiation:favor-parameter: true  
#开启基于请求参数内容协商策略

内容协商原理

1、判断当前响应头中是否已经有确定的媒体类型MediaType。

2、获取客户端支持接收的内容类型。(获取客户端Accept请求头字段)。

​ contentNegotiationManager内容协商管理器两种内容协商策略:

​ (1)ParameterContentNegotiationStrategy 基于format参数(mediaType有json;xml)

​ (2)HeaderContentNegotiationStrategy 默认基于请求头(mediaType为xml)

3、遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象。

4、找到支持操作Person的converter,把converter支持的媒体类型统计出来。

5、客户端需要【application/xml】,服务端能力【10种、json、xml】。

6、进行内容协商的最佳匹配媒体类型。

7、用支持将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。

自定义返回值mediaType数据格式,配置内容协商策略,添加自定义的mediaType类型。

**自定义MessageConverter **

实现多协议数据兼容。json、xml…

(1)、@ResponseBody 返回响应数据出去,调用 RequestResponseBodyMethodProcessor 处理

(2)、Processor 处理方法返回值,通过 MessageConverter 处理

(3)、所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)

(4)、内容协商找到最终的 messageConverter

thymeleaf

一个XML/XHTML/HTML5模板引擎,可用于Web与非Web环境中的应用开发。它是一个开源的现代化服务端Java模板引擎,是整合 Spring MVC 的可选模块,在应用开发中,使用 Thymeleaf 来代替 JSP或其他模板引擎。官网

注:由于SpringBoot打包是以jar的方式,不是war。其次我们的tomcat是嵌入式的,所以现在SpringBoot默认不支持jsp。Spring官方文档

基本语法

表达式名字语法用途
变量取值${…}获取请求域、session域、对象等值
选择变量*{…}获取上下文对象值
消息#{…}获取国际化等值
链接@{…}生成链接
片段表达式~{…}jsp:include 作用,引入公共页面片段

2、字面量

文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false

空值: null

变量: one,two,… 变量不能有空格

3、文本操作

字符串拼接: +

变量替换: |The name is ${name}|

4、数学运算

运算符: + , - , * , / , %

5、布尔运算

运算符: and , or

一元运算: ! , not

6、比较运算

比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )

7、条件运算

If-then: (if) ? (then)

If-then-else: (if) ? (then) : (else)

Default: (value) ?: (defaultvalue)

8、特殊操作

无操作: _

迭代

<tr th:each="prod : ${prods}"><td th:text="${prod.name}">Onions</td><td th:text="${prod.price}">2.41</td><td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

使用thymeleaf

引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在相关页面中定义命名空间:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form class="layui-form" lay-filter="userForm" id="userForm"><!--/*@thymesVar id="myUserName" type="ch"*/--><input type="text" th:text="${myUserName}" name="userName" lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
<a href="www.xxxx.com" th:href="${link}">百度</a> 
</form> 
</body>
</html>

controller返回对应页面:

@RequestMapping("/editUser")
public String editUser(Model model){User u = userService.getUser();model.addAttribute("myUserName",u.getUserName());model.addAttribute("myNickName",u.getNickName());model.addAttribute("link","www.baidu.com");return "user";
}
视图解析原理流程

1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址

2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer

3、任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。

4、processDispatchResult 处理派发结果(页面改如何响应)

  • render(mv, request, response); 进行页面渲染逻辑
  • 根据方法的String返回值得到 View 对象(定义了页面的渲染逻辑),所有的视图解析器尝试是否能根据当前返回值得到View对象,得到了 redirect:/main.html --> Thymeleaf new RedirectView(),ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。view.render(mv.getModelInternal(), request, response),视图对象调用自定义的render进行页面渲染工作。
  • RedirectView 如何渲染【重定向到一个页面】
    • 1、获取目标url地址
    • 2、response.sendRedirect(encodedURL)

视图解析:

1)返回值以 forward: 开始: new InternalResourceView(forwardUrl)–> 转发request.getRequestDispatcher(path).forward(request, response);

2)返回值以 redirect: 开始: new RedirectView() -> render就是重定向

3)返回值是普通字符串: new ThymeleafView()

HandlerInterceptor拦截器

定义拦截器,实现HandlerInterceptor接口,重写preHandler方法:

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取sessionHttpSession httpSession = request.getSession();if(httpSession.getAttribute("loginUser")==null){response.sendRedirect("/f/toLogin");return false;}return true;}
}

webMvcConfig重写addInterceptor方法:

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/","/login.html","/f/toLogin","/user/login","/img/*","/layui/**");}
}

拦截器执行过程:

preHandler->目标方法->postHandler->afterCompletion

原理

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有拦截器】

2、先顺序执行 所有拦截器的 preHandle方法

  • 如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle。
  • 如果当前拦截器返回为false。直接倒序执行所有已经执行了的拦截器的afterCompletion。

3、如果任何一个拦截器返回false。直接跳出不执行目标方法。

4、所有拦截器都返回True,执行目标方法。

5、倒序执行所有拦截器的postHandle方法。

6、前面的步骤有任何异常都会直接倒序触发 afterCompletion

7、页面成功渲染完成以后,也会倒序触发 afterCompletion

文件上传

文件上传自动配置类-MultipartAutoConfiguration,spring boot自动配置好了 StandardServletMultipartResolver 文件上传解析器。

原理步骤:

1、请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求。

2、参数解析器来解析请求中的文件内容封装成MultipartFile。

3、将request中多个文件信息封装为一个Map ,MultiValueMap<String, MultipartFile> 。

注:可以FileCopyUtils文件复制工具类的copy方法,实现文件流的拷贝。

核心配置文件中配置spring文件上传大小:

spring:servlet:multipart:max-file-size: 10MBmax-request-size: 100MB

前端页面表单:

<!--post提交,上传至/upload,contentType="multipart/form-data"-->
<form method="post" action="/upload" enctype="multipart/form-data"><input type="file" name="file"><br><input type="submit" value="提交">
</form>

后端接口:

    /*** MultipartFile 自动封装上传过来的文件* @RequestPart从请求中取multipartFile文件*/@PostMapping("/upload")public String upload(@RequestParam("username") String username,@RequestPart("headerImg") MultipartFile headerImg,@RequestPart("photos") MultipartFile[] photos) throws IOException {return "main";}
异常处理
错误处理默认规则
  • 默认情况下,Spring Boot提供/error处理所有错误的映射

  • 对于服务器端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器端,响应一个“ WhitelabelErrorView”,以HTML格式呈现。

  • 要对其进行自定义,添加View解析为error

  • 要完全替换默认行为,可以实现 ErrorController 并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。

  • templates/error/下的4xx,5xx页面会被自动解析;

定制错误处理逻辑
  • 自定义错误页

  • error/404.html error/5xx.html,有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页。

  • @ControllerAdvice+@ExceptionHandler处理全局异常,底层是使用ExceptionHandlerExceptionResolver

  • @ResponseStatus(返回状态码)+自定义异常 ,底层是使用ResponseStatusExceptionResolver,把@ResponseStatus注解的信息封装成ModelAndView并返回,底层调用 response.sendError(statusCode, resolvedReason)。

  • Spring底层的异常,如参数类型转换异常,使用DefaultHandlerExceptionResolver 处理框架底层的异常。

  • response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage())

  • 自定义实现 HandlerExceptionResolver接口的异常解析器,可以作为默认的全局异常处理规则。

  • ErrorViewResolver实现自定义处理异常,response.sendError(411,“自定义异常”),error请求就会转给controller。异常没有任何人能处理,则tomcat底层调用response.sendError,error请求就会转给controller。BasicErrorController要去的页面地址是ErrorViewResolver 。

异常处理自动配置
  • ErrorMvcAutoConfiguration 自动配置异常处理规则

  • 容器中的组件:类型:DefaultErrorAttributes -> id:errorAttributes。

  • public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver。

DefaultErrorAttributes:定义错误页面中可以包含哪些数据。

  • 容器中的组件:类型:BasicErrorController --> id:basicErrorController(json+白页 适配响应)。

  • 处理默认 /error 路径的请求,页面响应 new ModelAndView(“error”, model)。

    • 容器中有组件 View->id是error(响应默认错误页)。
  • 容器中放组件 BeanNameViewResolver,按照返回的视图名作为组件的id去容器中找View对象。

  • 容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver。

  • 如果发生错误,会以HTTP的状态码作为视图页地址(viewName),找到真正的页面。

    • viewName:error/404、5xx.html。

如果想要返回页面,就会找error视图StaticView类型的defaultErrorView,默认是一个白页。其中定义了类WhitelabelErrorViewConfiguration。

异常处理步骤流程

1、执行目标方法,目标方法运行期间有任何异常都会被catch,而且标志当前请求结束,并且用 dispatchException封装 。

2、进入视图解析页面渲染

mav = processDispatchResult(processedRequest, response, mappedHandler, mav, dispatchException);

3、处理handler发生的异常,处理完成返回ModelAndView(跳转地址和页面数据)。

  • 遍历所有的handlerExceptionResolvers,找到HandlerExceptionResolver处理器异常解析器。

  • 默认异常解析器(DefaultErrorAttributes、DefaultHandlerExceptionResolver、ExceptionHandlerExceptionResolver、ResponseHandlerExceptionResolver)

  • DefaultErrorAttributes定义错误信息(exception、status、stack_trace、error、message、path),把异常信息保存到request域,并且返回null。默认没有任何人能处理异常,所以异常会被抛出

  • 如果没有任何人能处理最终底层就会发送 /error 请求,会被底层的BasicErrorController处理。解析错误视图,遍历所有的 ErrorViewResolver解析,找到默认的DefaultErrorViewResolver(作用是把响应状态码作为错误页的地址),模板引擎最终响应这个页面error/xxx.html。

Web原生组件注入(Servlet、Filter、Listener)

使用Servlet API

@ServletComponentScan(basePackages = "com.xxx.xxx") :指定原生Servlet组件都放在那里

@WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器。

@WebFilter(urlPatterns={"/css/\*","/images/\*"})

@WebListener

DispatchServlet 如何注册进来

  • 容器中自动配置了 DispatcherServlet 属性绑定到 WebMvcProperties;对应的配置文件配置项是 spring.mvc。
  • 通过ServletRegistrationBean<DispatcherServlet>把 DispatcherServlet 配置进来。
  • 默认映射的是 / 路径。

Tomcat-Servlet;

多个Servlet都能处理到同一层路径,精确优选原则

A: /my/

B: /my/1

使用RegistrationBean

ServletRegistrationBean`, `FilterRegistrationBean`, and `ServletListenerRegistrationBean

嵌入式Servlet容器

默认支持的webServer有Tomcat, Jetty, or UndertowServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器。

原理

(1) SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat,web应用会创建一个web版的ioc容器 ServletWebServerApplicationContextServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFactory(Servlet 的web服务器工厂—> Servlet 的web服务器)。

(2) SpringBoot底层默认有很多的WebServer工厂,TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory。底层直接会有一个自动配置类ServletWebServerFactoryAutoConfiguration,它导入了ServletWebServerFactoryConfiguration

(3) ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有TomcatServletWebServerFactory ,它创建出Tomcat服务器并启动。TomcatWebServer 的构造器拥有初始化方法initialize—this.tomcat.start()。

(4) 内嵌服务器就是手动把启动服务器的代码调用,前提是tomcat核心jar包存在。

定制Servlet容器

  • 实现 WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> 接口的xxxCustomizer定制化器,可以改变xxxx的默认规则。

  • 把配置文件的值和ServletWebServerFactory 进行绑定。

  • 修改核心配置文件,server.xxx。

  • 直接自定义 ConfigurableServletWebServerFactory

  • 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能+ @Bean给容器中再扩展一些组件。

Druid数据源
  • Druid主要解决的问题就是传统数据库无法解决的大数据量查询性能的问题
  • 本质就是一个分布式支持实时数据分析的数据存储系统

在实际应用中,MyBatis 可以利用 Druid 作为其连接池,这样可以避免频繁地创建和关闭连接。MyBatis 会指定 Druid 作为连接池,并从中获取连接。这样MyBatis 从 Druid 中获得连接的管理工作,专注于数据处理本身。总结来说,Druid 为 MyBatis 提供了一个高效且可靠的连接池服务,使得MyBatis能够在不需要手动管理连接的情况下进行数据库操作。

引入druid-starter:

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>

系统中所有filter:

别名Filter类名
defaultcom.alibaba.druid.filter.stat.StatFilter
statcom.alibaba.druid.filter.stat.StatFilter
mergeStatcom.alibaba.druid.filter.stat.MergeStatFilter
encodingcom.alibaba.druid.filter.encoding.EncodingConvertFilter
log4jcom.alibaba.druid.filter.logging.Log4jFilter
log4j2com.alibaba.druid.filter.logging.Log4j2Filter
slf4jcom.alibaba.druid.filter.logging.Slf4jLogFilter
commonloggingcom.alibaba.druid.filter.logging.CommonsLogFilter

核心配置:

spring:datasource:   druid:url: jdbc:mysql://localhost:3306/dbusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driveraop-patterns: com.xxx.admin.*  #监控SpringBeanfilters: stat,wall     # 底层开启功能,stat(sql监控),wall(防火墙)stat-view-servlet:   # 配置监控页功能enabled: truelogin-username: adminlogin-password: 123456resetEnable: falseweb-stat-filter:  # 监控webenabled: trueurlPattern: /*exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'filter:stat:    # 对上面filters里面的stat的详细配置slow-sql-millis: 1000logSlowSql: trueenabled: truewall:enabled: trueconfig:drop-table-allow: false

SpringBoot配置示例

配置项列表

整合Redis

Redis 是一个开源的内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

核心配置:

spring:redis:host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.comport: 6379password: lfy:Lfy123456#client-type: jedis# jedis:# pool:#  max-active: 10

自动配置:

  • RedisAutoConfiguration 自动配置类。RedisProperties 属性类 ,spring.redis.xxx是对redis的配置。
  • 连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration。
  • 自动注入了RedisTemplate<Object, Object> : xxxTemplate。
  • 自动注入了StringRedisTemplate;k:v都是String。
  • 底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis。

连接测试:

  @Testvoid testRedis(){ValueOperations<String, String> operations = redisTemplate.opsForValue();operations.set("hello","world");String hello = operations.get("hello");System.out.println(hello);}
单元测试

Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库,作为最新版本的JUnit框架,JUnit5与之前版本的Junit框架有很大的不同。由三个不同子项目的几个不同模块组成。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

1)JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。

2)JUnit Jupiter: 提供了JUnit5的新的编程模型,是JUnit5新特性的核心,内部包含了一个测试引擎,用于在Junit Platform上运行。

3)JUnit Vintage: 提供了兼容JUnit4.x,Junit3.x的测试引擎。

相关变化:

  • 注解在 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。

注意:SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖,不能使用junit4的功能 @Test,JUnit 5’s Vintage Engine 从spring-boot-starter-test中移除,如果需要继续兼容junit4需要自行引入vintage。

引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<!--兼容junit4-->
<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>

SpringBoot整合Junit以后,编写测试方法:@Test标注(注意需要使用junit5版本的注解),Junit类具有Spring的功能,@Autowired、@Transactional 标注测试方法,测试完成后自动回滚。

JUnit5常用注解

JUnit5与JUnit4的注解变化

  • @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
  • @ParameterizedTest :表示方法是参数化测试
  • @RepeatedTest :表示方法可重复执行
  • @DisplayName :为测试类或者测试方法设置展示名称
  • @BeforeEach :表示在每个单元测试之前执行
  • @AfterEach :表示在每个单元测试之后执行
  • @BeforeAll :表示在所有单元测试之前执行
  • @AfterAll :表示在所有单元测试之后执行
  • @Tag :表示单元测试类别,类似于JUnit4中的@Categories
  • @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
  • @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
  • @ExtendWith :为测试类或测试方法提供扩展类引用
断言assertions

断言(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");
}
前置条件(assumptions)

JUnit 5 中的前置条件类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止直接跳过。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。

@DisplayName("前置条件")
public class AssumptionsTest {private final String environment = "DEV";@Test@DisplayName("assumeTrue")public void simpleAssume() {assumeTrue(Objects.equals(this.environment, "DEV"));assumeFalse(() -> Objects.equals(this.environment, "PROD"));}@Test@DisplayName("assumingThat")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 注解,而且嵌套的层次没有限制。但是,嵌套内部的@BeforeEach@AfterEach 方法不会对外部的@Test单元测试生效。相反,外层的会对内层的单元测试生效。

class xxx {@Test@DisplayName("is empty")void isEmpty() {}@Nested@DisplayName("after")class After {}
}
参数化测试

参数化测试是JUnit5很重要的一个新特性,用不同的参数多次运行测试,为我们的单元测试带来许多便利。

相关注解:

@ParameterizedTest:代表这是一个参数化测试单元,而不是普通测试单元。

@ValueSource:为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型,使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。

@NullSource: 表示为参数化测试提供一个null的入参。

@EnumSource: 表示为参数化测试提供一个枚举入参。

@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参。

@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)。

@ParameterizedTest
@ValueSource(strings = {"x", "xx", "xxx"})
public void parameterizedTest1(String string) {System.out.println(string);Assertions.assertTrue(StringUtils.isNotBlank(string));
}
@ParameterizedTest
@MethodSource("method")    //指定方法名
public void testWithExplicitLocalMethodSource(String name) {System.out.println(name);
}static Stream<String> method() {return Stream.of("xxx", "xx");
}

注:当然如果参数化测试仅仅只能做到指定普通的入参,他的强大之处的地方在于可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参。

指标监控Actuator

未来每一个微服务在云上部署以后,都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。

引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2.x版本与1.x版本不同在于在支持MVC的基础上支持webFlux函数式编程、注解扩展、丰富的安全策略,底层使用MicroMeter。

访问路径:localhost:8080/actuator,localhost:8080/actuator/EndPoint

相关配置:

#management是actuator的配置
management:endpoints:enabled-by-default: true #暴露所有JMX端点信息web:exposure:include: '*'  #以web方式暴露所有端点
Actuator Endpoint

常用的Endpoints监控端点

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.namelogging.file.path属性)。支持使用HTTPRange标头来检索部分日志文件的内容。
prometheus以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus

最常用的Endpoint

  • Health:监控状况
  • Metrics:运行时指标
  • Loggers:日志记录

Health Endpoint

健康检查端点,一般用于在云平台,平台会定时的检查应用的健康状况,status为up表示健康,为down不健康,web访问路径:localhost:8080/actuator/health。

Metrics Endpoint

提供详细的、层级的、空间指标信息,这些信息可以被主动推送或者被动获取方式得到。相关metric访问路径:localhost:8080/actuator/metrics/http.server.requests。

管理Endpoints

1、开启与禁用Endpoints

  • 默认所有的Endpoint除过shutdown都是开启的。
  • 需要开启或者禁用某个Endpoint,配置模式为management.endpoint.端点名.enabled = true
management:endpoint:health:show-details: alwaysenabled: true
  • 或者禁用所有的Endpoint然后手动开启指定的Endpoint
management:endpoints:enabled-by-default: falseendpoint:beans:enabled: truehealth:enabled: true

2、暴露Endpoints

支持的暴露方式

  • HTTP:默认只暴露health和info
  • JMX:默认暴露所有Endpoint,cmd使用jconsole本地访问
  • 除过health和info,剩下的Endpoint都应该进行保护访问。如果引入SpringSecurity,则会默认配置安全访问规则

JMX (Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架,提供了一种简单的、标准的监控和管理资源的性能监控方式。

定制 Endpoint

1、定制 Health 信息

监控端点的类名须以HealthIndicator结尾,方式一实现HealthIndicator接口:

@Component
public class MyHealthIndicator implements HealthIndicator {@Overridepublic Health health() {int errorCode = check(); if (errorCode != 0) {return Health.down().withDetail("Error Code", errorCode).build();}return Health.up().build();}
}

方式二继承AbstractHealthIndicator抽象类:

public class MyhealthHealthIndicator extends AbstractHealthIndicator{@Overrideprotected void doHealthCheck(Health.Builder builder) throws Exception {Map<String,Object> map = new HashMap<>();if(1 == 1){// 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);}
}

访问:localhost:8080/actuator/health 会返回的所有health信息,包括Myhealth。

2、定制info信息

常用两种方式:

1、编写核心配置文件

info:appName: MyAdminversion: 1.0.0#使用@@可以获取maven的pom文件值mavenProjectName: @project.artifactId@  mavenProjectVersion: @project.version@

2、编写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")).withDetail("hello","world");}
}

访问:localhost:8080/actuator/info 会输出以上方式返回的所有info信息。

3、定制Metrics信息

SpringBoot支持自动适配的Metrics。

增加定制Metrics:

class MyService{Counter counter;public MyService(MeterRegistry meterRegistry){//通过Registry注册metrics指标counter = meterRegistry.counter("myservice.hello.count");}public void hello(){//记录hello方法调用次数counter.increment();}
}
//也可以使用下面的方式
@Bean
MeterBinder queueSize(Queue queue) {return (registry) -> Gauge.builder("queueSize", queue::size).register(registry);
}

访问:localhost:8080/actuator/metrics/myservice.hello.count,会输出该监控指标信息。

4、自定义Endpoint

使用@EndPoint注解,属性id为EndPointName。

@Component
@Endpoint(id = "EPname")
public class DockerEndpoint {@ReadOperationpublic Map getDockerInfo(){return Collections.singletonMap("info","docker started...");}@WriteOperationprivate void stopDocker(){System.out.println("docker stopped....");}
}

访问:localhost:8080/actuator/EPname 获取以上信息。

开发ReadinessEndpoint来管理程序是否就绪,或者LivenessEndpoint来管理程序是否存活。

可视化应用监控服务

引入springboot-admin-server依赖:

<dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-server</artifactId><version>2.3.1</version>
</dependency>
<dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-client</artifactId><version>2.3.1</version>
</dependency>

在主配置类上添加@EnableAdminServer注解开启admin监控,在核心配置文件中配置:

#admin访问地址
spring.boot.admin.url=http://localhost:8080
#应用以ip注册
spring.boot.admin.instance.prefer-ip=true
#应用名
spring.application.name=hello
management.endpoints.enabled-by-default=true 
#暴露所有端点
management.endpoints.web.exposure.include=*
#以web方式暴露所有端点
高级特性与原理解析
Profile功能

为了方便多环境适配,springboot简化了profile功能。

1、application-profile功能

  • 默认配置文件 application.yaml,任何时候都会加载。

  • 指定环境配置文件: application-{env}.yaml,env可为test、prod。

  • 激活指定环境并指定相关配置参数。

    激活profile环境方式:

    一、配置文件激活:spring.profiles.active=prod

    二、命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=666

  • 修改配置文件的任意值,命令行优先。

  • 默认配置与环境配置同时生效。

  • 同名配置项,profile配置优先。

2、@Profile条件装配功能

该注解使类或方法等在指定配置环境下生效。

@Configuration(proxyBeanMethods = false)
@Profile("prod")//该类在生产环境下生效
public class ProductionConfiguration {// ...
}

3、profile分组

批量加载配置文件,互补配置,注意不要冲突。

spring.profiles.group.myprod[0]=prod
spring.profiles.group.myprod[1]=test
spring.profiles.active=myprod  
#激活
外部化配置

1、外部配置源

常用:Java属性文件、YAML文件、环境变量、命令行参数

2、springboot配置文件查找位置

(1) classpath 根路径

(2) classpath 根路径下config目录

(3) jar包当前目录

(4) jar包当前目录的config目录

(5) /config子目录的直接子目录

注:指定环境优先,外部优先,后面的可以覆盖前面的同名配置项

3、配置文件加载顺序

  1. 当前jar包内部的application.properties和application.yml
  2. 当前jar包内部的application-{profile}.properties 和 application-{profile}.yml
  3. 引用的外部jar包的application.properties和application.yml
  4. 引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml
springboot启动过程

starter启动原理:

  • autoconfigure包中配置使用 META-INF/spring.factories EnableAutoConfiguration的值,使得项目启动加载指定的自动配置类。
  • 编写自动配置类 xxxAutoConfiguration -> xxxxProperties
@Configuration
@Conditional
@EnableConfigurationProperties
@Bean

过程:引入starter —>xxxAutoConfiguration —> 容器中放入组件 —> 绑定xxxProperties.java —> 配置项

springboot应用启动过程:

  • 创建 SpringApplication

    • 保存一些信息。
    • 判定当前应用的类型。
    • bootstrappers:初始启动引导器,去spring.factories文件中找 org.springframework.boot.Bootstrapper
    • 找 ApplicationContextInitializer初始化器·,去spring.factories找 ApplicationContextInitializer
    • 找 ApplicationListener应用监听器(事件通知),去spring.factories找ApplicationListener
  • 运行 SpringApplication

    • StopWatch监控应用启停。
    • 记录应用的启动时间。
    • 创建引导上下文(Context环境)createBootstrapContext(),获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置。
    • 让当前应用进入headless模式。
    • 获取所有RunListener(运行监听器),getSpringFactoriesInstancesspring.factoriesSpringApplicationRunListener
    • 遍历SpringApplicationRunListener调用 starting 方法,相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
    • 保存命令行参数ApplicationArguments
    • 准备环境 prepareEnvironment()
      • 返回或者创建基础环境信息对象StandardServletEnvironment
      • 配置环境信息对象,读取所有的配置源的配置属性值
      • 绑定环境信息
      • 监听器调用listener.environmentPrepared()通知所有的监听器当前环境准备完成
      • 创建IOC容器createApplicationContext
        • 根据项目类型(Servlet)创建容器
        • 当前会创建AnnotationConfigServletWebServerApplicationContext
    • 准备ApplicationContext IOC容器的基本信息 prepareContext()
      • 保存环境信息
      • IOC容器的后置处理流程
      • 应用初始化器applyInitializers
        • 遍历所有的 ApplicationContextInitializer,调用 initialize.来对ioc容器进行初始化扩展功能
        • 遍历所有的 listener 调用 contextPrepared,EventPublishRunListenr,通知所有的监听器contextPrepared
      • 所有的监听器 调用 contextLoaded,通知所有的监听器 contextLoaded。
    • 刷新IOC容器refreshContext,创建容器中的所有组件
    • 容器刷新完成后工作afterRefresh
    • 所有监听器调用 listeners.started(context),通知所有的监听器started
    • 调用所有runners,callRunners()
      • 获取容器中的ApplicationRunner
      • 获取容器中的 CommandLineRunner
      • 合并所有runner并且按照@Order进行排序
      • 遍历所有的runner,调用 run 方法
    • 如果以上有异常,调用Listener 的 failed方法。
    • 调用所有监听器的 running 方法listeners.running(context),通知所有的监听器应用运行中
    • running如果有问题,继续通知 failed ,调用所有 Listener 的 failed,通知所有的监听器 failed

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/747543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信分销商城小程序开发定制价格解析

在互联网时代&#xff0c;电子商务的飞速发展使得微信分销商城小程序成为消费者购物体验的一大利器&#xff0c;同时也为企业带来了更多的商业机遇。那么&#xff0c;微信分销商城小程序开发定制的价格究竟是多少呢&#xff1f;这个问题需要从多个方面来加以考虑。首先&#xf…

代码随想录|Day20|二叉树09|669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

669. 修剪二叉搜索树 思路&#xff1a;利用二叉搜索树的性质&#xff0c;对于每个节点&#xff0c;判断其是否在区间内&#xff1a; 如果节点值 < low&#xff0c;则此节点和其左子树都不在范围内如果节点值 > high&#xff0c;则此节点和其右子树都不在范围内如果 low &…

MySQL 相关英文单词

1、database[deɪtəbeɪs] n. 数据库&#xff0c;资料库 2、net[net] 网络 3、start[stɑ:t] vt. 开始&#xff1b;启动 4、stop[stɒp] vi. 停止&#xff1b;中止&#xff1b;n. 停止&#xff1b;车站 5、root[ru:t] 根&#xff0c;MySQL 的超级管理员的用户名 6、…

鸿蒙视频播放的实现

文章目录 前言播放效果视频播放的实现总结 一、前言 现在市面上很多应用都跟视频有关&#xff0c;那么在鸿蒙系统上怎么来播放视频呢&#xff0c;今天就讲解视频播放控件&#xff0c;让你也能快速地进行视频播放功能开发。 最后呢&#xff0c;我会提供一个鸿蒙中涉及的主要…

2024年华为OD机试真题-任务处理-Java-OD统一考试(C卷)

题目描述: 在某个项目中有多个任务(用 tasks 数组表示)需要您进行处理,其中 tasks[i] = [si, ei],你可以在 si <= day <= ei 中的任意一天处理该任务。请返回你可以处理的最大任务数。 注:一天可以完成一个任务的处理。 输入描述: 第一行为任务数量 n,1 <= n …

二刷代码随想录——动态规划day47

文章目录 前言动态规知识点 动规五部曲一、198. 打家劫舍二、213. 打家劫舍 II三、337. 打家劫舍 III总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划二刷完卡子哥的刷题计划&#xff0c;加油&#xff01; 二刷决定精刷了&#xff0c;于是参加了卡子…

元函数与运行期(普通)函数的区别,为什么要用元函数?

看以下面代码你就知道了&#xff1a; /// <summary> /// 求整数所对应的二进制表示中1的个数 /// </summary> /// <typeparam name"Input"></typeparam> /// 创建时间&#xff1a;2024-01-24 抄自&#xff1a;《动手打造深度学习框架》 21…

深度学习模型部署(十)模型部署配套工具二

上篇blog讲了trtexec和onnx_graphsurgeon两个工具&#xff0c;一个用于将onnx转化为trt模型&#xff0c;另一个用于对onnx模型进行修改。这篇blog讲polygraphy和nsight systems&#xff0c;前者用于进行模型优化以及结果验证&#xff0c;后者用于性能分析。 polygraph polygra…

️ 亲身体验!探索工业界最前沿的安全帽数据集,你的工作安全靠它保驾护航!

一、SHWD安全帽佩戴检测数据集&#xff08;Safety helmet (hardhat) wearing detect dataset&#xff09; 介绍 SHWD 提供了用于安全头盔佩戴和人头检测的数据集。它包括7581张图像&#xff0c;其中9044个人体安全头盔佩戴对象&#xff08;正面&#xff09;和111514个正常头部…

渐开线花键不是齿轮?

在和一位小伙伴交流时&#xff0c;他认为齿轮和花键不一样&#xff0c;那花键是不是齿轮呢&#xff1f;老师傅们可以绕开了&#xff0c;我觉得对于一些平时接刚刚接触齿轮&#xff0c;或者很少接触的朋友来说&#xff0c;还是有必要聊一聊这个话题。 首先这个问题并不严谨&…

DeePhage:预测噬菌体的生活方式

GitHub - shufangwu/DeePhage: A tool for distinguish temperate phage-derived and virulent phage-derived sequence in metavirome data using deep learning 安装 conda create -n deephage conda activate deephage pip install numpy pip install h5py pip install ten…

Spring Security入门教程:利用Spring Security实现安全控制

在现今这个数码大展拳脚的时代&#xff0c;安全问题无疑是咱们这些搞软件开发的人需要谨慎应对的一块烫手山芋&#xff0c;无论是那些大型企业应用&#xff0c;还是那种小打小闹的个人项目&#xff0c;对我们宝贵的数据和服务的保护都显得尤为关键。 试想一下&#xff0c;若是…

Java代码基础算法练习-求数据序列的最大值及最小值---2024.3.15

题目 任务描述&#xff1a;输入n个整数&#xff0c;求n个整数的最大值及最小值&#xff0c;并输出相应的位置序号。&#xff08;注&#xff1a;n<10&#xff0c; 位置序号从1开始计算&#xff0c;若存在多个相同值的情形&#xff0c;则输出第1个值的序号&#xff09; 任务要…

深度学习训练结果记录---日志文件写入logger 【简洁明了】

目录 介绍举例1.引入头文件2.封装函数3.在程序入口调用4. 记录训练过程 介绍 在进行深度学习模型训练的过程中&#xff0c;一般会设置log日志&#xff0c;将训练过程的中间结果以及最终结果写入&#xff0c;方便再次打开的时候进行查看。 logging的一些函数这里就不作介绍&…

【Realsense】解决Ubuntu 20.04安装Realsense SDK后realsense-ros报错的问题

最近在Ubuntu 20.04上安装了Realsense SDK&#xff08;版本为2.54.2&#xff09;&#xff0c;但在尝试编译安装realsense-ros&#xff0c;按照Intel官方安装教程执行catkin_clean&#xff08;或者catkin_make&#xff09;&#xff0c;遇到了如下报错&#xff1a; CMake Error …

PTA题解 --- 求整数段和(C语言)

今天是PTA题库解法讲解的第二天&#xff0c;接下来讲解求整数段和&#xff0c;题目如下&#xff1a; 为了解决这个问题&#xff0c;你可以遵循以下的思路&#xff1a; 1. 读取输入的两个整数A和B。 2. 使用一个for循环&#xff0c;从A遍历到B。 3. 在循环中&#xff0c;打印当…

试卷上的水印如何去除?分享3种常用的方法!

在日常生活和工作中&#xff0c;我们经常会遇到一些带有水印的试卷或文档&#xff0c;这些水印不仅影响了我们的阅读体验&#xff0c;还可能对我们的工作和学习产生一定的困扰。那么&#xff0c;如何有效去除试卷上的水印呢&#xff1f;今天&#xff0c;就让我们一起来探讨这个…

渗透测试实战思路分析

免责声明&#xff1a;文章来源真实渗透测试&#xff0c;已获得授权&#xff0c;且关键信息已经打码处理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人…

【LeetCode周赛】第388场周赛

目录 100233. 重新分装苹果 简单100247. 幸福值最大化的选择方案 中等100251. 数组中的最短非公共子字符串 中等100216. K 个不相交子数组的最大能量值 困难 100233. 重新分装苹果 简单 100233. 重新分装苹果 分析&#xff1a; 排序贪心 代码&#xff1a; class Solution {…

Liunx下安装Redis(详细安装)

1、创建一个文件目录 mkdir /opt/redis2、进入安装目录 cd /opt/redis3、下载redis默认安装包 默认是3.0版本的 wget http://download.redis.io/releases/redis4、进行解压 tar -xzvf redis-3.0.7.tar.gz5、进入解压好的文件夹目录 cd redis-3.0.7 6、将redis重新安装到 …