Spring体系
2024-09-24 01:40:33 39 举报
AI智能生成
2024年4月24日01:34:36:更新,完善内容,补充了JavaWeb相关 2024年6月22日02:00:58:SpringBoot配置文件相关完善、Spring 事务失效完善、Mybatis内容完善、Spring Cloud分布式事务完善 2024年7月9日17:56:11:整理了SpringBoot里的冗余内容 2024年8月15日22:35:56:\n 进一步基于官方文档完善,SpringBoot倒没怎么补充,因为确实没啥东西,也就配置文件、自动配置那块要看看,其他都是spring、mvc那边的东西。 2024年9月3日17:04:27:①Spring结构调整、循环依赖内容完善;②Spring MVC内容完善 ③样式风格调整 2024年9月24日01:39:05:①完善Spring生命周期的细节;②完善@Autowired注解在不同位置注入的作用机制
作者其他创作
大纲/内容
Mybatis Plus
前言:
单纯是做一下笔记,其实也没必要看,要用到什么直接去官方文档看就行了。
是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
用过的
MybatisX代码生成
IDEA集成插件直接使用
主键自动生成(√)
从3.3.0版本开始,默认使用雪花算法结合不含中划线的UUID作为ID生成方式
策略
nextId
ASSIGN_ID
nextUUID
ASSIGN_UUID
属性填充(√)
用于在插入或更新数据时自动填充某些字段,如创建时间、更新时间等
使用步骤
1.实体类中,使用 @TableField 注解来标记哪些字段需要自动填充
2.创建一个类来实现 MetaObjectHandler 接口,并重写 insertFill 和 updateFill 方法
3.确保你的 MyMetaObjectHandler 类被 Spring 管理,可以通过 @Component 或 @Bean 注解来实现。
SQL打印(有默认开启)
多数据源(√)
这个文档要收费,所以不在这里补充。有需要免费分享的可以尝试扒一下我的社交账号私信0.0
分页插件(√)
①添加分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
}
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
}
②在自定义方法中使用
声明方法的使用返回值用IPage去接
IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
通过继承Page类或者声明IPage接口进行自定义分页使用
MyPage selectPageVo(MyPage page);
IPage入参,返回List
List<UserVo> selectPageVo(IPage<UserVo> page, Integer state);
逻辑删除(√)
条件构造器(√)
用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
QueryWrapper
UpdateWrapper
LambdaQueryWrapper
LambdaUpdateWrapper
没用过但是很期待的功能
自动维护DDL(向Hibernate靠拢?)
3.5.3+版本
批量操作
一次性执行多个数据库操作,从而减少与数据库的交互次数,提高数据处理的效率和性能
数据插入(Insert)
数据更新(Update)
数据删除(Delete)
Spring Cloud
服务注册与发现
Nacos
配置中心
Nacos
远程服务调用与负载均衡
OpenFeign
使用方式
①启动类添加@EnableFeignClients注解
②创建一个接口并添加@FeignClient("val")注解
Val的可选值
在集成了注册与发现中心的情况下,可以填写服务名,这会内置一个负载均衡效果
url
LoadBalance
服务网关
Spring Cloud GateWay
能干嘛
反向代理
鉴权
流量控制
熔断
日志监控
三大核心
Route
Predicate
就是匹配条件
Filter
能干嘛
请求鉴权
异常处理
。。。。
类型
全局默认过滤器GlobalFilters
直接实现接口即可
单一内置过滤器GatewayFilters
自定义过滤器
服务熔断降级
Sentinel
分布式事务
分布式事务的定义?
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器「分别位于不同的分布式系统的不同节点之上」。
一个大的操作由N多的小的操作共同完成。而这些小的操作又分布在不同的服务上。针对于这些操作,「要么全部成功执行,要么全部不执行」。
一个大的操作由N多的小的操作共同完成。而这些小的操作又分布在不同的服务上。针对于这些操作,「要么全部成功执行,要么全部不执行」。
分布式理论
CAP(强一致性)
简而言之,必须在一致性与可用性之间做出选择
BASE(最终一致性)
简而言之,我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性
分布式事务涉及到哪些角色?
TC(事务协调者)
seata服务端
TM(事务管理者)
seata客户端,就是sdk
RM(资源管理者)
database
XA协议是什么?
类似JDBC,是一套接口规范,各个数据库厂商基于这套接口规范进行了实现,通用了语言级的API。2PC和3PC衍生于XA协议
分布式事务的解决方案?
刚性兑付(强一致性)
2PC(二段提交)
详情
分为准备阶段和提交阶段。准备阶段:TC向所有参与者发送准备请求,询问是否可以准备提交事务。每个参与者要么准备好并锁住资源,要么拒绝准备。如果所有参与者都准备好了,TC向所有参与者发送提交请求,否则发送回滚请求。
优点
无侵入,好用
缺点
阻塞同步,性能差,不适合长事务
TC单点故障风险
3PC(三段提交)
详情
3PC 是 2PC 的改进版本,增加了一个中间阶段来减少TC和参与者之间的等待时间。准备阶段:与 2PC 类似,TC询问所有参与者是否可以准备提交事务。预提交阶段:如果所有参与者都准备好了,TC者向所有参与者发送预提交请求,参与者预提交并继续等待最终指令。提交阶段:TC发送提交请求,所有参与者正式提交事务。
优点
解决单点问题,以及通过超时机制减少阻塞
缺点
协议更复杂,实施成本较高,没有从根本上解决性能和数据一致性的问题
Seta-AT(√)
详情
对于3PC的落地实现
柔性事务(最终一致性)
TCC三段补偿
详情
2PC改良版,包含尝试(Try)、确认(Confirm)和取消(Cancel)三个步骤。Try 阶段:资源预留或锁定,检查并锁定需要的资源。Confirm 阶段:正式提交,执行实际的事务操作。Cancel 阶段:取消操作,释放预留的资源或撤销尝试阶段的操作。
优点
灵活性高,可以针对具体业务场景优化。TCC 完全不依赖底层数据库,能够实现跨数据库、跨应用资源管理,可以提供给业务方更细粒度的控制。
缺点
设计实现复杂,侵入性非常强,需要处理好资源预留和取消的逻辑。
本地消息表
详情
每一个服务的db下额外建立一张业务无关的表,基于对这张表记录的操作实现事务操作
优点
无锁,快
缺点
藕和度大,不可复用
独立消息微服务+消息队列+分布式定时任务轮询(√)
详情
将两个事物之间通过消息中间件进行异步解耦,类似本地消息表,但是把表放到MQ里了。
流程
1.主动者发送"待发送"消息给消息微服务
2.消息微服务收到消息,进行持久化数据库操作并返回(返回就是调主动者提供的一个回查接口,告诉主动者我持久化好消息了)
3.主动者收到消息微服务的返回后,执行本地的业务,持久化到数据库
4.主动者执行完业务,向消息微服务发送通知
5.消息微服务收到通知,将数据库里的待发送状态改为已发送
6.以上操作执行都ok,接着消息微服务把消息发到MQ
7.被动者监听MQ获取消息内容
8.被动者拿到消息内容,进行业务处理
9.被动者处理完业务,通知消息微服务把数据库里的消息记录改为已完成或者直接删除
PS:细节处理
1.保证消息投递100%,定时任务轮询待发送状态的消息,通过主动者提供的状态查询接口确认消息状态,如果业务执行成功,向MQ发送消息并发消息状态改为已发送,如果业务失败,删除消息
2.保证消息消费100%,,定时任务轮询已发送状态的消息,已过期的消息重新推到MQ,,被动者在保证幂等的情况下重新执行业务,,被动者执行完业务通知消息服务对消息进行状态修改为已完成或者删除消息
2.消息微服务收到消息,进行持久化数据库操作并返回(返回就是调主动者提供的一个回查接口,告诉主动者我持久化好消息了)
3.主动者收到消息微服务的返回后,执行本地的业务,持久化到数据库
4.主动者执行完业务,向消息微服务发送通知
5.消息微服务收到通知,将数据库里的待发送状态改为已发送
6.以上操作执行都ok,接着消息微服务把消息发到MQ
7.被动者监听MQ获取消息内容
8.被动者拿到消息内容,进行业务处理
9.被动者处理完业务,通知消息微服务把数据库里的消息记录改为已完成或者直接删除
PS:细节处理
1.保证消息投递100%,定时任务轮询待发送状态的消息,通过主动者提供的状态查询接口确认消息状态,如果业务执行成功,向MQ发送消息并发消息状态改为已发送,如果业务失败,删除消息
2.保证消息消费100%,,定时任务轮询已发送状态的消息,已过期的消息重新推到MQ,,被动者在保证幂等的情况下重新执行业务,,被动者执行完业务通知消息服务对消息进行状态修改为已完成或者删除消息
怎么用
@Translation+先处理本地,然后在通过MQ通知
分布式定时任务定时轮询消息服务,逼着上游向下游进行消息投递
优点
解耦,降低业务系统之间的耦合度
消息服务更加灵活,伸缩性强,独立维护,独立部署
相比本地消息表,降低开发成本,消息服务共用
缺点
主动者必须实现消息状态回查接口
需要分布式定时器实现两个任务
轮询待发送和已发送两个过期时间的状态进行重试操作
主动者通知被动者需要发两次消息,过于复杂
最大努力通知
详情
适合最终一致性要求较低的业务,比如短信通知
优点
确定
Seata
模式
AT模式
3PC的实现
AT 模式:基于 Seata 自身实现的两阶段提交,主要针对数据库操作。
TCC模式
2PC改良版,不好用
Saga模式
长事务解决方案,不好用,你画内b状态机去吧
XA模式
XA 模式使用起来与 AT 模式基本一致,用法上的唯一区别在于数据源代理的替换:使用 DataSourceProxyXA 来替代 DataSourceProxy
XA 模式:基于标准的 XA 协议,适用于跨多种资源的分布式事务。
服务链路追踪
Jaeger(我用过的)
Micrometer+ZipKin
SkyWalking
其他?
SpringBoot+MDC实现本地微服务链路追踪,结合feign拦截器实现分布式链路追踪
云原生
Spring Cloud Kubernetes
Spring Boot(核心思想:约定大于配置)
为什么?
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。
配置文件相关
SpringBoot配置加载顺序(后加载的配置会覆盖先加载的配置)
默认配置
位于spring-boot.jar内部的application.properties或application.yml
应用配置文件
config目录下的application.properties或application.yml文件(file:./config/)
当前目录下的application.properties或application.yml文件(file:./)
类路径根目录下的application.properties或application.yml文件(classpath:/config/)
类路径根目录下的application.properties或application.yml文件(classpath:/)
操作系统环境变量
命令行参数
多环境配置
为了支持多环境配置,Spring Boot引入了profile的概念,可以创建多个环境的配置文件,通过spring.profiles.active参数指定
application-dev.properties
application-prod.properties
配置文件加载原理
①启动创建ConfigurableEnvironment
②添加默认配置
③ConfigFileApplicationListener查找配置文件,加载到的每个配置文件会被添加到Environment中作为PropertySource,后加载的配置会覆盖先加载的配置
④根据profile.active处理Profile
⑤环境变量和系统属性,ConfigFileApplicationListener会将操作系统环境变量和JVM系统属性添加到Environment中
⑥处理命令行参数,并将其添加到Environment中
自动配置相关
自动配置原理
①每一个Starter都有一个spring-boot-autoconfigure依赖
②spring-boot-autoconfigure依赖的包里有一个META-INF/spring.factories文件,里面指定了所有启动要加载的自动配置类
③@EnableAutoConfiguration 会自动的把上面文件里面写的所有自动配置类都导入进来。xxxAutoConfiguration 是有条件注解进行按需加载的,不是无脑加载
④xxxAutoConfiguration给容器中导入一堆组件,组件都是从 xxxProperties中提取属性值
⑤xxxProperties又是和配置文件进行了绑定
常用注解
自动装配
大致原理:基于约定大于配置,本身内置了非常多的y依赖项集合,它们基于@Conditionnal注解进行注册,当我们在maven中添加了各种starter依赖后,满足装配条件的Bean会在启动时进行自动装配,实现方式看下面
@SpringBootApplication
定义在main方法入口类处,标注这是一个SpringBoot应用,用于启动sping boot应用项目
@SpringBootConfiguration
@Configuration
标注这个类这是一个配置Bean
@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class)
把主程序所在的包的所有组件导入进来
@Import(AutoConfigurationImportSelector.class)
在src/main/resources的META-INF/spring.factories
加载所有自动配置类:加载starter导入的组件
@ComponentScan
排除前面已经扫描进来的配置类、和自动配置类
tips:注解的执行顺序的倒叙的
步骤
①每一个Starter都有一个spring-boot-autoconfigure依赖
②spring-boot-autoconfigure依赖的包里有一个META-INF/spring.factories文件,里面指定了所有启动要加载的自动配置类
③@EnableAutoConfiguration 会自动的把上面文件里面写的所有自动配置类都导入进来。xxxAutoConfiguration 是有条件注解进行按需加载的,不是无脑加载
④xxxAutoConfiguration给容器中导入一堆组件,组件都是从 xxxProperties中提取属性值
⑤xxxProperties又是和配置文件进行了绑定
常用注解(分类版本)
组件注册
@SpringBootConfiguration
@Configuration
@Bean
@Scope
@Import
@ComponentScan
@Controller、 @Service、@Repository、@Component
@ImportResource
加载xml配置,一般是放在启动main类上
条件注解
@ConditionalOnXxx
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnBean
@ConditionalOnMissingBean
属性绑定
@ConfigurationProperties
声明组件的属性和配置文件哪些前缀开始项进行绑定
@EnableConfigurationProperties
快速注册组件
场景:SpringBoot默认只扫描自己主程序所在的包。如果导入第三方包,即使组件上标注了 @Component、@ConfigurationProperties 注解,也没用。因为组件都扫描不进来,此时使用这个注解就可以快速进行属性绑定并把组件注册进容器(这个还需要被标注的第三方组件类上本身标注了@configurationProperties注解)
● @EnableConfigurationProperties
● 1、开启Sheep组件的属性绑定
● 2、默认会把这个组件自己放到容器中
常用于导入第三方包
● 1、开启Sheep组件的属性绑定
● 2、默认会把这个组件自己放到容器中
常用于导入第三方包
@Value
application.properties定义属性,直接使用@Value注入即可
Web MVC
@PathVariable
用来获得请求url中的动态参数
@ResponseBody
支持将返回值放在response体内,而不是返回一个页面
@RequestParam
获取request请求的参数值
@RestController
组合@Controller和@ResponseBody
AOP
@Order
@Order(1),值越小优先级超高,越先运行
生命周期
@PostConstruct
spring容器初始化时,要执行该方法
自定义starter
①创建SpringBoot项目,添加spring-boot-starter依赖
②编写模块功能,引入模块所有需要的依赖
绑定配置的xxxProperties类,通过@ConfigurationProperties(prefix = "xxx")和配置文件进行绑定
③编写xxxAutoConfiguration自动配置类,帮其他项目导入这个模块需要的所有组件
④编写配置文件META-INF/spring.factories指定启动需要加载的自动配置
⑤打包,发布到Maven仓库
⑥其他项目引用
个人理解:starter里边写的那些虽然加了各种注解,看上去是被Spring托管了,但是基于SpringBoot的扫描规则,这些类根本就不会被扫描到。因此,为了装载这些类,我们通过创建一个Configuration类,在类中import其他要托管的类,然后把配置类的全类名加到spring.factories文件里,通过另外一种加载途径实现装载
Web MVC相关
Spring Boot 为 Spring MVC 提供自动配置,适用于大多数应用程序。它取代了对@EnableWebMvc的需要,两者不能一起使用。
自动配置
HttpMessageConverters
Spring MVC 使用 HttpMessageConverter 接口来转换 HTTP 请求和响应。合理的默认值是开箱即用的。
MessageCodesResolver
Spring MVC 有一个策略,用于生成错误代码,用于从绑定错误中呈现错误消息:MessageCodesResolver。
静态index.html支持
自动使用 ConfigurableWebBindingInitializer bean
自动注册 Converter、GenericConverter 和 Formatter bean
支持提供静态资源,包括对 WebJars 的支持
包含 ContentNegotiatingViewResolver 和 BeanNameViewResolver bean
JDBC(快速过两眼得了)
JDBC是什么?
一套Java与数据库系统进行通信的接口规范,由Java提供接口,由各个数据库厂商进行实现。
核心类
Driver
MySQL驱动
5.0
com.mysql.jdbc.Driver
8.0
com.mysql.cj.jdbc.Driver
加载与注册JDBC驱动
Class.forName(“com.mysql.cj.jdbc.Driver”);
Connection
创建连接
DriverManager.getConnection(url, user, password);
Statement
用于执行静态 SQL 语句
PrepareStatemant
避免SQL注入,预编译
SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句
CallableStatement
用于执行 SQL 存储过程
ResultSet
将查询结果映射到类
封装了执行数据库操作的结果集
ResultSetMetaData
ResultSet 对象中列的类型和属性信息的对象
数据库连接池
Druid
Hikari
事务
DBUtil工具类封装
JavaWeb(最近应该这样称呼? Jakarta EE)
前言
Web项目主要由Jakarta EE规范 Servlet.Api 提供技术支持
What is Jarkarta Servlet
Jakarta Servlet 是一个基石 Web 框架,可以充当面向演示和面向服务的 Web 应用程序。Jakarta Servlet 意在减少将 HTTP 请求转换为 Java 对象所需的样板代码,并将 Java 对象作为 HTTP 响应提供,并管理围绕它们的所有生命周期。
What is Servlet
Servlet是实现了jakarta.servlet.Servlet接口的一个Java类。Servlet接口定义了 init、service 和 destroy 等生命周期方法。在实现 HTTP 服务时,我们可以拓展jakarta.servlet.http.HttpServlet 类,该类必须扩展并覆盖其中一个 doXxx 方法
What is Servlet Container
支持基于 Jakarta Servlet 的 Web 组件的 Web 服务器模块称为 Servlet 容器。servlet 容器可以是 Jakarta 运行时的一部分,例如应用程序服务器。在将 Web 应用程序安装或部署到 servlet 容器时,可以配置 Web 组件行为的某些方面。
容器举例
Eclipse GlassFish
IBM Open Liberty
Red Hat WildFly
Apache Tomcat
Servlet的生命周期
Servlet 的生命周期由部署了 Servlet 的 Servlet 容器控制。
步骤
1. 如果 servlet 的实例不存在
加载 servlet 类
创建 servlet 类的实例
通过调用 init 方法初始化 servlet 实例
2.
servlet 容器调用服务方法,传递请求和响应对象。
Servlet的作用域
RequestScoped
它从客户端发送请求开始存在,直到它检索到相应的响应。它不会在其他地方共享。
SessionScoped
只要客户端使用相同的浏览器实例与 Web 应用程序交互,并且会话在服务器端没有超时,它就会存在。它在同一会话中的所有请求之间共享。
ApplicationScoped
只要 Web 应用程序存在,它就会存在。它在所有会话中的所有请求之间共享。
Servlet能保证并发请求出现的安全性问题嘛?
Web 容器通常会创建一个线程来处理每个请求。当资源可以并发访问时,它们的使用方式可能会不一致。第一步是确保表示资源的变量具有正确的范围,并使用尽可能窄的范围。
如果并发访问是不可避免的,那么您可以通过使用同步对象或原子对象来防止这种情况
创建和初始化 Servlet
web.xml
注解(建议使用)
①extends HttpServlet
②使用@WebServlet
③重写doGet或者doPost方法
API
Servlet请求处理流程
1. 客户端向 Web 服务器发送 HTTP 请求
2. 支持基于 Jakarta Servlet 的 Web 组件的 Web 服务器模块称为 Servlet 容器
3. servlet 容器将 HTTP 请求转换为 HttpServletRequest 对象,并准备 HttpServletResponse 对象
4. 这些对象被传递到 Web 组件,该组件可以与 Bean 或数据库交互以生成动态内容
5. Web 组件可以使用生成的动态内容填充 HttpServletResponse 对象,也可以将对象传递给另一个 Web 组件来填充它
6. servlet 容器最终将 HttpServletResponse 对象转换为 HTTP 响应,然后 Web 服务器将其返回给客户端
三大核心组件
Servlet接口
某种意义上来说,你可以认为它是一种规范,Java提供了对应的接口供我们实现,比如Tomcat这类服务端程序,我们称之为Servlet容器
使用方式
方式一:
继承HttpServlet类,重写services方法,在web.xml文件里配置servlet映射
方式二:
继承HttpServlet类,重写services方法,使用在目标类上使用@WebServlet注解,在注解信息里配置servlet注册信息
生命周期
构造
init()
只执行一次
service()
来一次请求执行一次
destory()
只执行一次
衍生类/接口
HttpServletRequest接口(请求ֿ域)
ServletConfig接口(会话域)
为Servlet提供Ӭ始Հ数的一种对,个Servlet都有自己独立唯一的ServletConfig对象
ServletContext接口(应用域)
所有的Servlet所共享
转发与重定向
服务器转发
url不会变,请求参数会透传
客户端重定向
url会变,请求参数不会透传
JSP(JavaServer Pages)
本质就是一个Servlet
JavaBean
一种可重用的 Java 类,通常用于封装数据和业务逻辑
其他组件
Filter(过滤器)
规范之一,对请求和响应进行预处理和后处理
功能
查询请求并采取相应措施
阻止请求和响应对进一步传递
修改请求头和数据。您可以通过提供请求的自定义版本来实现此目的
修改响应头和数据。您可以通过提供响应的自定义版本来实现此目的
与外部资源交互
身份验证、日志记录、图像转换、数据压缩、加密、标记流、XML 转换
核心方法(生命周期)
init
doFilter
destroy
使用
一、
声明Filter接口,重写doFilter方法
处理完过滤逻辑,filterChain.doFilter(reSuest,response)放行
web.xml文件里配置
二、
声明Filter接口,重写doFilter方法
处理完过滤逻辑,filterChain.doFilter(reSuest,response)放行
目标类上加上,@WebFilter注解,在注解信息里配置Filter信息
Listener(监听器)
用于监视 Web 应用程序中的事件,并在事件发生时执行相应的操作
★属实没怎么用过
Web context
ServletContextAttributeListener
ServletContextAttributeEvent
ServletContextListener
ServletContextEvent
Session
HttpSessionAttributeListener
HttpSessionBindingEvent
HttpSessionListener
HttpSessionActivationListener
HttpSessionEvent
Request
ServletRequestListener
ServletRequestAttributeListener
Filter和Interceptor的区别:
Filter 是 Servlet 规范中的一部分,它是基于 Java Servlet API 实现的
Interceptor 是在 Spring 框架中使用的一种拦截器,它是基于 Spring 框架的 AOP(面向切面编程)机制实现的
Filter 是通过在 web.xml 文件中配置来指定对特定 URL 或者某些 Servlet 请求进行拦截和处理的
Interceptor 主要用于拦截 Spring MVC 控制器的方法调用,对请求进行预处理和后处理
Filter 在 Servlet 容器中工作,对所有的请求和响应进行拦截,包括静态资源
Interceptor 仅作用于 Spring MVC 的控制器方法,不会拦截对静态资源的请求
会话管理
Cookie
Cookie 存储在客户端
Cookie 的生命周期由客户端控制
Cookie 可以存储少量的数据
Cookie 存储在客户端,可能会被篡改
Session
Session 存储在服务器端
Session 的生命周期由服务器控制
Session 可以存储大量的数据
Session 更安全,因为数据存储在服务器端
Spring Framework
Spring的起源
早期的J2EE解决方案,比如EJB不好用,开发难度高、性能差,而Spring的出现就是为了简化Java EE的开发,Spring是一个轻量级的Java EE开发框架
Spring的优势
非侵入式
IOC思想
DI是具体实现
AOP
组件化
容器化
Spring核心组件(看一看就好)
Core
Beans
提供了框架的基础部分,包括控制反转和依赖注入
core
封装了 Spring 框架的底层部分,包括资源访问、类型转换及一些常用工具类
context
建立在 Core 和 Beans 模块的基础之上,集成 Beans 模块功能并添加资源绑定、数据验证、国际化、Java EE 支持、容器生命周期、事件传播等。ApplicationContext 接口是上下文模块的焦点
spEl
提供了强大的表达式语言支持
Testing
Spring 支持 Junit 和 TestNG 测试框架
DataAccess/Integration
JDBC
提供了一个 JDBC 的样例模板
ORM
提供与流行的“对象-关系”映射框架无缝集成的 API
JMS
提供一套 “消息生产者、消息消费者”模板用于更加简单的使用 JMS
Transaction
支持编程和声明式事务管理。
OXM
提供了一个支持 Object /XML 映射的抽象层实现
Web Servlet
WebSocket
提供了简单的接口,用户只要实现响应的接口就可以快速的搭建 WebSocket Server,从而实现双向通讯。
Servlet
提供了一个 Spring MVC Web 框架实现
Web
提供了基本的 Web 开发集成特性
WebFlux
Spring WebFlux 是 Spring Framework 5.x中引入的新的响应式web框架
AOP
提供了面向切面编程实现
Aspects
提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架
Messaging
IOC(控制反转)
如何理解IOC?
控制反转是一种思想,用户管理Bean的行为转变为框架管理Bean,解放双手不用自己手动New对象,向容器“申请”。创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,达到一个解耦的效果
什么是Bean?
Bean 是由 Spring IoC 容器实例化、组装和管理的对象。
IOC配置的(二)三种方式
基于XML配置文件形式装配
通过ClassPathXmlApplicationContext装载
代码/注解方式装配
在配置类上添加@Configuration注解,在方法上添加@Bean注解,方法返回对象,本质上也可以认为是一个XML;同时可以在配置类上声明@ComponentScan,指定要扫描的包,然后使用注解@Component,@Controller,@Service,@Repository声明要注册的Bean。
通过AnnotationConfigApplicationContext装载
常用注解
@Component
标注这是一个Bean,搭配@ComponentScan使用
@Value
为变量赋值,可用使用字面量、spel或者引用配置文件的值进行赋值
@Configuration
标注这是一个配置Bean(等效xml文件)
@ComponentScan
指定扫描路径下的Bean
@Bean
标注这是一个Spring Bean,搭配@Configuration使用
@PropertySource
指定配置文件来源,和@Value搭配使用
@Scope
标注Bean的作用域
@Condition
根据条件实现类的装配
@Import
导入Configuration类,与 Spring XML 中的 <import/> 元素等效
@ImportResource
导入Spring XML文件,内部会 XmlBeanDefinitionReader 来解析 Spring <beans/> XML 文件
容器常用操作
获取Bean
getBean()
获取Bean名称
getBeanNamesForType()
getBeanDefinitionNames()
关闭容器
close()
判断容器中是否存在Bean
containsBean()
containsBeanDefinition()
Bean的实例化方式
通过反射调用构造方法来实例化(通常情况下都是这种)
工厂实例化(不太常用)
使用场景
如果我们使用到的类是我们自己编写的,那么可以通过依赖注入做解耦。但是,有时,我们需要依赖第三方库,需要实例化并使用第三方库中的相关类,这时,耦合性需要其他方式来避免。
XML方式
实例工厂实例化
①将 class 属性留空
②在 factory-bean 属性中指定当前容器中 Bean 的名称
③使用 factory-method 属性设置工厂方法本身的名称
代码示例:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
静态工厂方法实例化
①使用 class 属性来指定包含静态工厂方法的类
②在标签中使用factory-method属性指定静态的工厂方法
代码示例:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
代码/注解方式
FactoryBean接口实例化
对上面两种XML形式的简化
Bean的作用域(@Scope)
singleton单例,默认
IOC容器初始化时创建
prototype多实例
获取Bean时创建
request,web环境下
session,web环境下
application,Servlet上下文(不了解)
websocket(不了解)
依赖注入(DI)
先对DI下一个定义:
依赖注入(DI)是一个过程,在此过程中,对象仅通过构造函数参数、工厂方法参数或对象实例构造或从工厂方法返回后在其上设置的属性来定义其依赖关系(即与之协同工作的其他对象)。然后,容器会在创建 Bean 时注入这些依赖关系。从根本上说,这一过程是 bean 本身通过直接构建类或服务定位器模式来控制其依赖关系的实例化或位置的逆过程(因此被称为控制反转)。
DI的方式
XML配置的方式
构造方法注入(推荐)(XML里的<constructor-arg/>标签)
定义
基于构造函数的 DI 是通过容器调用带有多个参数的构造函数来实现的,每个参数代表一个依赖项。
优点
能避免循环依赖,项目在启动时如果存在循坏依赖,会抛异常提醒
保证对象的不变性,避免空指针异常
setter方法注入(XML里的<property/>标签)
定义
基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法来实例化 Bean 后在 Bean 上调用 setter 方法来完成的。
优点
setter 注入的一个好处是 setter 方法使该类的对象以后可以重新配置或重新注入
某种方面来说,你可以用它来配置循环依赖
缺点
(注入的有可能为null)应仅用于可以在类中分配合理默认值的可选依赖项,否则,必须在代码使用依赖项的任何地方执行非 null 检查
属性注入(自动装配)(XML里,bean标签里的autowire属性)
定义
可以不通过上述的两个标签显示的指定依赖关系,而是由Spring容器自动管理、更新Bean的协作关系。
autowire可选属性
no(默认),不开启
byName
Spring 会查找与需要自动装配的属性同名的 bean。
byType
如果容器中正好存在一个属性类型的 bean,则允许自动连接属性。如果存在多个 bean,则会抛出致命异常,这表明您不能对该 Bean 使用 byType 自动连线。如果没有匹配的 bean,则不会发生任何事情(未设置属性)。
constructor
类似于 byType,但适用于构造函数参数。如果容器中没有构造函数参数类型的一个 bean,则会引发致命错误。
拓展:
byName和byType类型的自动装配模式是针对property的自动装配,而constructor类型则是针对
构造方法参数的类型而进行的自动装配,它同样是byType类型的自动装配模式。不过,constructor是匹
配构造方法的参数类型,而不是实例属性的类型。与byType模式类似,如果找到不止一个符合条件的
bean定义,那么,容器会返回错误。使用上也与byType没有太大差别。
构造方法参数的类型而进行的自动装配,它同样是byType类型的自动装配模式。不过,constructor是匹
配构造方法的参数类型,而不是实例属性的类型。与byType模式类似,如果找到不止一个符合条件的
bean定义,那么,容器会返回错误。使用上也与byType没有太大差别。
优点
可以显著减少指定属性或构造函数参数的需要
可以随着对象的发展自动更新配置
缺点
属性和构造函数-参数设置中的显式依赖关系会始终覆盖掉Autowire。
没有显示配置的依赖关系那么明确,简而言之就是,你不知道Spring内部帮你做了什么东西
注解注入
涉及到的注解
@Autowire(√)
Spring提供的,byType,结合@Qualifier注解可以byName
拓展使用:
由于在ByType的情况下可能会遇到多个候选Bean,我们可以使用@Primary进行强制指定(@Primary只能作用于方法上)
@Resource(√)
JSR250规范实现,byName
拓展:
如果未显式指定名称,则默认名称派生自字段名称或 setter 方法。如果是字段,则采用字段名称。如果是 setter 方法,它采用 bean 属性名称。
@Inject(×)
JSR330规范实现,目的是为了兼容Jakarta,默认按照Type匹配,@Inject和@Named一起使用实现byName
注解的使用位置
加在属性上
创建Bean时通过后置处理器去容器里找要注入的Bean,如果需要注入的Bean没有实例化,会转而先实例化该Bean,再通过反射进行赋值
源码:
加在setter方法上
创建Bean时通过后置处理器,使用反射调用setter方法进行注入。源码入口是和属性一样的,只是这里有多态,调用inject方法走的是AutowiredMethodElement类的方法,而属性则是直接走的InjectedElement的inject方法
源码:
加在构造函数上
创建Bean时,直接判断是否有可以解析的构造函数,有的话直接通过反射去调用构造函数,不走后置处理器,根据形参的类型装配
源码:
拓展用法:
可用注入数组或者List
ps:
从 Spring Framework 4.3 开始,如果目标 Bean 一开始只定义了一个构造函数,则不再需要对此类构造函数进行 @Autowired 注解。但是,如果有多个构造函数可用,并且没有主/默认构造函数,则必须至少用 @Autowired 注释其中一个构造函数,以便指示容器使用哪个构造函数。
使用@DependsOn注解 / 或者在xml中使用depends-on强制指定依赖关系
@DependsOn注解 / depends-on 属性可以显式地强制在初始化使用此元素的 Bean 之前初始化一个或多个 bean,它既可以指定初始化时间依赖性,也可以指定相应的销毁时间依赖性
延迟加载
xml中使用lazy-init属性
注解使用@Lazy
对延迟初始的误区纠正:
当延迟初始化的 Bean 是未延迟初始化的单例 Bean 的依赖项时,容器会在启动时创建延迟初始化的 Bean,因为它必须满足单例的依赖关系。
Bean的生命周期
1、Bean对象的创建(调用无参构造函数)
2、为Bean对象设置相关属性(赋值)
依赖注入
3、Bean后置处理器(初始化前)
需要实现BeanPostProcessor后置处理器接口,重写postProcessBeforeInitialization方法,这块逻辑对所有Bean生效
4、Bean对象初始化(调用指定的初始化方法)
不同的Bean可以定制化处理,使用@PostConstruct
不同的Bean可以定制化处理,实现InitializingBean接口,重写afterPropertiesSet()方法
不同的Bean可以定制化处理,@Bean注解中通过initMethod属性指定方法签名
5、Bean后置处理器(初始化后)
需要实现BeanPostProcessor后置处理器接口,重写postProcessAfterInitialization方法,这块逻辑对所有Bean生效
6、Bean对象创建完成,使用
7、Bean对象销毁(执行指定销毁方法)
不同的Bean可以定制化处理,使用@PreDestroy
不同的Bean可以定制化处理,实现DisposableBean,重写destroy()方法
不同的Bean可以定制化处理,@Bean注解中通过destroyMethod属性指定方法签名
8、IOC容器关闭
容器对象.close()方法
ps:
特别标注,@Value对变量的负责操作在构造函数执行完之后,自定义init方法执行前,即第2步
Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。
关于循环依赖
Spring只是解决了单例模式下属性依赖的循环问题;Spring为了解决单例的循环依赖问题,使用了三级缓存
第一层缓存(singletonObjects):单例对象缓存池,已经实例化并且属性赋值,这里的对象是成熟对象
第二层缓存(earlySingletonObjects):单例对象缓存池,已经实例化但尚未属性赋值,这里的对象是半成品对象;
第三层缓存(singletonFactories): 单例工厂的缓存
Spring提早将对象暴露出来使用,解决循环依赖
细节描述
1. A 创建过程中需要 B,于是 A 将自己放到三级缓里面,去实例化B
2. B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 A 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
3. B 顺利初始化完毕,将自己放到一级缓存里面(此时 B 里面的A 依然是创建中状态)。然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将 A 自己放到一级缓存里面。
问题答疑
为什么构造器注入属性无法解决循环依赖问题?
由于 spring 中的 bean 的创建过程为先实例化 再初始化(在进行对象实例化的过程中不必赋值)将实例化好的对象暴露出去,供其他对象调用,然而使用构造器注入,必须要使用构造器完成对象的初始化的操作,就会陷入死循环的状态
二级缓存能不能解决循环依赖问题?
理论上二级缓存可以解决循环依赖问题,但是需要注意,为什么需要在三级缓存中存储匿名内部类(ObjectFactory),原因在于 需要创建代理对象 eg:现有A 类,需要生成代理对象 A 是否需要进行实例化(需要) 在三级缓存中存放的是生成具体对象的一个匿名内部类,该类可能是代理类也可能是普通的对象,而使用三级缓存可以保证无论是否需要是代理对象,都可以保证使用的是同一个对象,而不会出现,一会儿使用普通bean 一会儿使用代理类
单例以外的循环依赖怎么解决?
生成代理对象产生的循环依赖
@Lazy注解,延迟加载
修改文件名称,改变循环依赖类的加载顺序
@DependsOn注解,指定加载先后关系
使用构造注入
多例循环依赖
通过把bean改成单例的解决
构造器循环依赖
使用@Lazy注解解决
IOC源码分析
两个顶层接口
BeanFactory: 工厂模式定义了IOC容器的基本功能规范,能够管理任何类型的对象。该接口的容器默认采用延迟初始化策略
ApplicationContext是 BeanFactory 的一个子接口,补充了额外的企业特定功能信息,比如AOP集成、消息资源处理(国际化)、事件发布、特定的容器上下文比如Web环境的容器上下文WebApplicationContext。ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。
BeanRegistry: 向IOC容器手工注册 BeanDefinition 对象的方法
IOC容器初始化流程
初始化的入口在容器实现中的 refresh()调用来完成
对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致过程如下
通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统, URL 等方式来定为资源位置。如果是 XmlBeanFactory作为 IOC 容器,那么需要为它指定 bean 定义的资源,也就是说 bean 定义文件时通过抽象成 Resource 来被 IOC 容器处理的
通过 BeanDefinitionReader来完成定义信息的解析和 Bean 信息的注册, 往往使用的是XmlBeanDefinitionReader 来解析 bean 的 xml 定义文件 - 实际的处理过程是委托给 BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在 Spring 中使用 BeanDefinition 对象来表示 - 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition 这些相关的方法 - 他们都是为处理 BeanDefinitin 服务的
容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IoC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个HashMap 来实现的.
然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC 容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IoC 风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。 Spring 本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在ServletContext 中的框架实现。
总结:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中存放的是Bean的定义即BeanDefinition放到beanDefinitionMap中,本质上是一个ConcurrentHashMap<String, Object>;并且BeanDefinition接口中包含了这个类的Class信息以及是否是单例等
AOP(面向切面编程)
AOP思想简述
AOP实现定义:通过定义切面, 通过拦截切点实现了不同业务模块的解耦,是通过预编译方式和运行期间动态代理实现程序的统一维护的一种技术
相关概念
连接点 Join point
在哪里干
切入点 pointcut
在哪里干的集合
通知 Advice
干什么
前置通知
Before advice
后置通知
After returning advice
环绕通知
环绕通知是最常用的通知类型,从方法参数这中拿到JoinPoint自定义通知逻辑
Around advice
异常通知
After throwing advice
最终通知
After (finally) advice
切面 Aspect
在哪干和干什么集合
其他
引入
干什么
目标对象
对谁干
织入(weaving)
怎么实现的
AOP代理
怎么实现的一种典型方式
实现
Spring AOP
动态织入
JDK动态代理(Proxy+InvocationHandler)
原理:通过生成代理对象对原对象进行增强(基于接口的实现类增强)
有接口使用
因为JDK动态代理是基于接口实现的,如果被代理的类没有实现任何接口,就无法生成对应的代理类,也就无法实现动态代理
CGLIB动态字节码增强(MethodInterceptor)
原理:为需要增强的类生成一个子类,通过子类对父类逻辑进行增强(基于继承的子类增强)
无接口使用
AspectJ
静态织入
编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类
PS
AOP在运行时仍旧是纯的Spring AOP,并不依赖于AspectJ的编译器或者织入器
在使用Spring AOP时,如果你需要使用注解配置AOP的话,依旧要集成AspectJ相关的依赖。因为相关的注解是来自于AspectJ的
配置方式
XML配置(×)
Java API方式(×)
注解方式(需要AspectJ依赖支持)(√)
如何开启注解支持
XML开启
<aop:aspectj-autoproxy/>
注解开启
@EnableAspectJAutoProxy
常用注解
@Aspect
用来定义一个切面。
PS:注意,这个注解不会把类托管到容器,所以我们一般还要带上@Component注解
@pointcut
用于定义切入点表达式。在使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称,这个方法签名就是一个返回值为void,且方法体为空的普通方法。
PointCut的组成部分
一个由名称和任何参数组成的签名,切入点签名由正则方法定义提供
一个切入点表达式,切入点表达式通过使用@Pointcut注解表示
execution(权限 包 . 类 . 方法())
@Before
用于定义前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已有的切入点,也可以直接定义切入点表达式)。
@AfterReturning
用于定义后置通知,相当于AfterReturningAdvice。在使用时可以指定pointcut / value和returning属性,其中pointcut / value这两个属性的作用一样,都用于指定切入点表达式。
@Around(√可操作性最强)
用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。
使用细节
①将 Object 声明为其返回类型
②该方法的第一个参数必须是 ProceedingJoinPoint 类型
③将joinpoint的执行结果返回
@After-Throwing
用于定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定pointcut / value和throwing属性。其中pointcut/value用于指定切入点表达式,而throwing属性值用于指定-一个形参名来表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。
@After
用于定义最终final 通知,不管是否异常,该通知都会执行。使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。
增强通知的顺序
实现org.springframework.core.Ordered 接口,较低的那个有更高的优先级
用Order注解
较低的那个有更高的优先级
Spring AOP 还是 AspectJ?
总结来说就是 Spring AOP更易用,AspectJ更强大
AOP源码分析
Spring默认在目标类实现接口时是通过JDK代理实现的,只有非接口的是通过Cglib代理实现的。当设置proxy-target-class为true时在目标类不是接口或者代理类时优先使用cglib代理实现
事务管理(基于Spring AOP实现)
管理事务的方式
编程式事务(PlatformTransactionManager)
可以进行自定义粒度的事务管理
声明式事务(配置类上添加@EnableTransactionManagement注解开启)
这个用的比较多,好用,方便
事务@Transactional属性
只读read-only
默认false
超时timeout
默认无
回滚策略
rollbackFor属性:需要设置一个Class类型的对象
rollbackForClassName属性:需要设置一个字符串类型的全类名
noRollbackFor属性:需要设置一个Class类型的对象
rollbackFor属性:需要设置一个字符串类型的全类名
事务的隔离级别isolation
DEFAULT
默认,根据数据库类型决定
READ_UNCOMMITTED
读未提交
READ_COMMITTED
读已提交
REPEATABLE_READ
可重复读
SERIALIZABLE
串行化
事务的传播行为propagation
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播
TransactionDefinition.PROPAGATION_REQUIRED(√)
没有就新建,有就加入
TransactionDefinition.PROPAGATION_REQUIRES_NEW(√)
不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起
TransactionDefinition.PROPAGATION_NESTED
有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样
TransactionDefinition.PROPAGATION_MANDATORY
有就加入,没有就抛异
TransactionDefinition.PROPAGATION_SUPPORTS
有就加入,没有就不管了
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
不支持事务,存在就挂起
TransactionDefinition.PROPAGATION_NEVER
不支持事务,存在就抛异常
事务失效的场景
编程式事务
事务配置没配好
异常
多线程调用,多线程父子线程回滚异常,这玩意没法解决,别用
粗心
没有commit或者rollback
声明式事务
AOP失效
方法权限不是pubilc
方法被final修饰导致aop失效
同一个类中的方法调用没触发aop
事务的配置没配好
异常不匹配(默认是RuntimeException.class)
Bean没被Spring托管
多线程调用,多线程父子线程回滚异常,这玩意没法解决,别用
抛出的异常被try catch吞掉处理了,解决:catch后再抛一个,或者catch里直接手动回滚
Spring MVC
(这本来就是Spring里的一个模块,所以还是放在Spring里吧)
(这本来就是Spring里的一个模块,所以还是放在Spring里吧)
介绍
Spring MVC是Spring在Spring Container Core和AOP等技术基础上,遵循上述Web MVC的规范推出的web开发框架,目的是为了简化Java栈的web开发。它是基于 Servlet API 构建的原始 Web 框架。可以这么认为:Spring在Web这块对Jakarta EE的MVC规范进行了兼容实现,除此之外还有自己的实现(Spring WebFlux),不过我们实际情况下还是MVC那套用的比较多一点
为我们提供了什么特性?
更简洁的Web 层的开发
强大的约定大于配置的契约式编程支持
灵活的URL 到页面控制器的映射
非常容易与其他视图技术集成,如 Velocity、FreeMarker 等等
非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API
灵活的本地化、主题等解析
简单的异常处理
静态资源的支持
支持Restful 风格
组件/Bean(感觉可以直接去看servlet的内容)
DispatcherServlet
介绍
DispatcherServlet,又称前端控制器,为请求处理提供共享算法,而实际工作由可配置的委托组件执行
使用
Java 配置
web.xml配置
可委托的类(常见)
Controller
处理器/页面控制器,做的是 MVC 中的 C 的事情,但控制逻辑转移到前端控制器了,用于对请求进行
处理
处理
HandlerMapping
将请求映射到处理程序以及用于预处理和后处理的拦截器列表。
HandlerInterceptor
拦截HandlerMapping
preHandle(..)
postHandle(..)
afterCompletion(..)
HandlerAdapter
帮助 DispatcherServlet 调用映射到请求的处理程序,而不考虑处理程序的实际调用方式。
ViewResolver
将从处理程序返回的基于 String 的逻辑视图名称解析为要用于呈现响应的实际 View。
ThemeResovler
主题解析,通过它来实现一个页面多套风格,即常见的类似于软件皮肤效果
LocaleResolver, LocaleContextResolver
LocaleResolver、LocaleContextResolver
LocaleResolver、LocaleContextResolver
解决客户端正在使用的区域设置,并可能解决其时区,以便能够提供国际化视图。
MultipartResolver
在一些多部分解析库的帮助下,用于解析多部分请求(例如,浏览器样式文件上传)的抽象
SpringMVC实现原理之DispatcherServlet的初始化过程
init()
主要读取web.xml中servlet参数配置,并将交给子类方法initServletBean()继续初始化
initWebApplicationContext()
initWebApplicationContext用来初始化和刷新WebApplicationContext
onRefresh()
调用initStrategies(context)方法对DispatcherServlet中的组件进行初始化
initHandlerXXX
获取按照优先级排序后的HanlderMappings, 将来匹配时按照优先级最高的HanderMapping进行处理
拦截器
HandlerInterceptor
对处理器进行预处理和后处理
执行流程
图示:
时序图:
描述:
1. 用户发送请求至前端控制器 DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行
处理,作为统一访问点,进行全局的流程控制
处理,作为统一访问点,进行全局的流程控制
2. HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一
个 Handler 处理器(页面控制器)对象、多个 HandlerInterceptor 拦截器)对象,将该对象返回给DispatcherServlet
个 Handler 处理器(页面控制器)对象、多个 HandlerInterceptor 拦截器)对象,将该对象返回给DispatcherServlet
3. DispatcherServlet 将 HandlerExecutionChain对象交给HandlerAdapter
4. HandlerAdapter 将会根据适配的结果调用真正的处理器(Controller)的功能处
理方法,完成功能处理;并返回一个 ModelAndView 对象(包含模型数据、逻辑视图名)给DispatcherServlet;
理方法,完成功能处理;并返回一个 ModelAndView 对象(包含模型数据、逻辑视图名)给DispatcherServlet;
5. DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器,ViewResolver 将把逻辑视图名解析为具体的 View
6. DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中),View 会根据传进来的 Model 模型数据进行渲染,此处的 Model 实际是一个 Map 数据结构
7. 返回控制权给 DispatcherServlet,由 DispatcherServlet 返回响应给用户,到此一个流程结束
注解使用
控制器组件声明
@Controller
输出效果
使用 HTML 模板进行视图分辨率和呈现
@RestController
等于@Controller + @ResponseBody
输出效果
直接写入响应正文
请求映射
@RequestMapping
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
最佳实践:
类上使用@RequestMapping作为共享路径
方法上使用具体的Mapping进行映射
映射的URI模式
PathPattern(√默认)
预解析模式,与 URL 路径匹配。此解决方案专为 Web 使用而设计,可有效处理编码和路径参数,并高效匹配。
AntPathMatcher
将 String 模式与 String 路径匹配,效率较低
Handler方法
方法参数
@RequestParam
将 Servlet 请求参数(即查询参数或表单数据)绑定到控制器中的方法参数
@MatrixVariable
用于访问 URI 路径段中的名称-值对
@PathVariable
捕获的 URI 变量
@RequestHeader
将请求标头绑定到控制器中的方法参数
@CookieValue
将 HTTP cookie 的值绑定到控制器中的方法参数
@RequestBody
用于访问 HTTP 请求正文。使用 HttpMessageConverter 实现将正文内容转换为声明的方法参数类型。
@RequestPart
访问 multipart/form-data 请求中的部件,使用 HttpMessageConverter 转换部件的主体。
@ModelAttribute
用于访问模型中的现有属性(如果不存在,则实例化),并应用数据绑定和验证。
@SessionAttribute
用于访问任何会话属性
@RequestAttribute
用于访问请求属性
参数校验(沿用Hibernate Validator)
@NotNull
@Null
@NotBlank
验证注解的元素值不为空(不为 null、去除首位空格
后长度为 0),不同于@NotEmpty,@NotBlank 只应用
于字符串且在比较时会去除字符串的首位空格
后长度为 0),不同于@NotEmpty,@NotBlank 只应用
于字符串且在比较时会去除字符串的首位空格
@NotEmpty
验证注解的元素值不为 null 且不为空(字符串长度不
为 0、集合大小不为 0)
为 0、集合大小不为 0)
@Valid
指定递归验证关联的对象;
如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加
@Valid 注解即可级联验证
如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加
@Valid 注解即可级联验证
返回值
@ResponseBody
返回值通过 HttpMessageConverter 实现进行转换为JSON,并写入响应。
@ModelAttribute
要添加到模型的属性,视图名称通过 RequestToViewNameTranslator 隐式确定。
异常处理
@ExceptionHandler
注解式声明异常处理器
@ControllerAdvice
@RestControllerAdvice
跨域
注解配置
@CrossOrigin
启用跨域请求
代码配置
①implements WebMvcConfigurer
②@Override addCorsMappings
③registry.allowedOrigins
视图技术(模板引擎)
Thymeleaf
JSP
FreeMarker
MVC配置
开启配置
配置类上添加@EnableWebMvc
集成配置类
implements WebMvcConfigurer
拦截器
@Override addInterceptors
registry.addInterceptor
消息转换
@Override configureMessageConverters
视图控制器
@Override addViewControllers
registry.addViewController
视图解析器
@Override configureViewResolvers
registry.enableContentNegotiation
其他集成(TODO)
RestClient
JMS
Task Execution and Scheduling
Email
Cache
Observability Support
WebFlux
MyBatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
Mybatis框架解决了什么问题:
手动对象关系映射
解决方式:反射
SQL 注入攻击
解决方式:预编译
配置繁琐
xml配置
结果集获取
类型处理
连接复用
连接池配置
拓展:
这里为什么不直接将Mybatis称为ORM框架呢?
其实Mybatis也没把自己成为ORM框架。mybatis属于半orm,因为sql语句需要自己写。与其他比较标准的 ORM 框架(比如 Hibernate )不同, mybatis 并没有将 java 对象与数据库关联起来,而是将 java 方法与 sql 语句关联起来。像Hibernate这种,你对Java对象的修改会直接映射到表结构更改上的喔。
核心组件
SqlSessionFactoryBuilder
通过实例化和使用它来创建SqlSessionFactory,创建完成后就可以丢弃它。所以它的最佳作用域是作为局部方法变量来使用。
SqlSessionFactory
应用运行期间一直存在。它的最佳实践应该是单例的。最佳作用域是应用级别的作用域。
SqlSession
每一个线程都应该有自己的SqlSession实例。它是线程不安全的,不能被共享。最佳作用域是会话级别的。
映射器实例
从 SqlSession 中获得。
Mybatis核心XML文件配置(一般Spring环境下才需要写)
properties标签
用于引入外部配置,进行动态替换使用
settings标签
顾名思义Mybatis是核心配置项,不过基本上都是用默认的
typeAliases标签
为 Java 类型设置一个缩写名字,降低冗余的全限定类名书写
typeHandlers(这个要了解一下)
在设置PreparedStatement中的参数或从结果集中取出一个值时,会用类型处理器将获取到的值以合适的方式转换成 Java 类型,这块也是基本用默认的。
Mybatis XML映射文件的配置
XML文件常用的顶级元素
select
insert
delete
update
sql
sql片段复用
cache
自定义缓存
cache-ref
自定义缓存
resultmap
结果集映射
还有一个#{}和${}的区别
${}直接使用字符串替换,使用的场景也比较固定,动态表名、列名
#{}使用PreparedStatement 进行预编译,效果就是会使用 ? 替代,用户入参使用的比较多,安全性高,可用避免SQL注入。如前面的typeHandlers配置项所说,它会用类型处理器将获取到的值以合适的方式转换成 Java 类型。
结果映射
ResultMap(强大的)
ResultMap一般不用显示配置,默认会找同名的属性的类型进行转换,当然如果结果映射确实比较复杂,还是得显示配置。
ResultType
结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。一般简单的映射可以用这个。它内部也是会自动创建ResultMap
tips:
ResultMap和ResultType只能二选一
在简单的场景下,MyBatis 可以为你自动映射查询结果。当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。 这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性。
缓存
一级缓存
sqlSession级别
默认开启
Select语句会被缓存;
Insert、Update、Delete语句会被刷新缓存;
使用LRU算法清除不用的缓存;
能存1024个引用
Insert、Update、Delete语句会被刷新缓存;
使用LRU算法清除不用的缓存;
能存1024个引用
二级缓存
Application级别
默认不开启
缓存配置
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存
MyBatis对会话(Session)级别的一级缓存设计的比较简单,就简单地使用了HashMap来维护,并没有对HashMap的容量和大小进行限制
一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行<cache/>
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能
动态SQL
if
语法<if test = " 条件 ">条件通过需要执行的sql部分</if>
choose(when、otherwise)类似Java的switch
trim(where、set)
前面两种在特殊的情况下,会导致SQL格式出现问题,执行失败,这个就是比较智能,会根据不同情况进行处理
foreach
对集合进行遍历
关于模糊查询
使用方式一,#符
LIKE "%"#{name}"%"
使用方式二,字符串拼接
AND name LIKE CONCAT(CONCAT('%',#{name},'%'))
使用方式三,方法入参里直接传
比如:param.setUsername("%CD%");
分页
分页方式
MySQK内置的limit分页
缺点:基于内存的分页,查出所有记录再按偏移量和limit取结果
Mybatis自带的RowBounds分页,内部也是用limit,只是不需要我们手动写
使用 RowBounds 进行分页时,MyBatis 会先查询出整个ResultSet结果集,然后在内存中按照 offset 和 limit 进行裁剪
自定义拦截器插件进行分页
拦截StatementHandler去做分页,不过最后本质还是用limit去做分页的
使用PageHelper插件分页(也是自定义拦截器方式)(推荐使用√)
引入分页插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>最新版本</version>
</dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>最新版本</version>
</dependency>
配置拦截器插件
com.github.pagehelper.PageInterceptor
在代码中使用
RowBounds方式的调用(物理分页)
List<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(0, 10));
PageHelper 静态方法调用(基于Mybatis拦截器)(推荐)
PageHelper.startPage(1, 10);
List<Country> list = countryMapper.selectIf(1);
List<Country> list = countryMapper.selectIf(1);
PageHelper.offsetPage(1, 10);
List<Country> list = countryMapper.selectIf(1);
List<Country> list = countryMapper.selectIf(1);
lambda用法(推荐)
Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(()-> countryMapper.selectGroupBy());
常用插件(只需实现 Interceptor 接口,并指定想要拦截的方法签名即)
● Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
● ParameterHandler (getParameterObject, setParameters)
● ResultSetHandler (handleResultSets, handleOutputParameters)
● StatementHandler (prepare, parameterize, batch, update, query)
● ParameterHandler (getParameterObject, setParameters)
● ResultSetHandler (handleResultSets, handleOutputParameters)
● StatementHandler (prepare, parameterize, batch, update, query)
注解开发(×),老老实实在XML写SQL!
官方对于Mybatis-Java注解开发的说法:
不幸的是,Java 注解的表达能力和灵活性十分有限。尽管我们花了很多时间在调查、设计和试验上,但最强大的 MyBatis 映射并不能用注解来构建——我们真没开玩笑。
虽然不建议用,但是了解一下,有一些在写XML文件的时候也有用到
静态SQL
@Insert
@Update
@Delete
@Select
动态SQL
@InsertProvider
@UpdateProvider
@DeleteProvider
@SelectProvider
参数映射
@Param (√)
结果集
@ResultMap
@ResultType
SQL 语句构建器(Java API)(客观看待,至少我在实际工作中确实没见过人用过这个)
MyBatis 在 XML 映射中具备强大的 SQL 动态生成能力。但有时,我们还是需要在 Java 代码里构建 SQL 语句。
SQL 类
new SQL()
方法(好像跟关键字也没啥差别)
SELECT
SELECT_DISTINCT
FROM
JOIN
INNER_JOIN
LEFT_OUTER_JOIN
RIGHT_OUTER_JOIN
WHERE
OR
AND
GROUP_BY
HAVING
ORDER_BY
LIMIT
OFFSET
DELETE_FROM
INSERT_INTO
UPDATE
VALUES
......
原理篇
Mybatis功能原理
动态代理
使用动态代理技术完成Mapper的代理、SQL的解析
动态字节码技术
使用动态字节码技术完成对象关系的映射、延迟装配
运行逻辑
①读取、解析配置文件
②装配数据库连接以及事务管理
③读取Mapper接口以及xml文件所在的位置,完成关联
④mapper 代理对象调用 mapper 接口方法时 mybatis 利用动态代理找到对应的标签
⑤mybatis 找到对应标签 解析 SQL 表达式及动态代理完成参数的适配
⑥mybatis 使用 JDBC 执行 SQL 操作、拿到 Resultset
⑦通过配置告知 mybatis 列和对象属性的对应关系、让 mybatis 完整封装
⑧过程中预留 拦截器执行
实操篇
Spring集成Mybatis(略,现在基本上也见不到了吧)
Spring Boot集成Mybatis
原理/效果
①自动探测存在的 DataSource
②创建并注册一个 SqlSessionFactory 的实例,并将探测到的 DataSource 作为数据源
③创建并注册一个从 SqlSessionFactory 中得到的 SqlSessionTemplate 的实例
④自动扫描你的 mapper,将它们与 SqlSessionTemplate 相关联,并注入到Spring容器
基本步骤
①添加Mybatis的核心依赖
②添加mybatis-spring-boot-starter依赖
③通过@Mapper注解写Mapper类,并与xml映射
④在业务类中注入Mapper使用
进阶用法
关于Mapper扫描
MyBatis-Spring-Boot-Starter 将默认搜寻带有 @Mapper 注解的 mapper 接口。当然,你也可以通过注解自定义扫描的路径@MapperScan
配置
mapper-locations
XML 映射文件的路径
0 条评论
下一页