SpringBoot学习

总体需要学习掌握哪些?

怎么用?

什么场景使用?

特性是什么?

有没有什么注意点?

原理是什么?

  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
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信