总体需要学习掌握哪些?
怎么用?
什么场景使用?
特性是什么?
有没有什么注意点?
原理是什么?
- sprint boot 如何快速开始一个任务?
1.1. 看一下pom的内容
需要有自己这个moudle的名字,group和artifactid
有父类依赖的,需要有parent,没有的则不需要
默认的pom至少有出去开头部分,至少有两块
dependencies –用于引入依赖
和 build.plugins –用于编包
spring-boot-maven-plugin –这个plugin可以做什么?
1.2. 不使用自带的parent dependency,应该如何写?
- 如何操作数据?
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 均支持增强,则:
- 切面 fooAspect 定义在父上下文。
- 父上下文和子上下文,均要开启 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??
样例:
定义类型转换
- 自己实现WebMvcConfigurer
- SpringBoot 在WebMvcAutoConfiguration(自动配置类)中实现了一个
- 需要关注 addFormatter这个实现方法,分别获取Converter,GenericConverter,Formatter
- 添加自定义的Converter
- 添加自定义的Formatter
- SpringBoot 在WebMvcAutoConfiguration(自动配置类)中实现了一个
定义校验
- 通过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自动配置相关内容:
- 需要支持我们需要配置什么?
- 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 课程答疑
- 使用MySQL代替H2
- 示例中使用到的一些Java语言特性说明
- MyBatis Generator 生成代码时的一些说明
- 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 ,我们不能根据代码的书写顺序来决定它的执行顺序
使用场景
- 如果使用 WebFlux,那么请求处理过程中需要访问其他 http 服务时,就可以使用 WebClient。
- 在通用一点,如果你希望通过异步线程来访问 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中让其来加载
主要工作
- 首先我们需要自己写一个 Configuration 的配置类,
- 然后在配置类中做我们需要的自动配置,
- 主要通过一些条件注解来实现的
- 与内置的自动配置类一样,在我们的 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 中快速实现类似自动配置的功能
需求与问题
核心诉求
- 现存系统,不打算重构
- Spring 版本3.x,不打算升级版本和引入 Spring Boot
- 期望能够在少改代码的前提下实现一些功能增强
面临问题
- 3.x 的 Spring 没有条件注解
- 无法自动定位需要加载的自动配置
核心解决思路
解决加载加载条件判断的问题
条件判断
- 通过 BeanFactoryPostProcessor 进行判断
- 在bean定义完成之后,初始化之前,对我们的这些 bean 的定义做一些后置的处理
配置加载
- 编写 Java Config 类
- 使用 @Configuration
- 引入配置类
- 通过 component-scan, 让 component-scan中包含我们写的 Java Config
- 通过 XML 文件 import,如果原有系统是使用 XML的方式,就通过 XML来加载我们的配置
如果需要被扫描类所属 package 是带有 @SpringBootApplication 注解类所在 package以下的所有package里面所有的配置,所以即使即使没有通过 component-scan 或者XML做明确定义,也能扫描到我们提供的配置类;
但是如果提供的配置类与 @SpringBootApplication 所在 package 不是上下级关系,需要增加 component-scan 扩展扫描范围。
Spring 的两个扩展点
- BeanPostProcessor
在 Bean 初始化之后,可以针对 Bean 实例进行一些处理
比如对 bean 做一些增强,处理一些逻辑
- 在 Bean创建后提供定制逻辑回调
- BeanFactoryPostProcessor 接口
- 针对 Bean 定义
- 在容器创建 Bean 前获取配置元数据
- Java Config 中需要定义为 static 方法
- 如果不定义为 static 那么,spring 在启动的时候会报一个 warning
与 Bean 定制相关的一些内容
生命周期的Callback
- 初始化相关, 在 Bean创建之后对它做一些定制(创建之后调用特定的方法),一共有三种方式
- 实现 InitializingBean 接口
- 使用 @PostConstruct 注解
- 用我的Bean 定义的 XML文件里面指定一个 init-method, 或者在我的Bean 注解里面指定 init-method
- 在 Bean 回收/销毁的时候做一些操作
- 实现 DisposableBean 接口,spring会调用指定方法
- 使用 @PreDestroy 注解,当Bean销毁的时候,Spring 会进行一个调用
- 在Bean定义的XML文件中 配置一个 destroy-method ,指定Bean销毁时调用哪个方法, 或者在Bean的注解中指定
- 初始化相关, 在 Bean创建之后对它做一些定制(创建之后调用特定的方法),一共有三种方式
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 定制自己的起步依赖
主要内容
- 自动配置相关模块,即 autoconfigure 模块,包含自动配置代码。 如果你的依赖需要做自动配置,可以在该模块中写下自己的自动配置。
注: 这个模块不是必须的,
如果我们的模块不需要自动配置,就不需要 autoconfigure 模块。
如果我们的模块依赖了 spring-boot-starter ,因为 SpringBoot 的自动配置都集中在 SpringBoot的autoconfigure中,所以也会自动加载SpringBoot的自动配置 - 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 章节 外置配置的内容
外化配置加载顺序,按照先后出现顺序加载
- 开启 DevTools 时,~/.spring-boot-devtools.properties
- 测试类上的 @TestPropertySource 注解
- @SpringBootTest#properties 属性
命令行参数 (例如,--server.port=9000)
优先加载- SPRING_APPLICATION_JSON 中的属性
- ServletConfig 初始化参数
- ServletContext 初始化参数
- java:comp/env 中的 JNDI 属性
System.getProperties()
java -D 参数指定的属性,在命令行中可以通过 -D 参数来指定一些系统属性操作系统环境变量
- random.* 涉及到的 RandomValuePropertySource
- jar包外部的 application-{profile}.properties 或者 .yml
- jar包内部的 application-{profile}.properties 或者 .yml
- jar包外部的 appliaction.properties 或者 .yml
- jar包内部的 application.properties 或者 .yml
- @Configuration 类上的 @PropertySource
- 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
主要步骤
- 实现 PropertySource
- 将 PropertySource 注册到 Environment中
- 实现下面的接口
- EnvironmentPostProcessor
- BeanFactoryPostProcessor
- 从 Environment 对象中获取 PropertySources
- 将自己的 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 容器
有以下几种方式
控制依赖
- 不添加 Web相关依赖
通过修改配置的方式
如果代码中有一些与Web服务接口调用等相关的交互,或者很难完全去除Web相关依赖,可以使用修改配置的方式,关闭Web 容器的支持
- spring.main.web-application-type=none
编程的方式
- SpringApplication
- setWebApplicationType() 方法,将
webApplicationType
类型设置为none
- setWebApplicationType() 方法,将
- SpringApplicationBuilder
- Web()
- 在调用 SpringApplication 的 run() 方法前设置 WebApplicationType
- SpringApplication
常用工具类
不同的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指定的类去执行
- Main-Class: org.springframework.boot.loader.JarLauncher
项目的主类
- MANIFEST.MF
Start-Class: xxx.yyy.zzz
这里是我们添加了
@SpringApplication
注解的类的全限定类名
如何创建可直接执行的jar(无需 java -jar来引导执行,直接通过 ./xxx.jar即可执行)
通过plugin,org.springframework.boot.spring-boot-maven-plugin,在
- 打包后的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