格局管理(项目经验)

格局管理和性能优化

Java线程池
https://www.bilibili.com/video/BV1dt4y1i7Gt/?spm_id_from=333.337.search-card.all.click&vd_source=b095f3891bf521af6b5ac46a2e806536
https://www.bilibili.com/video/BV1wh411e7nd/?spm_id_from=333.337.search-card.all.click&vd_source=b095f3891bf521af6b5ac46a2e806536
https://blog.zhaolq.com/article/2023/07/22%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7%E7%B1%BB-Executor%E4%B8%8E%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%9A%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E6%AD%A3%E7%A1%AE%E7%9A%84%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%9F/

springboot启动过程
https://www.youtube.com/watch?v=jyTdn5-yV2o&ab_channel=%E5%B0%9A%E7%A1%85%E8%B0%B7IT%E5%9F%B9%E8%AE%AD%E5%AD%A6%E6%A0%A1
https://www.bilibili.com/video/BV1vtUWYQENj/?spm_id_from=333.337.search-card.all.click&vd_source=b095f3891bf521af6b5ac46a2e806536

bean的生命周期
https://www.bilibili.com/video/BV1L14y1S7cf/?spm_id_from=333.337.search-card.all.click&vd_source=b095f3891bf521af6b5ac46a2e806536
https://www.bilibili.com/video/BV1wT4y1W7Ga/?spm_id_from=333.337.search-card.all.click&vd_source=b095f3891bf521af6b5ac46a2e806536
https://www.bilibili.com/video/BV1Fi4y147HC/?spm_id_from=333.337.search-card.all.click&vd_source=b095f3891bf521af6b5ac46a2e806536

Java锁
https://blog.zhaolq.com/article/2015/10/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B5%E4%B9%8B%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E4%B8%8E%E5%90%8C%E6%AD%A54-%E9%94%81%E7%9A%84%E5%88%86%E7%B1%BB/
https://xie.infoq.cn/article/4e370ded27e4419d2a94a44b3

总结

工作日志记录

及时总结成可输出文档,包括但不限于 业务通用指标库、算法能力(sql能力、业务算法、建模算法、公共算法等)总结及代码实现、,并做成高阶技能快查app

制定规范,包括 需求规范、app开发规范、数据使用规范、数据清洗规范

重大贡献和输出记录,做好当下。

Level答辩:1、做了多少装备,开发的好(设计好,测试充分,交付件齐全);2、做了哪些能力帮助团队成长

绩效自评,数据尽可能量化。包括:重点工作完成情况,正向/负向关键事件等。注意涉及性能、效率提升等一定要有量化数据

进度汇报

负责模块、目标、问题、风险

数据处理术语

1、数据清洗是过滤杂质(冗余数据、非标数据、无效数据、异常数据、错误数据),一是为了解决数据质量问题,二是让数据更适合做挖掘。

2、数据萃取是提纯

数据库

建立主键

数据库表必须要有主键

优化索引

问题:导出功能走了全表扫描,导致扫描行数过多, mysql swap急速升高。

解决方案:建立合适的索引,尤其是跟在where、order by、关联查询的on后面的字段。

避免子查询

问题:临时表(即 from 后面跟 select xxx from table 这种写法)的用法偏多。因为是加载到内存中的临时表,没有索引可走,会走全表扫描。

解决方案:尽量避免非必要的临时表;

避免过多的表关联

问题:多表连接查询,有的查询甚至能关联到七八张表。

解决方案:禁止超过3张表的关联查询(由公司纳入数据库规范),一切运算等工作全部交由 Java 代码实现,数据库只做简单的存取动作。

运算交给Java

问题:多数业务逻辑都用sql语句实现,Java只做数据封装及展示,处理的业务逻辑非常有限。

解决方案:由于应用服务器资源十分充裕,而数据库存储资源异常紧张,建议一切运算等工作全部交由java代码实现,数据库只做简单的存取动作。

避免串行查询

避免多次串行查询数据库。后一次查询是否依赖前一次查询的结果,如果不是建议改成并行查询。

在业务逻辑设计的时候,尽量避免这种大批量串行查询的操作。

提高吞吐量

读写分离、Redis缓存

使用常规语句

分库分表、读写分离的中间件MyCat不支持复杂SQL语句,开发时尽量使用常规语句,提高软件的可移植性和可扩展性,为后期整改(数据库切换、分库分表、读写分离)做准备。

常规语句:DBMS都支持的语句,整理输出团队文档,避免出现非常规语句。实际项目中只需支持团队可能用到的DBMS即可(无需支持所有市面上的DB,团队把控)。

主从库

MyCat写入数据到master库,从slave库读取数据,master库数据需要同步到slave库。

关键点:主从同步实现方式;主从同步所需时间

阿里巴巴开源的MySQL同步工具 https://github.com/alibaba/canal

数据库调优

image-20241010071428562

sql及索引优化(性能分析工具)

sql慢问题定位:慢查询日志分析工具mysqldumpslow、sql执行成本show profile

执行计划:分析查询语句explain或describe(表的读取顺序、读取数据的访问类型、哪些索引可以使用、哪些索引被实际使用、表之间的引用、使用索引长度、每张表有多少行被优化器查询)。

根据执行计划优化索引,可以利用mysql8的隐藏索引并观察性能(invisible)。

  • id:id值越大,优先级越高,越先执行;id相同是一组,从上往下顺序执行;id号每个号码,表示一趟独立的查询, 一个sql的查询趟数越少越好;

  • type:访问类型从好到坏:system、const、eq_ref、ref、fulltext、ref_or_null、index_merge、unique_subquery、index_subquery、range、index、ALL
    system:当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory。
    const:根据主键或者唯一二级索引列与常数进行等值匹配时出现,比如对单表使用where等值匹配查询就是const
    eq_ref:对被驱动表的访问方法。在连接查询时,当被驱动表通过主键或者唯一二级索引列等值匹配的方式进行访问情况下会出现。
    ref:当通过普通的二级索引列与常量进行等值匹配时,对表的访问类型就可能是ref
    ref_or_null:当对普通二级索引进行等值匹配查询,该索引列的值也可以是NULL值时,对表的访问类型就可能是ref_or_null
    index_merge:索引合并,利用多个索引,keykey_len 是多个值。一般情况下对于某个表的查询只能使用到一个索引,当使用 orunionunion all 时会出现?
    unique_subquery:子查询包含IN,若查询优化器决定将IN子查询转换为EXISTS子查询,而且子查询可以使用到主键进行等值匹配时出现。
    index_subquery:子查询使用普通索引
    range:范围索引,使用 inbetween>< 时会出现。
    index:当使用索引覆盖,但需要扫描全部的索引记录时出现。覆盖索引:非聚簇复合索引包括查询里的SELECT、JOIN和WHERE子句用到的所有列( 索引列+主键 包含 SELECT 到 FROM之间查询的列 ),无需回表。联合索引不能直接进行refrange访问,只能扫描整个索引的记录。
    all:全表扫描。

  • possible_keys和key:可能用到的索引实际使用的索引。一个字段可能存在多个索引(单列索引、联合索引)。

  • key_len:实际使用到的索引长度,帮助检查 是否充分的利用了索引值越大越好,主要对 联合索引 有参考意义。

  • ref:当前索引被哪个列(另一张表的关联字段)或常量(where=’a’)使用。

  • rows:预估的需要读取或扫描的行数,即使用索引过滤后剩下的行数。

  • filtered:最终行数通过指定索引过滤剩下的行数(即 rows) 的百分比。

  • Extra:包含不适合在其他列中显示但十分重要的额外信息,帮助更准确的理解MySQL到底将如何执行给定的查询语句,额外信息有好几十个,官网 EXPLAIN Extra Information 。举例:

    No tables used:没有FROM子句时。
    Impossible WHEREWHERE子句永远为FALSE时,即查询结果为0行。
    Using where:访问类型为 allindex 时可能出现。
    Using index:仅使用索引树中的信息即可从表中检索列信息(覆盖索引),而无需执行额外的查找来读取实际行(无需回表)。
    Using index condition:where中虽然出现了索引列,却不能使用索引。比如 WHERE key1 > 'z' AND key1 LIKE '%a';,最后一个条件未使用索引。
    Using join buffer (Block Nested Loop):官方:将早期连接的表分部分读入连接缓冲区,然后使用缓冲区中的行与当前表执行连接。也就是说,EXPLAIN 输出中上一行表中的键被缓冲,并且从出现 Using join buffer 的行所表示的表中分批获取匹配的行。尚硅谷:在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为其分配一块名叫join buffer的内存块来加快查询速度,也就是我们所讲的基于块的嵌套循环算法
    Using sort_union(...), Using union(...), Using intersect(...):访问类型为 index_merge 时会出现。
    Using temporary:查询过程中需要创建临时表来保存中间结果。比如包含 distinct、group by、order by、union 等子句的查询过程中,如果不能有效利用索引完成查询,很可能会建立临时表。最好使用索引替换掉临时表。
    Using filesort:在内存中或磁盘上进行排序的称为文件排序。若使用文件排序检索的行非常多,必须优化为索引排序检索。

索引失效场景

1、联合索引不满足最左前缀原则

2、索引列参与运算

3、索引列参使用函数

4、like 的占位符位于条件的首部

5、索引列发生隐式转换(若索引字段为int,参数为varchar,mysql会将参数转化为int类型,依旧会走索引)

6、or两边出现非索引列

7、or两边出现 “>” 和 “<” 范围查询,即便是索引列也不会走

8、两个索引列做比较

9、使用 “<>” 或 “!=” 作为条件查询时,需要慎重,普通索引会查询结果集占比较大时索引会失效。

10、is not null

11、not in:主键索引有效,普通索引失效。

12、not exists:索引失效。

13、当进行范围查询(>、< 、>=、<=、in等条件)时,根据查询结果占全表数据比例的不同,优化器有可能会放弃索引,进行全表扫描。举例,当Mysql发现通过索引扫描的行记录数超过全表的10%-30%时,优化器可能会放弃走索引,自动变成全表扫描。

哪些情况适合创建索引

  1. 字段的数值有唯一性的限制
  2. 频繁作为 WHERE 查询条件的字段
  3. 经常 GROUP BY 和 ORDER BY 的列
  4. UPDATE、DELETE 的 WHERE 条件列
  5. DISTINCT 字段需要创建索引
  6. 多表 JOIN 连接操作时,创建索引注意事项
  7. 使用列的类型小的创建索引
  8. 使用字符串前缀创建索引
  9. 区分度高(散列性高)的列适合作为索引
  10. 使用最频繁的列放到联合索引的左侧
  11. 在多个字段都要创建索引的情况下,联合索引优于单值索引

限制索引的数目,一般不超过6个,原因:

1、空间占用
2、影响增删改性能,因为索引维护有成本
3、当多个索引可用于查询时,会增加优化器生成最优执行计划的时间

哪些情况不适合创建索引

  1. 在where中使用不到的字段,不要设置索引
  2. 数据量小的表最好不要使用索引
  3. 有大量重复数据的列上不要建立索引
  4. 避免对经常更新的表创建过多的索引
  5. 不建议用无序的值作为索引
  6. 删除不再使用或者很少使用的索引
  7. 不要定义冗余或重复的索引

数据库表结构优化

  • 优化数据库结构:数据冗余、字段类型、索引建立、拆分表(冷热数据分离)、禁用索引和唯一性检查(禁止事务自动提交来提升插入速度)、使用非空约束(省去not null判断导致索引失效、索引null需要1bit空间)。
    分析表:analyze table。分析字段值的分布,表中某一键所在的列不重复的值的个数,该值越接近表中的总行数,则在表连接查询或者索引查询时,就越优先被优化器选择使用。
    检查表:check table。
    优化表:optimize table。消除删除或者更新造成的空间碎片并重新利用。只能优化表中的 VARCHARBLOBTEXT 类型的字段。
  • 大表优化:当MySQL单表记录数过大时,数据库的CRUD性能会明显下降。
    方案:查询时使用范围条件、读写分离(一主一从、双主双从)、垂直拆分(垂直分库、垂直分表)、水平拆分(单表控制1000万以内,最好进行分库,分担磁盘IO提升并发性)。
    注意:尽量避免数据分片,无法避免则尽量选择客户端分片架构。两种常见分片方案:客户端代理(分片逻辑封装在jar中,通过修改或封装jdbc层来实现。比如sharding-jdbc)、中间件代理(分片逻辑封装在中间件服务器中。比如Mycat)。

MySQL选项参数优化

max_execution_time(select语句超时处理)

服务器硬件优化

如何定位调优问题

  • 用户的反馈(主要):用户是我们的服务对象,因此他们的反馈是最直接的。虽然他们不会直接提出技术建议,但是有些问题往往是用户第一时间发现的。我们要重视用户的反馈,找到和数据相关的问题。

  • 日志分析(主要):我们可以通过查看数据库日志和操作系统日志等方式找出异常情况,通过它们来定位遇到的问题。

  • 服务器资源使用监控:通过监控服务器的CPU、内存、I/O等使用情况,可以实时了解服务器的性能使用,与历史情况进行对比。

  • 数据库内部状况监控:在数据库的监控中,活动会话(Active Session)监控 是一个重要的指标。通过它,你可以清楚地了解数据库当前是否处于非常繁忙的状态,是否存在 SQL 堆积等。

  • 其它:除了活动会话监控以外,我们也可以对事务、锁等待等进行监控,这些都可以帮助我们对数据库的运行状态有更全面的认识。

MySQL监控分析视图-sys schema

  1. 主机相关:以host_summary开头,主要汇总了IO延迟的信息。
  2. Innodb相关:以innodb开头,汇总了innodb buffer信息和事务等待innodb锁的信息。
  3. I/o相关:以io开头,汇总了等待I/O、I/O使用量情况。
  4. 内存使用情况:以memory开头,从主机、线程、事件等角度展示内存的使用情况
  5. 连接与会话信息:processlist和session相关视图,总结了会话相关信息。
  6. 表相关:以schema_table开头的视图,展示了表的统计信息。
  7. 索引信息:统计了索引的使用情况,包含冗余索引和未使用的索引情况。
  8. 语句相关:以statement开头,包含执行全表扫描、使用临时表、排序等的语句信息。
  9. 用户相关:以user开头的视图,统计了用户使用的文件I/O、执行语句统计信息。
  10. 等待事件相关信息:以wait开头,展示等待事件的延迟情况。

微服务

注册中心、配置中心、SpringCloud Gateway、Spring Cloud Security、Spring Cloud OpenFeign、Spring Cloud LoadBalancer、Spring Cloud Circuit breaker(两种实现Resilience4J、Spring Retry)

Stream 和 Bus

https://blog.csdn.net/qq15035899256/article/details/129972696

SpringCloud Stream:它是通过对消息中间件进行抽象封装,提供一个统一的接口供我们发送和监听消息。主要功能:实现消息队列的相关操作,消息的异步调用能让各个服务充分解耦且更加灵活。

SpringCloud Bus:它是在 Stream 基础之上再次进行抽象封装,使得我们可以在不用理解消息发送、监听等概念的基础上使用消息来完成业务逻辑的处理。主要功能:借助消息驱动来实现将消息(事件)广播到各个服务中,让服务对这些消息进行消费。

注意

为了缩短实时链路,不必要的操作通过消息传递。

Redis

https://blog.zhaolq.com/article/2020/06/Redis%E6%95%99%E7%A8%8B/

数据库缓存、分布式session、分布式系统集中限流、全局ID

分布式锁:string。SETNX不存在则设置KeyValue

排行榜:zset。按分数排序和检索)

消息队列:list。提供的阻塞命令 blpop、brpop,

点赞、签到、打卡:set。微博ID是t1001,用户ID是u3001,用 like:t1001 来维护 t1001 这条微博的所有点赞用户

  • 点赞了这条微博:sadd like:t1001 u3001
  • 取消点赞:srem like:t1001 u3001
  • 是否点赞:sismember like:t1001 u3001
  • 点赞的所有用户:smembers like:t1001
  • 点赞数:scard like:t1001

MQ

通常被用来解决在高并发压力下类似于 异步处理、应用解耦、流量削锋、消息通讯、最终消息一致性 等这样的问题。

异步处理:用户注册时发送邮件和短信。传统的模式:用户下单—>邮件发送—>短信提醒,三个步骤全部完成,才能返回用户消费成功,因为后面两个步骤完全没有必须是当前时间完成,可以用户下单成功后,直接发送给mq,之后邮件发送和短信提醒,可以其他时间段来消费发送给用户。

应用解耦(事务拆分):订单系统通知库存系统扣减库存。库存系统需要收到订单后库存减一,这时候如果库存系统宕机,会造成订单丢失,把订单消息发入mq,库存系统再去mq消费,就能解决这一问题。

流量削锋:大型双11活动时候,0点有上亿并发,这时候数据库并不能承载那么大的数据冲击,而专门为高并发设计的mq可以承受住海量的请求,发送给mq,存储成功后,再消费。

消息通讯:微信单人发消息、QQ聊天群

最终消息一致性:订单和库存,银行转账

集中日志搜集和分析:

秒杀场景,异步扣减库存解决超卖的问题

1、限流
2、将库存放到redis中、接收用户请求的时候。从redis取库存,判断库存量是否大于本次订单购买量
库存大于本次购买量:扣减redis中的库存、并且将订单信息推送到MQ;
库存小于本次购买量:直接返回、数量不足。
3、MQ消费者获取消息:
1):更新数据库库存(乐观锁)
2):生成订单信息,扣除用户账户的订单金额(余额不足的话、将本次购买量加回到库存里)
3):异步通知用户购买结果。

Redis只是解决性能问题,数据库才是解决库存一致性问题

场景

异步通信

有些业务不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

解耦

降低工程间的强依赖程度,针对异构系统进行适配。在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

冗余

有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。

扩展性

因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。便于分布式扩容。

过载保护

在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提取预知;如果以为了能处理这类瞬间峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。

可恢复性

系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

顺序保证

在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。

缓冲

在任何重要的系统中,都会有需要不同的处理时间的元素。消息队列通过一个缓冲层来帮助任务最高效率的执行,该缓冲有助于控制和优化数据流经过系统的速度。以调节系统响应时间。

数据流处理

分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择。

选型

RabbitMQ,RocketMQ,Kafka

https://support.huaweicloud.com/productdesc-rabbitmq/rabbitmq-pd-190828005.html

https://blog.51cto.com/knifeedge/5011115

解决雪崩问题的常见方案有哪些?

架构之高并发:降级和熔断:https://pdai.tech/md/arch/arch-y-reduce.html

服务降级:手动开关降级(实时生效)、熔断降级、限流降级

  • 请求限流:限制流量在服务可以处理的范围,避免因突发流量而故障
  • 线程隔离:控制业务可用的线程数量,将故障隔离在一定范围
  • 失败处理:定义fallback逻辑,让业务失败时不再抛出异常,而是走fallback逻辑
  • 服务熔断:将异常比例过高的接口断开,拒绝所有请求,直接走fallback

业务量动态扩容或者缩容

运维工作

开发建表以后运维这边审核建表是否规范(仅数据库规范方面,不含业务);

测试工作

转测内容如果包含存储过程要在转测邮件中提及,由运维审核存储过程执行效率。

测试环节需进行性能测试,争取把性能问题消灭在测试环节。

已上生产的执行慢的接口,挨个整改。

性能监控

应用性能监控系统

  • 可以跟踪从接收请求到响应的全流程

  • 可以展示某一接口在某个时间段内的访问情况,包括接口所在地域,访问时间,响应时间,响应状态等

  • 可以展示某一接口的完整调用链。其中有

    a、该接口调用的所有服务,包括调用其他服务,http请求其他接口,访问数据库等;

    b、该接口每个调用过程所耗费时间;

  • 可以捕捉到执行的完整sql语句

社区是否有这样的软件,只需要我们在Java中将它需要的访问数据插入数据库?

高并下数据一致性方案

解决高并发场景下数据一致性的方案有两种

​ 【延时双删策略】:在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。
​ 先删除缓存,再写库,休眠500ms(考虑业务逻辑耗时、redis和数据库主从同步耗时等),再次删除缓存
​ 【双删策略+缓存超时设置】的最差情况:1、在缓存过期时间内发生数据存在不一致。2、同时又增加了写请求的耗时。
​ 【异步更新缓存】:利用MySQL binlog进行增量订阅消费(消息队列将增量数据更新到redis上)
​ 从redis读取热点数据;增删改都在MySQL;redis更新通过消息队列和MySQL的binlog
​ MySQL的主从备份机制也是通过binlog来实现数据一致性
​ 设置缓存的过期时间是保证数据一致性的关键操作,需结合业务进行合理设置。

玩转spring全家桶笔记

JDBC

Spring事务抽象,7种事务传播类型。

事务隔离级别,Spring默认隔离级别取决于数据库。

mysql高级篇看完,包含锁机制

分布式理论

分布式系统 - 知识体系详解:https://pdai.tech/md/arch/arch-z-overview.html

Spring JDBC 异常抽象:https://blog.51cto.com/u_10448399/2517990

O/R Mapping实践

  • 让MyBatis更好用的那些工具:MyBatis Generator
  • 让MyBatis更好用的那些工具:MyBatis PageHelper
  • 让MyBatis更好用的那些工具:MyBatis Mapper https://mapper.mybatis.io/

货币金额的开源Java类库:Joda-Money

mybatis分页原理,为什么mapper中的 @Param(“pageNum”) 和 @Param(“pageSize”) 可以被使用?为什么 PageHelper.startPage(pageNum, pageSize); 会生效?为什么 RowBounds 会自动分页?

NoSQL实践

Spring的缓存抽象

Redis:库存仅使用master读写,避免salve延迟同步发生超卖

Spring核心技术之ICO容器

https://docs.spring.io/spring-framework/reference/core.html

Spring IoC 容器和 Bean 简介

https://docs.spring.io/spring-framework/reference/core/beans/introduction.html

org.springframework.beans 和包 org.springframework.context 是 Spring Framework IoC 容器的基础。 BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。 ApplicationContextBeanFactory 的子接口。

简而言之,BeanFactory 提供了配置框架和基本功能,而 ApplicationContext 添加了更多企业特定的功能。ApplicationContext 是 BeanFactory 的完整超集,通常说的 Spring IoC 容器 指的是 ApplicationContext 。

在 Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 bean。bean 是一个由 Spring IoC 容器实例化、组装和管理的对象。否则,bean 只是应用程序中的众多对象之一。 Bean 以及它们之间的依赖关系反映在容器使用的配置元数据中。

容器概述

https://docs.spring.io/spring-framework/reference/core/beans/basics.html

org.springframework.context.ApplicationContext 接口代表 Spring IoC 容器,通过读取配置元数据实现 bean的实例化、配置和组装。配置元数据可以表示为带注释的组件类、具有工厂方法的配置类或外部 XML 文件或 Groovy 脚本。

ApplicationContext 接口的几个实现是 Spring 核心的一部分。在独立应用程序中,通常创建 AnnotationConfigApplicationContextClassPathXmlApplicationContext 的实例。

一个spring应用可以有多个上下文,创建 ApplicationContext 时可以指定父级(不同于Java中的继承)。对于许多Web应用程序来说,拥有一个 WebApplicationContext 足够了。SpringApplication.run() 中调用了 createApplicationContext(),它会根据枚举类 WebApplicationType 创建合适的上下文。

Spring Web MVC之DispatcherServlet

https://docs.spring.io/spring-framework/reference/web/webmvc.html

DispatcherServlet (Front Controller) 使用 Spring 配置来发现它所需的委托组件,以进行请求映射、视图解析、异常处理。

image-20240810121542628

上下文层次结构

https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet/context-hierarchy.html

特殊Bean类型

https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet/special-bean-types.html

DispatcherServlet 委托特殊的 bean 来处理请求并呈现适当的响应。所谓的“特殊 bean”是指实现框架契约的 Spring 管理的对象实例。这些通常带有内置合约,但您可以自定义它们的属性并扩展或替换它们。

源码

参考视频,以下是简单的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package org.springframework.web.servlet;

public class DispatcherServlet extends FrameworkServlet {
// 此 servlet 使用的 HandlerMapping 列表。
@Nullable
private List<HandlerMapping> handlerMappings;

// 暴露 DispatcherServlet 特定的请求属性,并委托 doDispatch 进行实际调度。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 一些Attribute的赋值

try {
doDispatch(request, response);
}
}

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
processedRequest = checkMultipart(request);
try {
try {
// 确定当前请求的处理程序。
mappedHandler = getHandler(processedRequest);
// 确定当前请求的处理程序适配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 已注册拦截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//
applyDefaultViewName(processedRequest, mv);
// 已注册拦截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
// 处理handler的调用结果,该结果要么是 ModelAndView,要么是要解析为 ModelAndView 的异常。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
}

Controller注解

https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

Handler Methods

@RequestMapping处理程序方法具有灵活的签名,可以从一系列受支持的控制器方法参数和返回值中进行选择。

Type Conversion

Spring 根据配置的转换器自动应用类型转换。默认情况下,支持简单类型(int、long、Date 等)。您可以通过 WebDataBinder 或 通过使用 FormattingConversionService 注册 Formatters 来自定义类型转换。 Spring Field Formatting

View

https://docs.spring.io/spring-framework/reference/web/webmvc-view.html

https://docs.spring.io/spring-framework/reference/web/webmvc-view/mvc-jackson.html