SpringBoot学习

  1. SpringBoot注解annotation总结
  2. spring的事务
  3. Spring数据访问
  4. 12 spring的jdbc异常抽象
  5. JPA
  6. Mybatis
  7. 分库分表的中间件
  8. SpringApplicationContext
  9. SringMV的请求处理流程
  10. 如何定义Controller?和Controller中的一些方法?
  11. SprigMVC中的视图解析机制
  12. 45 SpringMVC中的视图解析机制(下)
  13. SpringMVC中的常用视图
  14. 48 静态资源和缓存
  15. Spring MVC的异常解析
  16. 在Spring MVC 如何使用/实现 拦截器
  17. 52 课程答疑
  18. 通过 RestTemplate 访问 Web 资源
  19. RestTemplate 的高阶用法
  20. 简单定义RestTemplate
  21. 通过WebClient 访问Web 资源
  22. 设计好的 RESTful Web Service
  23. 什么是 HATEOAS
  24. 使用 Spring Data REST 实现简单的超媒体服务
  25. 63 分布式环境中,如何解决 Session 的问题
  26. 64 使用 WebFlux 代替 Spring MVC
  27. 重新认识 Spring Boot 的组成部分
  28. 了解自动配置的实现原理
  29. 动手实现自己的自动配置
  30. Spring Boot 提供的错误分析机制
  31. 70 如何在低版本 Spring 中快速实现类似自动配置的功能
  32. 71 了解起步依赖及其原理
  33. 72 定制自己的起步依赖
  34. 73 深挖 Spring Boot 的配置加载机制
  35. 74 理解配置背后的 PropertySource 抽象
  36. 79 如果定制Web容器的运行参数
  37. 82 编写命令行运行的程序
  38. 83 了解可执行 Jar包背后的秘密
  39. 84 如何将 SpringBoot应用,打包成Docker镜像文件

总体需要学习掌握哪些?

怎么用?

什么场景使用?

特性是什么?

有没有什么注意点?

原理是什么?

  1. sprint boot 如何快速开始一个任务?

1.1. 看一下pom的内容
需要有自己这个moudle的名字,group和artifactid
有父类依赖的,需要有parent,没有的则不需要
默认的pom至少有出去开头部分,至少有两块
dependencies –用于引入依赖
和 build.plugins –用于编包
spring-boot-maven-plugin –这个plugin可以做什么?

1.2. 不使用自带的parent dependency,应该如何写?

  1. 如何操作数据?

2.1. 如何配置一个数据源?

2.2. 如果需要多个数据源,应该如何配置?

2.3. 如何使用数据库连接池?好处是什么?

2.4. spring 中如何操作数据库的事务?

2.5. 怎么判断回滚?
在transaction注解中可以设置当遇到指定异常类时才回滚


SpringBoot注解annotation总结

// 用在class上,
// 意味着,这个class可以用于spring boot的web request请求
@RestController

// 用在method上
// 意味着,@RequestMapping(“xxx”) 这个后面的xxx与标记的方法是对应的,当通过浏览器或者curl访问
这个url时,就是在调用这个方法
@RequestMapping

because @RestController combines @Controller and @ResponseBody, two annotations that results in web requests returning data rather than a view.

@SpringBootApplication

@Configuration

@EnableAutoConfiguration

@ComponentScan

@Component

spring发现该注解后,会自动将有该注解的类变成Bean注册到容器中

有两个很常用的词
configure

注入型的注解

  • @Autowired

Bean是Spring容器中成员的最小组成部分,它也是普通的JavaBean

spring的事务

编程式事务

声明式事务

Spring数据访问

  • jdbc
  • ORM
  • NoSQL
  • Reactive

12 spring的jdbc异常抽象

无论使用何种数据访问方式,都能使用一样的异常

spring是怎么识别那些错误码的?

spring-error-code.xml 将各个数据库常见的数据码和错误定义在该文件中

也支持自定义错误码

JPA

Mybatis

可以使Mybatis更好用的工具

  • Mybatis Generator
    • 一些内嵌子工具
  • Mybatis PageHelper
    • 帮助更好的处理分页的
    • 支持多种数据库
    • 支持多种分页形式

注意:如果既要使用组件自动生成的部分,又需要对它做一个手动的微调

把手动处理的部分和自动生成的部分放在不同的位置

有两套model,mapper,xml,这样下次自动生成时就会仅覆盖自动成的部分,不会干扰手动处理的部分

尽管Mybatis Genertor有合并的逻辑,但是并不是很靠谱。

分库分表的中间件

TDDL、Cobar、MyCAT、Sharding-Shpere

SpringApplicationContext

上下文和切面增强

FooConfig.java:父上下文(parent application context)。
applicationContext.xml:子上下文(child application context)。

FooConfig.java 中定义两个 testBean,分别为 testBeanX(foo) 和 testBeanY(foo)。
applicationContext.xml 定义了一个 testBeanX(bar)。

委托机制:在自己的 context 中找不到 bean,会委托父 context 查找该 bean。


代码解释:
fooContext.getBean(“testBeanX”),在父上下文查找 testBeanX,命中直接返回 testBeanX(foo)。
barContext.getBean(“testBeanX”),在子上下文查找 testBeanX,命中直接返回 testBeanX(bar)。
barContext.getBean(“testBeanY”),在子上下文查找 testBeanY,未命中;委托父上下文查找,命中,返回 testBeanY(foo)。


场景一:
父上下文开启 @EnableAspectJAutoProxy 的支持
子上下文未开启 <aop: aspectj-autoproxy />
切面 fooAspect 在 FooConfig.java 定义(父上下文增强)

输出结果:
testBeanX(foo) 和 testBeanY(foo) 均被增强。
testBeanX(bar) 未被增强。

结论:
在父上下文开启了增强,父的 bean 均被增强,而子的 bean 未被增强。


场景二:
父上下文开启 @EnableAspectJAutoProxy 的支持
子上下文开启 <aop: aspectj-autoproxy />
切面 fooAspect 在 applicationContext.xml 定义(子上下文增加)

输出结果:
testBeanX(foo) 和 testBeanY(foo) 未被增强。
testBeanX(bar) 被增强。

结论:
在子上下文开启增强,父的 bean 未被增强,子的 bean 被增强。


根据场景一和场景二的结果,有结论:“各个 context 相互独立,每个 context 的 aop 增强只对本 context 的 bean 生效”。如果想将切面配置成通用的,对父和子上下文的 bean 均支持增强,则:

  1. 切面 fooAspect 定义在父上下文。
  2. 父上下文和子上下文,均要开启 aop 的增加,即 @EnableAspectJAutoProxy 或<aop: aspectj-autoproxy /> 的支持。

SringMV的请求处理流程

官方文档中有流程解析图

一个请求的大致处理流程

SpringMVC 中的 DispatcherServlet

render,如果是在图形相关内容,可以翻译为,“渲染”

但是在Sring中,目前选择翻译为 “呈现”

idea中发起request请求的插件

handler与mapping

如何寻找合适的handler?

charset 字符集

如何定义Controller?和Controller中的一些方法?

如何定义映射关系

@Controller

@RequestMapping

  • path / method 指定映射路径与http方法
  • params / headers 限定参数映射范围
  • consumes / produces 限定只包含特定Content-Type的请求,/ 限定特定响应格式,需要加特定accept头

一些快捷方式

  • @RestController (@Controller 与 @ResponseBody结合到一起)
  • @GetMapping / @PostMapping / @PutMapping / @DeleteMapping / @PatchMapping

定义处理方法的常用注解

  • @RequestBody 请求正文/ @ResponseBody 响应正文/ @ResponseStatus Http响应的返回码/状态码
  • 下面是定义参数时使用的
  • @PathVariable URL路径中的变量/ @RequestParam 请求的参数/ @RequestHeader 请求的HTTP头
  • 参数中可以接受 HttpEntity / 响应的时候可以为 ResponseEntity

很多参数与返回类型可供选择
具体参考SpringMVC官方网站文档
1.3.3 Handler Methods
Method Arguments
Return Values

@Controller的方法能够接受的参数与返回值
mvc-ann-arguments 方法参数
mvc-ann-return-types 方法返回值

问题:
@Controller与@RestController具体有什么区别?请求上有什么区别?仅仅是@RestController=@Controller+@ResponseBody??

样例:

定义类型转换

  1. 自己实现WebMvcConfigurer
    • SpringBoot 在WebMvcAutoConfiguration(自动配置类)中实现了一个
      • 需要关注 addFormatter这个实现方法,分别获取Converter,GenericConverter,Formatter
    • 添加自定义的Converter
    • 添加自定义的Formatter

定义校验

  • 通过Validator 对绑定结果进行校验
    • Hibernate Validator, 如果有这个就使用
  • 在绑定的对象上添加一个, @Valid 注解
  • BindingResult , 绑定的检查结果,在参数中通过BindingResult传递进去
    • 当不希望SpringMvc处理绑定报错,想自己处理报错内容时使用,参考43节,第6章,more-complex代码

Multipart 上传

  • 配置MultipartResolver
    • SpringBoot 自动配置 MultipartAutoconfiguration
  • 支持类型 multipart/form-data
  • MultipartFile 类型

org.apache.commons-lang3 类库
NumberUtils
StringUtils.split()

处理表单???
- 可以不适用@RequestBody?
- 在@PostMapping中使用consumes=MediaType.application_form_urlencoded_value就可以?
- 这就涉及到@RequestBody与@RequestParam两个注解的区别了
- 可以看一下Spring中这两个注解的注释
- @RequestBody注解,是把整个请求的Body传给参数
- @RequestParam注解,是可以把Body里的对应部分却出来传给参数

postman中,form-data 与x-www-form-urlencoded有啥区别?

SprigMVC中的视图解析机制

视图解析的基本实现

ViewResolver与view接口
用的比较多的ViewResolver

DispatcherServlet 中的视图解析逻辑

针对有ModelAndView返回的解析过程

  • 初始化DispatcherServlet的时候,会初始化所有的ViewResolver,这里它会把Spring上下文当中所有的ViewResolver都加载进来(一般建议不改动,也可以选择只加载对应的某一个viewResolver)
  • 具体请求时,会在doDispatch()中对我们处理的ModelAndView的结果去做processDispatchResult()的时候,会去做从视图名到具体视图的一个解析,当解析出View对象的时候,会做一个视图的呈现。
    • 如果没有返回视图的话,使用RequestToViewNameTrnaslater去做一个尝试解析一个View的名字

转发(forword)与重定向(redirect)的区别

  • forword是服务器的跳转,浏览器是感知不到的,浏览器地址不会改变
  • redirect是客户端的跳转,浏览器地址会改变

45 SpringMVC中的视图解析机制(下)

加了@ResponseBody注解的请求,它的结果是如何输出到Response当中去的

SpringMVC中的常用视图

  • 查看spring官方文档,支持的视图列表
  • 最常用的Jackson-based JSON/XML
  • 支持的模板引擎
    • 最常用的Thymeleaf & FreeMarker

配合MessageConverter, 类型到视图的转换

SpringBoot对Jackson的支持

Spring自动配置相关内容:

  1. 需要支持我们需要配置什么?
  2. SpringBoot帮我们处理了什么?

SpringBoot会自动处理一下在classpath中出现的,需要处理转换的类型,

  • 我们只需要将依赖放到dependency中就可以
  • 当然我们也可以自己做这些Bean的配置

Thymeleaf

java模板引擎

模板如何写?可以参考Thymeleaf官方文档

(我个人理解,使用场景是web页面)

48 静态资源和缓存

有比java程序更合适提供静态资源的工具,比如使用nginx(lzy就是这样)

通常关于用户的静态资源一般放在CDN上,由CDN提供静态资源的访问,当CDN回源时,有一组专门提供静态资源服务的服务器,来提供CND回源所需要的的服务。
也可以有一个CMS的服务,由后台配置该资源的访问,同时静态资源本身还可以添加一些缓存(例如,Squid,Varnish)

另一方面,应用的请求,可以通过Gateway进入到我们的后台系统来,在Gateway中可以设置特定路径的Cache-controller的配置。
也可以通过nginx设置对缓存的控制

Spring MVC的异常解析

核心接口

  • HandlerExceptionResolver

实现类

DispatcherServlet中doService方法中处理异常的代码

如何写异常处理方法

处理方法

  • @ExceptionHandler

添加位置

  • @Controller / @RestController
  • @ControllerAdvice / @RestControllerAdvice (优先级会低于上面的两个)

官方文档 1.3.6 Exceptions

在Spring MVC 如何使用/实现 拦截器

核心接口

  • HandlerInterceptor
    • boolean preHandler() true -> 继续处理,false -> 停止处理
    • void postHandler()
    • void afterCompletion()

针对 @ResponseBody 和 ResponseEntity 的情况

  • 提供了 ResponseBodyAdvice

针对异步请求的接口

  • AsyncHandlerInterceptor
    • void afterConcurrentHandlingStarted()
    • 如果是异步操作,是不会执行postHandler() 和 afterCompletion()方法的

看一下在DispatcherServlet()方法中如何处理的
- doDispath
- applyPreHandle()
- handle
- applyPostHandle()

配置拦截器

常规方法

  • WebMvcConfigurer.addInterceptors()
    • 各种Interceptor 添加的动作

Spring Boot中的配置

  • 创建一个带@Configuraion注解的类,让这个类去实现(implements) WebMvcConfigurer 配置类,然后自己去实现当中的 addInterceptors 的方法
  • 不能带 @EnableWebMvc (想彻底自己控制MVC配合除外)

参考 第6章的springbucket代码

52 课程答疑

  1. 使用MySQL代替H2
  2. 示例中使用到的一些Java语言特性说明
  3. MyBatis Generator 生成代码时的一些说明
  4. MyBatis-Plus 介绍

第7章

通过 RestTemplate 访问 Web 资源

SpringBoot中的RestTemplate

  • SpringBoot 中没有自动配置 RestTemplate
  • SpringBoot 提供了 RestTemplateBuilder
    • RestTemplateBuilder.build()

RestTemplate 中常用的方法

它提供了与http 的Method 对应的方法

GET请求

  • getForObject() / getForEntity()

Post请求

  • postForObject() / postForEntity()

Put请求

  • put()

Delete 请求

  • delete()

提供了构造URI 的一些 方法

构造URI

  • UriComponentsBuilder
    构造相对于当前请求的URI
  • ServletUriComponentsBuilder
    构造指向Controller 的 URI
  • MvcUriComponentsBuilder

RestTemplate 的高阶用法

传递 Http Header

  • RestTemplate.exchange() + RequestEntity / ResponseEntity

类型转换

  • @JsonComponent + JsonSerializer / JsonDeserializer

解析泛型类型

  • RestTemplate.exchange() + ParameterizedTypeReference
  • ParameterizedTypeReference
    • 帮助定义泛型的类型

简单定义RestTemplate

RestTemplate 支持的HTTP 库

通用接口

  • clientHttpRequestFactory
    默认实现 (默认是JDK自带的)
  • SimpleClientHttpRequestFactory

优化底层请求策略

连接管理

  • PoolingHttpClientConnectionManager
  • KeepAlive 策略
    超时设置
  • connectTimeout / readTimeout

SSL校验

  • 证书检查策略

apache HttpComponents 提供了默认的KeepAlived 策略,同时,它的官网上提供了一个定制方式。

通过WebClient 访问Web 资源

通过 Reactive 的方式访问HTTP 资源

了解 WebClient

  • 一个以 Reactive 方式处理 Http 请求的非阻塞式的客户端

支持的底层 HTTP 库

  • Reactive Netty - ReactorClientHttpConnector (使用的更多)
  • Jetty ReactiveStream HttpClient - JettyClientHttpConnector

WebClient 的基本使用

创建 WebClient

在Spring Boot 中没有自动配置,需要自己创建

  • WebClient.create()
  • WebClient.buidler()

发起请求

  • get() / post() / put() / delete() / patch()

获取结果

  • retrieve() / exchange()

处理 HTTP Status

  • onStatus()

响应正文

  • bodyToMono() / bodyToFlux()

个人理解,是一种处理 http 请求的客户端,所以需要有 创建连接,发起请求,获取请求结果,处理请求结果和状态 的过程

特色是使用 Reactive 的方式处理。

第7章样例中,main方法中没有启用web,为什么?

注意:
Reactive ,我们不能根据代码的书写顺序来决定它的执行顺序

使用场景

  1. 如果使用 WebFlux,那么请求处理过程中需要访问其他 http 服务时,就可以使用 WebClient。
  2. 在通用一点,如果你希望通过异步线程来访问 HTTP 服务时,,就可以考虑使用 WebClient。Reactor 可以简化你的多线程代码

关于 Spring 中各种 xxxTemplate 的优点

Spring 中的 xxxTempalte 都是对一些常见的操作做了统一的封装,屏蔽了底层的各种实现差异(即,底层依赖上可以更换各种支持的库,上层操作基本不用变),对上提供统一的操作。
RestTemplate 封装了各种 http 客户端,底层可以使用 JDK 内置的 HTTP 操作,也可以使用 HttpClient (apache 的 HTTPComponent 的 http 客户端),和 OKHttp,同时还提供了与 Spring MVC 一致的格式转换能力,这是单纯的 http 客户端无法比拟的能力。

设计好的 RESTful Web Service

什么是 REST?

Roy Thomas Fielding 博士的论文:
https://www.infoq.cn/article/web-based-apps-archit-design

不全面的理解:RESTful 是一种风格,但也可以暂时把它理解为一种 http uri 的表现形式。

Richardson 成熟度模型 - Glory of REST

如何实现 RESTful Web Service

  • 识别资源
  • 选择合适的资源粒度
  • 设计 URI
  • 选择合适的 HTTP 方法和返回码
  • 设计资源的表述 (JSON / XML / …)

识别资源

  • 找到领域名词
    • 能用 CRUD 操作的名词 ???
  • 将资源组合为集合(即集合资源) ,如,将所有coffee 放到一起成为 coffee 的集合,订单放到一起成为订单集合
  • 将资源合并为复合资源, 如,将 coffee + order 一起返回,即组成了一个复合资源
  • 计算或处理函数, 例如:从上海到北京的距离,是一种计算,没有领域名词,但也可以处理成为一种资源

设计资源的粒度

站在服务端的角度,要考虑

  • 网络效率, 在一次网络交互传输多少?
  • 表述的多少???
  • 客户端的易用程度

站在客户端的角度,要考虑

  • 可缓存性
  • 修改频率
  • 可变性

如何构建更好的 URI

  • 使用域及子域对资源进行合理的分组或者划分
  • 在 URI 的路径部分,使用斜杠分隔符(/)来表示资源之间的层次关系
  • 在 URI 的路径部分,使用逗号(,)和分号(;)来表示非层次元素 (不同的库,使用的分隔符不一定一致,,和; 用的并不是很多)???
  • 使用连字符(-)和下划线(_)来改善长路径中名称的可读性
  • 在 URI 的查询部分,使用“与”符号(&)来分隔参数
  • 在 URI 中避免出现文件扩展名(如,.php, .aspx 和 .jsp)

认识 HTTP 的方法

  • 安全,它不会改变我们资源中的各种内容
  • 幂等,不管请求多少次结果都是一样的

get
post
delete
put
head
options
trace

认识 HTTP 状态码

200
201
202
301
303
304
307
400
401
403
404
410
500
503

具体可以参考 https://www.runoob.com/http/http-status-codes.html

选择合适的表述

  • JSON
    • 各种子类
  • XML
  • HTML
  • ProtoBuf

什么是 HATEOAS

概念

与传统的服务契约的区别

使用 Spring Data REST 实现简单的超媒体服务

认识 HAL

全称:Hypertext Application Language

  • 基于 JSON 的扩展
  • HAL 是一种简单的格式,为 API 中的资源提供简单一致的链接

HAL 帮助我们定义了一个模型,模型中包含下面几部分

  • 链接,我们所有的这种链接资源
  • 内嵌资源, embedded 的 resource
  • 状态

Spring Data REST

Spring Boot 依赖

  • spring-boot-starter-data-rest

Spring Boot 做了自动配置

常用注解和类

  • @RepositoryRestResource ,使用了该接口时,当我们有了一个 Repository ,那么Spring Data REST 会自动将该 Repository 变成一个 RESTful 的接口,把它变成一个资源
  • Resource
  • PageResource

63 分布式环境中,如何解决 Session 的问题

问题:使用场景是什么?

使用spring session 实现简单的分布式会话管理

使用第三方数据库进行session存储

64 使用 WebFlux 代替 Spring MVC

什么是 WebFlux?

  • 基于 Reactive 技术栈之上的,用于构建 Web 应用程序
  • 基于 Reactive Stream API ,运行在非阻塞服务器(Netty,Jetty,并不是指物理上的服务器,应该是指服务容器)上

为什么会有 WebFlux?

  • 对于非阻塞 Web 应用的需要
  • 函数式编程的需要及普及

关于 WebFlux 的性能

  • 单次请求的耗时并不会很大的改善,改善的是并行操作
  • 仅需要少量固定数量的线程和较少的内存即可实现扩展

Spring MVC v.s. WebFlux ,如何做取舍?

  • 已有 Spring MVC 应用,运行正常,就别改了
  • 依赖了大量阻塞式持久化 API 和网络 API,建议使用 Spring MVC
  • 已经使用了非阻塞技术栈,可以考虑使用 WebFlux
  • 想要使用 Java8 Lambda 结合轻量级函数式框架,可以考虑 WebFlux

问题:哪些是阻塞式持久化?mysql是的?redis,mongodb不是?

重新认识 Spring Boot 的组成部分

Spring Boot 用于构建应用程序

Spring Boot 不是什么?

  • 不是应用服务器

    这里是指,不是 Tomcat 或 Jetty 这样的东西,在 Spring Boot 的应用程序中,可以囊括 Tomcat/Jetty,或者可以将应用打成 war 包,部署在这些上面

  • 不是 JavaEE 之类的规范

  • 不是代码生成器

  • 不是 Spring Framework 的升级版

    是为了帮助大家更好的使用 Spring Framework 的

Spring Boot 的特性

  • 方便地创建可独立运行的 Spring 应用程序

  • 直接内嵌 Tomcat,Jetty 或者 Undertow

  • 简化了项目的构建配置

    简化了 mvn , gradle 这样的配置

  • 为 Spring 及第三方库提供自动配置

  • 提供生产级特性 ??

    • 例如,监控
  • 无需生成代码或进行 XML 配置

    • 可直接使用 Java Configuration 等

Spring Boot 的四大特性

  • 自动配置 - Auto Configuration
  • 起步依赖 - Starter Dependency
  • 命令行界面 - Spring Boot CLI
  • Actuator

了解自动配置的实现原理

自动配置

  • 基于添加的 JAR 依赖(在 ClassPath 中出现的类,application.properties,等),自动对 Spring Boot 应用程序进行配置
  • 在 spring-boot-autoconfiguration jar包中

开启自动配置

  • @EnableAutoConfiguration

    • exclude=Class<?>[]

      如果希望排除掉一些自动配置的类,可以使用 exclude 这个属性排除掉指定的类即可

  • @SpringBootApplication

    这个注解中包含了 @EnableAutoConfiguration 注解,同时这个注解也可以使用 @EnableAutoConfiguration 注解的属性,如,exclude

@EnableAutoConfiguration 的实现原理

  • 自动帮忙我们 import AutoConfigurationImportSelector

    这个类会自动帮助我们加载 META-INF/spring.factories 中的特定属性,即 org.springframework.boot.autoconfigure.EnableAutoConfiguration

条件注解

  • @Conditional
  • @ConditionalOnClass
  • @ConditionalOnBean –有指定bean时的操作
  • …OnMissBean –没有指定bean时的操作
  • …OnProperty

观察自动配置的判断结果,了解哪些自动配置生效了,哪些没有生效

  • 在命令行中添加 “–debug” 这个参数

会有完整的输出,哪些匹配了match,哪些没有匹配,哪些exclude 没有加载,哪些是无条件配置的…

问题:初始化加载顺序,注解加载的优先级

动手实现自己的自动配置

自动配置部分是,当我们在主程序中没有提供自己的配置/代码,那么 Spring Boot 会自动加载内置的/已有的自动配置好的配置/代码

当我们希望 Spring 可以自动加载我们的定制功能,我们可以通过实现自己的自动配置集成到Spring Boot中让其来加载

主要工作

  1. 首先我们需要自己写一个 Configuration 的配置类,
  2. 然后在配置类中做我们需要的自动配置,
    • 主要通过一些条件注解来实现的
  3. 与内置的自动配置类一样,在我们的 spring.factories 当中去声明一下我们的自动配置类,让 Spring Boot 可以找到我们的自动配置

这个过程分别对应下面三个部分:

编写 Java Config

  • @Configuration

添加条件

  • @Conditional

定位自动配置

  • META-INF/spring.factories

条件注解大家庭

条件注解

  • @Conditional
  • 所有的条件注解都是基于这个注解,它是 Spring 4 引入的新的注解,(在 Spring 3 中没有@Conditional 这个注解,后面会提交如何在 Spring 3.x版本当中如何实现自动配置)

类条件

  • @ConditionalOnClass 存在指定Class时怎么样
  • @ConditionalOnMissingClass 不存在指定Class时怎么样

属性条件

  • @ConditionalOnProperty

Bean 条件

  • @ConditionalOnBean
  • @ConditionalOnMissingBean
  • @ConditionalOnSingleCandidate

资源条件

  • @ConditionalOnResource

Web 应用条件

  • @ConditionalOnWebApplication
  • @ConditionalOnNotWebApplication

其他条件

  • @ConditionalOnExpression
  • @ConditionalOnJava
  • @ConditionalOnJndi

自动配置的执行顺序

执行顺序

  • @AutoConfigureBefore
  • @AutoConfigureAfter
  • @AutoConfigureOrder

Spring Boot 提供的错误分析机制

在 Spring Boot 官方文档中的 Spring Boot Features 章节 1.1. Startup Failure 中有涉及这部分的内容

FailureAnalysis.class

三个组成部分

  • description 失败的描述
  • action 失败后的动作
  • Throwable cause 失败后的异常

70 如何在低版本 Spring 中快速实现类似自动配置的功能

需求与问题

  1. 核心诉求

    • 现存系统,不打算重构
    • Spring 版本3.x,不打算升级版本和引入 Spring Boot
    • 期望能够在少改代码的前提下实现一些功能增强
  2. 面临问题

    • 3.x 的 Spring 没有条件注解
    • 无法自动定位需要加载的自动配置

核心解决思路

解决加载加载条件判断的问题

条件判断

  • 通过 BeanFactoryPostProcessor 进行判断
    • 在bean定义完成之后,初始化之前,对我们的这些 bean 的定义做一些后置的处理

配置加载

  1. 编写 Java Config 类
    • 使用 @Configuration
  2. 引入配置类
    • 通过 component-scan, 让 component-scan中包含我们写的 Java Config
    • 通过 XML 文件 import,如果原有系统是使用 XML的方式,就通过 XML来加载我们的配置

      如果需要被扫描类所属 package 是带有 @SpringBootApplication 注解类所在 package以下的所有package里面所有的配置,所以即使即使没有通过 component-scan 或者XML做明确定义,也能扫描到我们提供的配置类;

      但是如果提供的配置类与 @SpringBootApplication 所在 package 不是上下级关系,需要增加 component-scan 扩展扫描范围。

Spring 的两个扩展点

  1. BeanPostProcessor
    • 在 Bean 初始化之后,可以针对 Bean 实例进行一些处理

      比如对 bean 做一些增强,处理一些逻辑

  • 在 Bean创建后提供定制逻辑回调
  1. BeanFactoryPostProcessor 接口
    • 针对 Bean 定义
    • 在容器创建 Bean 前获取配置元数据
    • Java Config 中需要定义为 static 方法
      • 如果不定义为 static 那么,spring 在启动的时候会报一个 warning

与 Bean 定制相关的一些内容

  1. 生命周期的Callback

    • 初始化相关, 在 Bean创建之后对它做一些定制(创建之后调用特定的方法),一共有三种方式
      • 实现 InitializingBean 接口
      • 使用 @PostConstruct 注解
      • 用我的Bean 定义的 XML文件里面指定一个 init-method, 或者在我的Bean 注解里面指定 init-method
    • 在 Bean 回收/销毁的时候做一些操作
      • 实现 DisposableBean 接口,spring会调用指定方法
      • 使用 @PreDestroy 注解,当Bean销毁的时候,Spring 会进行一个调用
      • 在Bean定义的XML文件中 配置一个 destroy-method ,指定Bean销毁时调用哪个方法, 或者在Bean的注解中指定
  2. xxxAware 接口 (注入相关内容)

    • ApplicationContextAware, 把整个ApplicationContext 通过接口注入到Bean中,这样在Bean中就可以取得整个 ApplicationContext
    • BeanFactoryAware, 类似 ApplicationContext ???
    • BeanNameAware ,把Bean 的名字注入到实例中

AbstractBeanFactory.java

  • getBean()

AbstractAutowireCapableBeanFactory.java

  • hasDestoryMethod()

    即使我们的 Bean 不是 DisposableBean 或者 AutoCloseableBean,只要有一个Close或者Shutdown的方法,Spring 也会在销毁 Bean 的时候去调用它们

Bean 的一些其他常用操作

判断类是否存在

  • ClassUtils.isPresent(), 用于判断一个类是否存在于ClassPath中

判断 Bean 是否已定义

  • ListableBeanFactory.containsBeanDefinition()
  • ListableBeanFactory.getBeanNamesForType()

注册 Bean 定义

  • 如果是 BeanDefinitionRegistry, 使用 BeanDefinitionRegistry.registerBeanDefinition() 方法注册
    • 注册前要先利用 GenericBeanDefinition 去setBeanClass
  • 如果不是上面的类型,就使用 BeanFactory.registerSingleton() 方法注册,并传入实例


如果使用 Spring4.x以上版本,即Spring支持自动配置后,尽管我们的定义的配置类不在 @SpringBootApplication 默认packge 下,SpringBoot 也会扫描到,
因为在 spring.factories 文件中默认配置了 org.springframework.boot.autoconfigure.EnableAutoConfiguration, SpringBoot 就会找到我们的自动配置类。

71 了解起步依赖及其原理

Spring Boot 的特色功能之一,Starter Dependency

问题,SpringBoot 为什么会提供Starter Dependency 这样的功能,其背后是如何实现的

需要关注的就是 Maven 如何管理依赖

没有 Starter Dependency 之前的时候

  • 传统maven项目,需要我们自己管理依赖问题
  • 要实现一个功能, 需要引入哪些依赖
  • 多个依赖项目之间是否会有兼容问题
    • 当多个版本的包被引入到依赖,Classpath中同package同方法时就可能出现依赖冲突

关于 Maven 依赖管理的一些小技巧

了解你的依赖

  • mvn dependency:tree , 打印工程所有的maven依赖
  • IDEA Maven Helper 插件
  • 使用 duplicate-finder 这个mvn插件来找到可能冲突的类

排除特定依赖

  • exclusion , 在pom.xml中排除指定依赖

统一管理依赖

  • dependencyManagement , 当项目是多module的时候,在parent的pom.xml中统一管理部分依赖
  • Bill of Materials - bom, Maven提供的一种功能,具体去搜一下

SprigBoot 的起步依赖(Starter Dependency)

Starter Dependencies

  • 直接面向功能, 即我们只需要知道自己要什么功能,去SpringBoot 中找到相关功能的Starter Dependency, 然后剩下的该功能有哪些依赖,SpringBoot会帮我们处理这个功能所需要的的所有的相关依赖。
  • 好处,一站获得所有相关依赖,不再复制粘贴

官方的 Starters

从官方文档的 Starters 章节中,我们可以看到它帮我们制作提供了很多功能模块的 starters, 我们需要时,只要做一个指定 starter的引入即可

  • spring-boot-starter-*

问题:引入多个 Starter 依赖,它们里面有重复的依赖,或者不同版本的依赖时,SpringBoot 是如何解决的?

  • Spring Boot 同一个版本中统一处理了所有支持的第三方依赖的版本,所以在使用同版本的starter时,会不出现jar冲突问题。

72 定制自己的起步依赖

主要内容

  1. 自动配置相关模块,即 autoconfigure 模块,包含自动配置代码。 如果你的依赖需要做自动配置,可以在该模块中写下自己的自动配置。

    注: 这个模块不是必须的,

    如果我们的模块不需要自动配置,就不需要 autoconfigure 模块。
    如果我们的模块依赖了 spring-boot-starter ,因为 SpringBoot 的自动配置都集中在 SpringBoot的autoconfigure中,所以也会自动加载SpringBoot的自动配置

  2. starter 模块, 包含指向自动配置模块的依赖及其他相关依赖

命名方式

  • xxx-spring-boot-autoconfigure
  • xxx-spring-boot-starter

    通过加前缀的方式,来与官方的依赖做区分。

一些注意事项

  • 不要使用 spring-boot 作为依赖的前缀,这样容易与 Spring 官方的混淆在一起
  • 不要使用 spring-boot 的配置命名空间, 同上
  • starter 中仅添加必要的依赖,减少最终打出来的包的依赖
  • (非必须)声明对 spring-boot-starter 的依赖
    • 主要是为了加载一些 SpringBoot 的自动配置,如果上层项目引入了 spring-boot-starter 的依赖,也就不需要了,因为大部分SpringBoot项目都会有该依赖

73 深挖 Spring Boot 的配置加载机制

SpringBoot是如何加载配置文件和配置项的?

根据 SpringBoot 的官方文档 24 章节 外置配置的内容

外化配置加载顺序,按照先后出现顺序加载

  1. 开启 DevTools 时,~/.spring-boot-devtools.properties
  2. 测试类上的 @TestPropertySource 注解
  3. @SpringBootTest#properties 属性
  4. 命令行参数 (例如,--server.port=9000) 优先加载
  5. SPRING_APPLICATION_JSON 中的属性
  6. ServletConfig 初始化参数
  7. ServletContext 初始化参数
  8. java:comp/env 中的 JNDI 属性
  9. System.getProperties() java -D 参数指定的属性,在命令行中可以通过 -D 参数来指定一些系统属性
  10. 操作系统环境变量
  11. random.* 涉及到的 RandomValuePropertySource
  12. jar包外部的 application-{profile}.properties 或者 .yml
  13. jar包内部的 application-{profile}.properties 或者 .yml
  14. jar包外部的 appliaction.properties 或者 .yml
  15. jar包内部的 application.properties 或者 .yml
  16. @Configuration 类上的 @PropertySource
  17. SpringApplication.setDefaultProperties() 设置的默认属性

application[-profile].properties 文件的默认位置

  • ./config
  • ./
  • CLASSPATH 中的 /config
  • CLASSPATH 中的 /

通过命令行参数,修改名字或者路径

  • spring.config.name 修改配置文件的名字,比如不叫做application.properties,改为env.properties
  • spring.config.location
  • spring.config.additional-location

Relaxed Binding

什么是Relaxed Binding

命令风格 使用范围 示例
短划线分隔 (优先使用) properties 文件 / YAML文件 / 系统属性 geektime.spring-boot.first-demo
驼峰式 同上 geektime.springBoot.firstDemo
下划线分隔 同上 geektime.spring_boot.first_demo
全大写,下划线分隔 环境变量 GEEKTIME_SPRINGBOOT_FIRSTDEMO

74 理解配置背后的 PropertySource 抽象

添加 PropertySource

  • 使用XML文件的时候,通过 context:property-placeholder 这个标签来声明配置文件 ???
  • PropertySourcesPlaceholderConfigurer Spring 3.1开始使用这个类来处理,为了兼容之前的版本,下面的类也依然存在
    • PropertyPlaceholderConfigurer
  • 如果使用的是 Java Config 配置的形式,那么只需要在Config类上添加下面的注解即可
    • @PropertySource
    • @PropertySources 多个数据源就使用这个

如果是使用 SpringBoot,SpringBoot 提供了 @ConfigurationProperties 注解更方便使用

  • 可以将属性绑定到结构化对象上
  • 支持 Relaxed Binging
  • 支持安全的类型转换
  • @EnableConfigurationProperties

定制 PropertySource

主要步骤

  1. 实现 PropertySource
  2. 将 PropertySource 注册到 Environment中
    1. 实现下面的接口
      • EnvironmentPostProcessor
      • BeanFactoryPostProcessor
    2. 从 Environment 对象中获取 PropertySources
    3. 将自己的 PropertySource 添加到合适的位置

79 如果定制Web容器的运行参数

SpringBoot支持的Web容器

  • spring-boot-starter-tomcat
  • spring-boot-starter-jetty
  • spring-boot-starter-undertow
  • spring-boot-starter-reactor-netty

通过选择不同的 starter-dependency 来实现不同Web容器的替换

修改容器配置

端口

  • server.port
  • server.address

压缩相关内容

  • server.compression.enabled
  • server.compression.min-response-size
  • server.compression.mime-types

Tomcat 特定配置

  • server.tomcat.max-connections=10000
  • server.tomcat.max-http-post-size=2MB
  • server.tomcat.max-swallow-size=2MB
  • server.tomcat.max-threads=200
  • server.tomcat.min-spare-threads=10

其他Web容器,分别在不同的 server.xxx下面

错误处理

  • server.error.path=/error
  • server.error.include-exception=false
  • server.error.include-stacktrace=never
  • server.error.whitelabel.enabled=true

其他

  • server.use-forward-headers
  • server.servlet.session.timeout

通过编程的方式实现容器的配置

  • WebServerFacotoryCustomizer
    • TomcatServletWebServerFactory
    • JettyServletWebServerFactory
    • UndertowServletWebServerFactory
      官方文档也提供了其他的编程方式来处理

82 编写命令行运行的程序

关闭 Web 容器

有以下几种方式

  1. 控制依赖

    • 不添加 Web相关依赖
  2. 通过修改配置的方式

    如果代码中有一些与Web服务接口调用等相关的交互,或者很难完全去除Web相关依赖,可以使用修改配置的方式,关闭Web 容器的支持

    • spring.main.web-application-type=none
  3. 编程的方式

    • SpringApplication
      • setWebApplicationType() 方法,将 webApplicationType 类型设置为none
    • SpringApplicationBuilder
      • Web()
    • 在调用 SpringApplication 的 run() 方法前设置 WebApplicationType

常用工具类

不同的Runner, 功能上类似,在程序启动的时候执行一段代码, 区别在于参数不同

  • ApplicationRunner
    • 参数是 ApplicationArguments
  • CommandLineRunner
    • 参数是 String[]

如果有多个Runner,可以通过添加 @Order 注解,或者实现 Order 接口来指定运行顺序

返回码

  • ExitCodeGenerator

83 了解可执行 Jar包背后的秘密

认识可执行 Jar

其中包含4部分

  • Jar 描述,META-INF/MANIFEST.MF

    manifest 翻译为 程序集清单,装配件清单

  • SpringBoot Loader,org/springframework/boot/loader/

  • 项目内容, BOOT-INF/classes/xxx/yourclass.class

  • 项目依赖, BOOT-INF/lib/dependency1.jar,dependency2.jar…

其中不包含

  • jdk / jre

如何找到程序的入口

jar的启动类

  • MANIFEST.MF
    • Main-Class: org.springframework.boot.loader.JarLauncher

      在这里指定 jar -jar xxx.jar 要运行需要的启动类,因为是jar包,所以是JarLuncher,如果是war包,则换成 WarLauncher

      指定之后,java就会找到这个类运行。那么JarLauncher又是怎样找到SpringBoot的应用程序的呢?通过下面的Start-Class指定的类去执行

项目的主类

  • MANIFEST.MF
    • Start-Class: xxx.yyy.zzz

      这里是我们添加了 @SpringApplication 注解的类的全限定类名

如何创建可直接执行的jar(无需 java -jar来引导执行,直接通过 ./xxx.jar即可执行)

通过plugin,org.springframework.boot.spring-boot-maven-plugin,在属性添加为true的属性

  • 打包后的jar可直接运行,无需java命令,即不需要 java -jar的命令来引导执行,是可以直接再命令行中运行。
  • 可以在 <jar文件的同名文件>.conf 的文件中配置程序需要的参数
配置项 说明 备注
CONF_FOLDER 放置.conf的目录位置 只能放置环境变量中
JAVA_OPTS JVM启动时的参数 比如 JVM的内存Xms,Xmx,和GC
RUN_ARGS 传给程序执行的参数

注:因为是可以直接实行,所以在linux 中也可以放在init.d,或system目录下作为一个服务来操作。

通过less 或者more命令,直接查看这个可独自运行的jar文件,可以知道该文件的开头是一个 SpringBoot生成的执行脚本。如果不是可独自运行的jar,jar文件头没有改可执行脚本。

为什么可以在jar文件头放一个可执行脚本?

利用了shell文件是从上向下读,zip文件是从尾部向前读的特性。

这段内容可以打出来看看,学习一下。

84 如何将 SpringBoot应用,打包成Docker镜像文件

什么是Docker镜像?

  • 镜像是静态的只读模板
  • 镜像中包含构建Docker容器的指令
  • 镜像是分层的 (利用了一个叫做,联合文件系统 的技术来处理)
  • 通过Dockerfile 来创建镜像

Dockerfile中常用的指令

可以通过官方文档了解

通过Maven构建 Docker 镜像

准备工作

  • 提供一个Dockerfile
  • 配置 dockerfile-maven-plugin 插件
    • 到官网查看一下这个插件如何使用

构建执行

  • mvn package
  • mvn dockerfile:build

检查结果

  • docker images
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 ligongzhao
  • 访问人数:867 | 浏览次数:1098

请我喝杯咖啡吧~

支付宝
微信