Spring
This is a custom summary and does NOT appear in the post.
Table of Contents
站在巨人的肩膀上, 多看看廖雪峰手写Sping以便了解Spring的核心逻辑思想
自己只记录一些自己觉得有用的小Tip
I. ENVIRONMENT
A. R&D and Testing Env
Use IDE of “InteliJ IDEA”, and download JDK wihin IDEA
1. Backup and Sync
同步之后, 大部分的配置和插件都可以同步下来, 以下仅作备考
Configuration
- Enable Backup and Sync ->
Enable Backup and Sync
- 在各个项目中分别选择Java JDK环境, 不安装Java在裸机
- Settsings -> Editor -> Code Style
- General tab, 选择 Line separator: Unix and macOS(\n)
- SQL -> General, 将
keywords
和Built-in types
设置为大写(To upper)
Plugins
- IdeaVim
- 在IDEA中创建
~/.ideavimrc
文件(实际创建在C:\Users\Ivan\.ideavimrc
) - 只需在最后一行增加配置引用即可
source //wsl.localhost/Ubuntu/root/.vimrc
, 已经可以使用WSL中的配置文件了 - 配置完, 需要退出并重新打开IDEA
- IDEA的vim插件, 从插入模式退出到普通模式后, 自动切换为英文输入法, 暂时没搞定, Windows VisualStudio的VsVim插件倒是自动有这个配置, 暂时没琢磨明白
- 在IDEA中创建
MyBatisCodeHelperPro(貌似有官方版, 下次试试)- Redis
- RemoteHost
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar pear-admin-pro-1.11.9-SNAPSHOT.jar
Additionally, record more Java in MicroSoft Windows 10/11 as below:
- 在系统环境变量中增加一条, 以防止在不同环境下的命令行输入乱码
- 变量名称:
JAVA_TOOL_OPTIONS
- 值:
-Duser.language=en
- 变量名称:
B. Product Env and Fixing Bugs
Install the jdk-21_windows-x64_bin.exe
on MicroSoft Windows, or apt install openjdk-21-jdk
on Ubuntu Service
And make sure the right $PATH
on any kind of system
使用公司的开发服务器作为部署服务器, 与客户生产环境保持一样的数据库结构和代码打包
如果遇到生产环境的问题, 使用该环境用于验证, 备份和修复
C. Demo Env
Install the jdk-21_windows-x64_bin.exe
on MicroSoft Windows, or apt install openjdk-21-jdk
on Ubuntu Service
And make sure the right $PATH
on any kind of system
部署在云上, 使用域名, 仅作对外演示, 使用当前的生产版本
II. PACKAGE MANAGEMENT
A. First of All
Maven 3.8.1 blocked http connection
-
Do NOT edit this original IDEA maven settings file
C:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\plugins\maven\lib\maven3\conf\settings.xml
settings.xml 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24... <mirrors> <!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | <mirror> <id>mirrorId</id> <mirrorOf>repositoryId</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://my.repository.com/repo/path</url> </mirror> --> <mirror> <id>maven-default-http-blocker</id> <mirrorOf>external:http:*</mirrorOf> <name>Pseudo repository to mirror external repositories initially using HTTP.</name> <url>http://0.0.0.0/</url> <blocked>true</blocked> </mirror> </mirrors> ...
-
Find personal maven setting path in IDEA settings and DIY it
C:\Users\ivan\.m2\settings.xml
(If not exists, create this file)settings.xml 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<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 http://maven.apache.org/xsd/settings-1.2.0.xsd"> <mirrors> <!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | <mirror> <id>mirrorId</id> <mirrorOf>repositoryId</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://my.repository.com/repo/path</url> </mirror> --> <mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors> </settings>
-
Reload pom.xml file in IDEA and automaticlly download the dependencies
B. How to use maven
III. JVM
TODO: Some basic JVM knowledge
IV. IOC
A. LifeCircle
@Configuration
https://www.cnblogs.com/java-chen-hao/p/11640177.html
V. AOP
JetBrains IDEA 中如果想了解某个注解的实现, 没有太好的办法, 就像想查找一个类在哪里被反射并调用了, 试行的办法:
- 阅读大厂的源码, 文档, 了解该注解的内部实现逻辑
- 小厂的代码, 只能是全文查找类似这样的
getAnnotation(DictFormat.class)
代码
A. HTTP 请求注解
- [TODO]: 各种继承关系的注解, 类似RestController, 包含了ResponseBody等, 后续补充
- Controller中设置Headers, 指定Content-Type/Accept
- Springboot中Controller中的comsumes所指定的是HTTP客户端的Content-Type的内容, 默认application/x-www-form-urlencoded
- Springboot中Controller中的produces所指定的是HTTP客户端的Accept的内容
常见的http头部Content-Type:
- application/x-www-form-urlencoded
- multipart/form-data
- application/json
- application/xml
前后组合如下:
- 服务端设置`Content-Type:"application/json"`并返回给前端, 前端dataType指定"json"
可以解码HTTP的body内容
- 服务端设置`Content-Type:application/json`并返回给前端, 前端dataType不指定"json"
可以解码HTTP的body内容, data类型是Object
- 服务端不设置`Content-Type:application/jsonResponse`并返回给前端, 前端dataType指定"json"
可以解码HTTP的body内容,data类型是Object
- 服务端不设置`Content-Type:application/json`并返回给前端, 前端dataType不指定"json"
不能解码HTTP的body内容, data类型是String
B. 易混淆注解的对比
@Component @Bean @Service @Configurable, With this annotation, a class can be scaned manually or automaticlly
@Bean
,
@Component
与@Service
没有区别, @Service
还没想好, 是一个预留的注解定义
@Configurable
是单例模式, 在启动时加载
@Resource @Autowired
Both @Autowired (or @Inject) and @Resource work equally well. But there is a conceptual difference or a difference in the meaning
@Resource means get me a known resource by name. The name is extracted from the name of the annotated setter or field, or it is taken from the name-Parameter.
@Inject or @Autowired try to wire in a suitable other component by type.
So, basically these are two quite distinct concepts. Unfortunately the Spring-Implementation of @Resource has a built-in fallback, which kicks in when resolution by-name fails. In this case, it falls back to the @Autowired-kind resolution by-type.
While this fallback is convenient, IMHO it causes a lot of confusion, because people are unaware of the conceptual difference and tend to use @Resource for type-based autowiring.
@Repository @Mapper
- @Mapper 一定要有,否则 Mybatis 找不到 mapper。
- @Repository 可有可无,可以消去依赖注入的报错信息。
- @MapperScan 可以替代 @Mapper。
@Validate @Valid
- @Validate是org.springframework.validation.annotation.Validated 导入的
- @Validate 可以分组
- @Valid是javax.validation.Valid 导入的
- @Valid 可以递归
- controller类上写: @Validated
- 如果是Bean的对象xxxRequest类限制参数, 则参数类中各自校验; 在controller类中的方法参数括号内写: @Valid
- 如果是属性String number限制, 在controller类中的方法入参前写: @NotBlank(message = “xxx不能为空”)
PS:
@RequestParam()不那么好用, 返回信息不友好
C. @Transactional
详见 DAO Mapper章节
VI. SERVLET
VII. SECURITY
以下大概描述的是Java Security的主要组件结构, 即使遗忘了实现细节, 掌握其结构和机制, 就容易理解得多
Configure 这里包含初始化, 注册哪些filter
Filter 具体拦截, 并把相对应的且support()的provider传进去
Provider
提供自己的认证比对实现方法, 并且实现support()
满足filter查找
Handler 增加成功与失败的handler
VIII. JOB
Quartz
IX. THIRD-PART HTTP API
RestTemplate 是从Spring3.0开始支持的一个远程HTTP请求工具,RestTemplate提供了常见的Rest服务(Rest风格、Rest架构)的客户端请求的模版,能够大大提高客户端的编写效率
多年来,Spring框架的RestTemplate一直是客户端HTTP访问的首选解决方案,它提供了同步、阻塞API以简洁的方式处理HTTP请求。然而,随着对非阻塞、反应式编程以更少的资源处理并发的需求不断增加,特别是在微服务架构中,RestTemplate已经显露出其局限性。 从Spring Framework 5开始,RestTemplate已被标记为已弃用,Spring团队推荐WebClient作为其继任者。在这篇文章中,我们将通过实际示例深入探讨RestTemplate被弃用的原因、采用WebClient的优势以及如何有效过渡。
Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection. NOTE: As of 6.1, RestClient offers a more modern API for synchronous HTTP access. For asynchronous and streaming scenarios, consider the reactive org. springframework. web. reactive. function. client. WebClient.
传统应用: 新的RestClient
流式应用: 才考虑WebClient
X. EVENT MONITOR
ApplicationEvent Monitor module-bpm模块中有用到, 拿来主义, 上游入库后"生产"出下游库存就依靠这个监听者观察客户定制的BOM原料是否满足, 满足即可生产 [TODO]:
XI. DAO MAPPER
把事务传播放在这里, 主要是借鉴了ChatGPT的解答
A. Transactional
1. Propagation
-
事务传播, 需要时则加入传播的扩散要求
@Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW) -
何时该扩散, 内层事务如何处理, 可以组合考虑
2. Isolation
以下是 MySQL 事务隔离级别的简要场景及效果区别:
-
Read Uncommitted
场景: 允许读取未提交的数据。
效果:
脏读: 可能发生,读取其他事务未提交的数据。
不可重复读: 可能发生,因为事务间可能修改已读取数据。
幻读: 可能发生,因为可能插入新数据。
-
Read Committed
场景: 只读取已提交的数据,避免读取未提交事务的内容。
效果:
脏读: 不会发生,保证读取的是已提交数据。
不可重复读: 可能发生,因为同一事务中重复读取时,数据可能被其他事务修改。
幻读: 可能发生,因为其他事务可能插入新记录。
Tips: 不可重复读重点在于update和delete
-
Repeatable Read (MySQL默认) 场景: 保证同一事务中,读取的结果是固定的,即便其他事务修改了数据。
效果:
脏读: 不会发生。
不可重复读: 不会发生,事务中数据一致。
幻读: 通过间隙锁避免,查询范围内的数据是固定的。Tips: 该级别产生幻读的重点场景在于insert, 总的来说就是事务A对数据进行操作,事务B还是可以用insert插入数据的,因为使用的是行锁,这样导致的各种奇葩问题就是幻读,表现形式很多
TODO: MySQL的间隙锁在开源代码中是否已经默认被应用
-
Serializable 场景: 强制事务串行化,确保事务间完全隔离。
效果:
脏读: 不会发生。
不可重复读: 不会发生。
幻读: 不会发生,因为会对范围内的操作加锁,阻止插入或修改。 -
总结表格
Isolation Level | 脏读(Dirty Read) | 不可重复读(Non Repeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
Read Uncommitted | ✅ | ✅ | ✅ |
Read Committed | ❌ | ✅ | ✅ |
Repeatable Read | ❌ | ❌ | ❌ |
Serializable | ❌ | ❌ | ❌ |
**锁防止别的事务修改或删除,GAP锁防止别的事务新增,**行锁和 GAP锁结合形成的的 Next-Key锁共同解决了 RR级别在写数据时的幻读问题。
十一、Serializable
这个级别很简单,读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,实现简单,数据更加安全,但是并发能力非常差。如果你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可以使用这种模式。
这里要吐槽一句,不要看到 select就说不会加锁了,在Serializable这个级别,还是会加锁的!
数据库事务、隔离级别和锁ACID的真实含义隔离级别和并发控制MySQL和PostgreSQL对比如何写代码略读
B. JDBC
Once a connection is obtained we can interact with the database. The JDBC Statement, CallableStatement, and PreparedStatement interfaces define the methods and properties that enable you to send SQL or PL/SQL commands and receive data from your database.
They also define methods that help bridge data type differences between Java and SQL data types used in a database.
The following table provides a summary of each interface’s purpose to decide on the interface to use.
Interfaces | Recommended Use |
---|---|
Statement | Use this for general-purpose access to your database. Useful when you are using static SQL statements at runtime. The Statement interface cannot accept parameters. |
PreparedStatement | Use this when you plan to use the SQL statements many times. The PreparedStatement interface accepts input parameters at runtime. |
CallableStatement | Use this when you want to access the database stored procedures. The CallableStatement interface can also accept runtime input parameters. |
XII. UTILS
很多源码中总结的Utils很好用, 多学习一下
判断类实例中的各个属性是否有null
1 |
|
兼容多种时间格式的输入
- util
- invoke
1 |
|
XIII. MISCELLANEOUS
EXCEPTION
TryCatchFinally
首先看个例子:
- return2
- return3
1 |
|
查阅资料得到下面的说法:
当try语句退出时肯定会执行finally语句。这确保了即使发了一个意想不到的异常也会执行finally语句块。 但是finally的用处不仅是用来处理异常——它可以让程序员不会因为return、continue、或者break语句而忽略了清理代码。 把清理代码放在finally语句块里是一个很好的做法,即便可能不会有异常发生也要这样做。
注意,当try或者catch的代码在运行的时候,JVM退出了。那么finally语句块就不会执行。同样,如果线程在运行try或者catch的代码时被中断了或者被杀死了(killed),那么finally语句可能也不会执行了,即使整个运用还会继续执行。
说明:如果在try语句里有return语句,finally语句还是会执行。它会在把控制权转移到该方法的调用者或者构造器前执行finally语句。也就是说,使用return语句把控制权转移给其他的方法前会执行finally语句。
那么结果为什么是3不是2呢?
当执行到return ++x;
时,jvm在执行完 ++x 后会在局部变量表里另外分配一个空间来保存当前x的值。
现在还没把值返回给y,而是继续执行finally语句里的语句。等执行完后再把之前保存的值**(是2, 不是x)**返回给y。所以就有了y是2不是3的情况。
其实这里还有一点要注意的是,如果你在finally里也用了return语句,比如return +xx;
, 那么y会是3。
因为规范规定了,当try和finally里都有return时,会忽略try的return,而使用finally的return。
原文链接:https://blog.csdn.net/wujingjing_crystal/article/details/52495189
THREAD
ThreadLocal
Thread Lock
一、ReentrantLock是用来做什么的? 二、ReentrantLock的实现原理是什么? 三、ReentrantLock对比于Synchronized有哪些优缺点? 四、ReentrantLock的简单使用
一、ReentrantLock是用来做什么的? 这个类是JUC工具包中对线程安全问题提供的一种解决方案,它主要是用来给对象上锁,保证同一时间这能有一个线程在访问当前对象。这样处理是为了防止如果一个线程对某个公共变量进行了改变,而其它线程读取时读出来的是原有数据导致脏读的问题。
二、ReentrantLock的实现原理是什么? ReentrantLock主要是通过同步队列和CAS机制来实现的,它实现的过程中主要包含下面几个属性:
status:锁状态,0表示没有线程获取锁,1表示已有线程获取锁 exclusiveOwnerThread:当前持有锁的线程 Node:节点,是ReentrantLock内部维持的一个双向链表(同步阻塞队列)的基本构成 具体流程
1、上锁更新锁状态:当A线程持有锁时,会通过CAS将status状态置为1,并将A线程自身存入exclusiveOwnerThread属性当中; 2、线程入队:而后线程B通过CAS获取锁时发现无法获取锁,此时就会获取node信息,但是由于node双向链表是null所以会通过CAS来创建一个双向链表的head对象,之后再把线程B封装成的Node节点通过尾插法接入双向链表的尾部; 3、线程阻塞:入队完成后再调用park方法进行阻塞; 4、释放锁并更新锁状态:当A线程释放锁时会将status属性重置为0,且把exclusiveOwnerThread置为null; 5、唤醒线程:A线程释放锁完毕后会调用unpark方法来唤醒双向链表中下一节点的线程B;
三、ReentrantLock对比于Synchronized有哪些优缺点? 1、ReentrantLock的锁状态是可见的,而Synchronized的锁状态是不可见的; 2、ReentrantLock是JDK层面的实现,而Synchronized是JVM层面的实现; 3、ReentrantLock需要手动释放锁,而Synchronized不需要手动释放锁; 4、ReentrantLock可以是非公平锁也可以是公平锁,而Synchronized只能是非公平锁; 5、ReentrantLock是可被中断的,而Synchronized是不可被中断的; 6、Synchronized在特定条件下是后来的线程先获取锁,而ReentrantLock是先来的线程先获取锁; 四、ReentrantLock的简单使用