提交 2e8fd246 编写于 作者: 沉默王二's avatar 沉默王二 💬

IO

上级 645a7b7d
......@@ -152,9 +152,11 @@
- [详解 Java 中的优先级队列(PriorityQueue 附源码分析)](docs/collection/PriorityQueue.md)
- [Java WeakHashMap详解(附源码分析)](docs/collection/WeakHashMap.md)
## Java输入输出
## Java IO
- [Java IO学习整理](docs/io/shangtou.md)
- [看完这篇,Java IO 不再混乱!](docs/io/shangtou.md)
- [详解 File、Path、Paths、Files 四个类,操作文件不再难](docs/io/file-path.md)
- [文件的世界,一切皆字节(Stream)](docs/io/byte.md)
- [如何给女朋友解释什么是 BIO、NIO 和 AIO?](docs/io/BIONIOAIO.md)
......
......@@ -147,14 +147,9 @@ export const sidebarConfig = sidebar({
collapsable: true,
prefix:"io/",
children: [
{
text: "概览",
link: "shangtou",
},
{
text: "BIO、NIO和AIO",
link: "BIONIOAIO",
},
"shangtou",
"file-path",
"BIONIOAIO",
],
},
{
......
---
title: 文件的世界,一切皆字节(Stream)
shortTitle: 文件的世界,一切皆字节
category:
- Java核心
tag:
- Java IO
description: Java程序员进阶之路,小白的零基础Java教程,文件的世界,一切皆字节 OutputStream、InputStream
head:
- - meta
- name: keywords
content: Java,Java SE,Java 基础,Java 教程,Java 程序员进阶之路,Java 入门,Java IO,java stream,java OutputStream,java InputStream
---
我们必须得明确一点,一切文件数据(文本、图片、视频等)都是以二进制的形式存储的,传输时也如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终是二进制数据。
## 2.2 字节输出流(OutputStream)
`java.io.OutputStream` 抽象类是表示**字节输出流**的所有类的**超类**(父类),将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法,不要问我`OutputStream`为啥可以定义字节输出流的基本共性功能方法,熊dei啊,上一句说过了**OutputStream是字节输出流的所有类的超类**[继承](https://blog.csdn.net/qq_44543508/article/details/102375196)知识,懂?(如果是真的不理解的小白同学,可以点击蓝色字体[继承](https://blog.csdn.net/qq_44543508/article/details/102375196)进入补习)
**字节输出流的基本共性功能方法**:
> 1、 `public void close()` :关闭此输出流并释放与此流相关联的任何系统资源。
>
> 2、 `public void flush()` :刷新此输出流并强制任何缓冲的输出字节被写出。
>
> 3、 `public void write(byte[] b)`:将 b.length个字节从指定的字节数组写入此输出流。
>
> 4、 `public void write(byte[] b, int off, int len)` :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。 **也就是说从off个字节数开始读取一直到len个字节结束**
>
> 5、 `public abstract void write(int b)` :将指定的字节输出流。
> **以上五个方法则是字节输出流都具有的方法,由父类OutputStream定义提供,子类都会共享以上方法**
#### FileOutputStream类
`OutputStream`有很多子类,我们从最简单的一个子类FileOutputStream开始。看名字就知道是文件输出流,用于将数据写出到文件。
#### FileOutputStream构造方法
不管学啥子,只有是对象,就从构造方法开始!
> 1、 `public FileOutputStream(File file)`:根据File对象为参数创建对象。
>
> 2、 `public FileOutputStream(String name)`: 根据名称字符串为参数创建对象。
**推荐第二种构造方法**【开发常用】:
```javascript
FileOutputStream outputStream = new FileOutputStream("abc.txt");
```
就以上面这句代码来讲,类似这样创建字节输出流对象都做了**三件事情**
1、调用系统功能去创建文件【输出流对象才会自动创建】
2、创建outputStream对象
3、把foutputStream对象指向这个文件
> 注意:
>
> 创建输出流对象的时候,系统会自动去对应位置创建对应文件,而创建输出流对象的时候,文件不存在则会报FileNotFoundException异常,也就是系统找不到指定的文件异常。
当你创建一个流对象时,必须直接或者间接传入一个文件路径。比如现在我们创建一个`FileOutputStream`流对象,在该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。有兴趣的童鞋可以测试一下,具体代码如下:
```java
public class FileOutputStreamConstructor throws IOException {
public static void main(String[] args) {
// 使用File对象创建流对象
File file = new File("G:\\自动创建的文件夹\\a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("G:\\b.txt");
}
}
```
#### FileOutputStream写出字节数据
使用FileOutputStream写出字节数据主要通过`Write`方法,而`write`方法分如下三种
```javascript
public void write(int b)
public void write(byte[] b)
public void write(byte[] b,int off,int len) //从`off`索引开始,`len`个字节
```
1. **写出字节**`write(int b)` 方法,每次可以写出一个字节数据,代码如下:
```java
public class IoWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 写出数据
fos.write(97); // 写出第1个字节
fos.write(98); // 写出第2个字节
fos.write(99); // 写出第3个字节
// 关闭资源
fos.close();
}
}
输出结果
abc
```
> 1. 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。
> 2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。
2. **写出字节数组**`write(byte[] b)`,每次可以写出数组中的数据,代码使用演示:
```java
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "麻麻我想吃烤山药".getBytes();
// 写出字节数组数据
fos.write(b);
// 关闭资源
fos.close();
}
}
输出结果
麻麻我想吃烤山药
```
3. **写出指定长度字节数组**`write(byte[] b, int off, int len)` ,每次写出从`off`索引开始,`len`个字节,代码如下:
```java
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b,2,2);
// 关闭资源
fos.close();
}
}
输出结果
cd
```
#### FileOutputStream实现数据追加续写、换行
经过以上的代码测试,每次程序运行,每次创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续**追加新数据**呢?并且实现**换行**呢?其实很简单,这个时候我们又要再学习`FileOutputStream`的另外两个构造方法了,如下:
1、`public FileOutputStream(File file, boolean append)`
2、`public FileOutputStream(String name, boolean append)`
这两个构造方法,第二个参数中都需要传入一个boolean类型的值,`true` 表示追加数据,`false` 表示不追加也就是清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,至于Windows换行则是 `\n\r` ,下面将会详细讲到。
实现数据追加续写代码如下:
```java
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt"true);
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b);
// 关闭资源
fos.close();
}
}
文件操作前cd
文件操作后cdabcde
```
Windows系统里,换行符号是`\r\n` ,具体代码如下:
```java
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 定义字节数组
byte[] words = {97,98,99,100,101};
// 遍历数组
for (int i = 0; i < words.length; i++) {
// 写出一个字节
fos.write(words[i]);
// 写出一个换行, 换行符号转成数组写出
fos.write("\r\n".getBytes());
}
// 关闭资源
fos.close();
}
}
输出结果
a
b
c
d
e
```
> * 回车符`\r`和换行符`\n` :
> * 回车符:回到一行的开头(return)。
> * 换行符:下一行(newline)。
> * 系统中的换行:
> * Windows系统里,每行结尾是 `回车+换行` ,即`\r\n`;
> * Unix系统里,每行结尾只有 `换行` ,即`\n`;
> * Mac系统里,每行结尾是 `回车` ,即`\r`。从 Mac OS X开始与Linux统一。
## 2.3 字节输入流(InputStream)
`java.io.InputStream` 抽象类是表示**字节输入流**的所有类的**超类**(父类),可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
**字节输入流的基本共性功能方法**:
> 1、 `public void close()` :关闭此输入流并释放与此流相关联的任何系统资源。
>
> 2、`public abstract int read()`: 从输入流读取数据的下一个字节。
>
> 3、 `public int read(byte[] b)`: 该方法返回的int值代表的是读取了多少个字节,读到几个返回几个,读取不到返回-1
#### FileInputStream类
`java.io.FileInputStream` 类是文件输入流,从文件中读取字节。
#### FileInputStream的构造方法
> 1、 `FileInputStream(File file)`: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
>
> 2、 `FileInputStream(String name)`: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名name命名。
同样的,推荐使用第二种构造方法:
```javascript
FileInputStream inputStream = new FileInputStream("a.txt");
```
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出`FileNotFoundException`
构造举例,代码如下:
```java
public class FileInputStreamConstructor throws IOException{
public static void main(String[] args) {
// 使用File对象创建流对象
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);
// 使用文件名称创建流对象
FileInputStream fos = new FileInputStream("b.txt");
}
}
```
#### FileInputStream读取字节数据
1. **读取字节**`read`方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回`-1`,代码测试如下【read.txt文件中内容为abcde】:
```java
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");//read.txt文件中内容为abcde
// 读取数据,返回一个字节
int read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
// 读取到末尾,返回-1
read = fis.read();
System.out.println( read);
// 关闭资源
fis.close();
}
}
输出结果
a
b
c
d
e
-1
```
循环改进读取方式,代码使用演示:
```java
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 定义变量,保存数据
int b
// 循环读取
while ((b = fis.read())!=-1) {
System.out.println((char)b);
}
// 关闭资源
fis.close();
}
}
输出结果
a
b
c
d
e
```
2. **使用字节数组读取**`read(byte[] b)`,每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回`-1` ,代码使用演示:
```java
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // read.txt文件中内容为abcde
// 定义变量,作为有效个数
int len
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组变成字符串打印
System.out.println(new String(b));
}
// 关闭资源
fis.close();
}
}
输出结果
ab
cd
ed
```
由于`read.txt`文件中内容为`abcde`,而错误数据`d`,是由于最后一次读取时,只读取一个字节`e`,数组中,上次读取的数据没有被完全**替换**【注意是替换,看下图】,所以要通过`len` ,获取有效的字节
![](https://img-blog.csdnimg.cn/20191015160242904.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NTQzNTA4,size_16,color_FFFFFF,t_70)
代码如下:
```java
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
// 定义变量,作为有效个数
int len
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(b0len));// len 每次读取的有效字节个数
}
// 关闭资源
fis.close();
}
}
输出结果
ab
cd
e
```
在开发中一般强烈推荐使用数组读取文件,代码如下:
```javascript
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class input2 {
public static void main(String args[]){
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("a.txt");
int len = 0 ;
byte[] bys = new byte[1024];
while ((len = inputStream.read(bys)) != -1) {
System.out.println(new String(bys,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
```
#### 字节流FileInputstream复制图片
**复制图片原理**
![](https://img-blog.csdnimg.cn/20191013204020152.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NTQzNTA4,size_16,color_FFFFFF,t_70)
**代码实现**
复制图片文件,代码如下:
```java
public class Copy {
public static void main(String[] args) throws IOException {
// 1.创建流对象
// 1.1 指定数据源
FileInputStream fis = new FileInputStream("D:\\test.jpg");
// 1.2 指定目的地
FileOutputStream fos = new FileOutputStream("test_copy.jpg");
// 2.读写数据
// 2.1 定义数组
byte[] b = new byte[1024];
// 2.2 定义长度
int len;
// 2.3 循环读取
while ((len = fis.read(b))!=-1) {
// 2.4 写出数据
fos.write(b, 0 , len);
}
// 3.关闭资源
fos.close();
fis.close();
}
}
```
**注****复制文本、图片、mp3、视频等的方式一样**
到这里,已经从File类讲到了字节流OutputStream与InputStream,而现在将主要从字符流Reader和Writer的故事开展。
\ No newline at end of file
此差异已折叠。
---
title: 看完这篇,Java IO 不再混乱!
shortTitle: 看完这篇,Java IO不再混乱
category:
- Java核心
tag:
- Java
- Java IO
description: Java程序员进阶之路,小白的零基础Java教程,Java IO 体系看起来类很多,感觉很复杂,但其实是 IO 涉及的因素太多了。在设计 IO 相关的类时,编写者也不是从同一个方面考虑的,所以会给人一种很乱的感觉,并且还有设计模式的使用,更加难以使用这些 IO 类,所以特地对 Java 的 IO 做一个总结。
head:
- - meta
- name: keywords
content: Java,Java SE,Java 基础,Java 教程,Java 程序员进阶之路,Java 入门,Java IO
---
# Java IO学习整理
“老王,Java IO 也太上头了吧?”新兵蛋子小二向头顶很凉快的老王抱怨道,“你瞧,我就按照传输方式对 IO 进行了一个简单的分类,就能搞出来这么多的玩意!”
......@@ -16,7 +21,7 @@ tag:
看着肺都快要气炸的小二,老王深深地吸了一口气,耐心地对小二说:“主要是 Java 的设计者考虑得比较多吧,所以 IO 给人一种很乱的感觉,我来给你梳理一下。”
### 01、传输方式划分
## 01、传输方式划分
就按照你的那副思维导图来说吧。
......@@ -80,7 +85,7 @@ tag:
理解了上面这些方法,基本上 IO 的灵魂也就全部掌握了。
### 02、操作对象划分
## 02、操作对象划分
小二,你细想一下,IO IO,不就是输入输出(Input/Output)嘛:
......@@ -94,7 +99,7 @@ tag:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/io/shangtou-03.png)
**1)文件**
### **1)文件**
文件流也就是直接操作文件的流,可以细分为字节流(FileInputStream 和 FileOuputStream)和字符流(FileReader 和 FileWriter)。
......@@ -144,7 +149,7 @@ fileWriter.close();
当掌握了文件的输入输出,其他的自然也就掌握了,都大差不差。
**2)数组**
### **2)数组**
通常来说,针对文件的读写操作,使用文件流配合缓冲流就够用了,但为了提升效率,频繁地读写文件并不是太好,那么就出现了数组流,有时候也称为内存流。
......@@ -176,7 +181,7 @@ byte[] dest =bos.toByteArray();
bos.close();
```
**3)管道**
### **3)管道**
Java 中的管道和 Unix/Linux 中的管道不同,在 Unix/Linux 中,不同的进程之间可以通过管道来通信,但 Java 中,通信的双方必须在同一个进程中,也就是在同一个 JVM 中,管道为线程之间的通信提供了通信能力。
......@@ -219,7 +224,7 @@ thread1.start();
thread2.start();
```
**4)基本数据类型**
### **4)基本数据类型**
基本数据类型输入输出流是一个字节流,该流不仅可以读写字节和字符,还可以读写基本数据类型。
......@@ -251,7 +256,7 @@ das.writeBoolean(true);
das.writeChar('A');
```
**5)缓冲**
### **5)缓冲**
CPU 很快,它比内存快 100 倍,比磁盘快百万倍。那也就意味着,程序和内存交互会很快,和硬盘交互相对就很慢,这样就会导致性能问题。
......@@ -262,7 +267,7 @@ CPU 很快,它比内存快 100 倍,比磁盘快百万倍。那也就意味
缓冲流在内存中设置了一个缓冲区,只有缓冲区存储了足够多的带操作的数据后,才会和内存或者硬盘进行交互。简单来说,就是一次多读/写点,少读/写几次,这样程序的性能就会提高。
**6)打印**
### **6)打印**
恐怕 Java 程序员一生当中最常用的就是打印流了:`System.out` 其实返回的就是一个 PrintStream 对象,可以用来打印各式各样的对象。
......@@ -280,7 +285,7 @@ try (PrintWriter pw = new PrintWriter(buffer)) {
System.out.println(buffer.toString());
```
**7)对象序列化/反序列化**
### **7)对象序列化/反序列化**
序列化本质上是将一个 Java 对象转成字节数组,然后可以将其保存到文件中,或者通过网络传输到远程。
......@@ -302,7 +307,7 @@ try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(
```
**8)转换**
### **8)转换**
InputStreamReader 是从字节流到字符流的桥连接,它使用指定的字符集读取字节并将它们解码为字符。
......@@ -330,6 +335,10 @@ out.close() ;
---------
最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html)
关注二哥的原创公众号 **沉默王二**,回复**111** 即可免费领取。
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/xingbiaogongzhonghao.png)
......
此差异已折叠。
---
title: HR问你目前拿到哪几个offer了,该怎么回答?
shortTitle: HR问你目前拿到哪几个offer了,怎么回答?
shortTitle: HR问你目前拿到哪几个offer了,怎么回答?
tags:
- 优质文章
category:
......
---
title: 公司新来一个同事,把 @Transactional 事务注解运用得炉火纯青...
shortTitle: 公司新来一个同事,把 @Transactional 事务注解运用得炉火纯青...
shortTitle: 谈谈 @Transactional 的原理和坑
description: 谈谈 @Transactional 的原理和坑。
author: 楼仔
category:
......@@ -12,7 +12,7 @@ Java 后端面试的时候,面试官经常会问到 @Transactional 的原理
这篇文章,**会先讲述 @Transactional 的 4 种不生效的 Case,然后再通过源码解读,分析 @Transactional 的执行原理,以及部分 Case 不生效的真正原因。**
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopd8GibpH0Mf2MflkrxCOeuibLafv4Gd9tYChNFtFguFu44NwXP0pD1rBg/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-912703df-775a-48b9-abbb-0e951d9da1bf.jpg)
## 项目准备
......@@ -225,33 +225,33 @@ public void testMultThread() throws Exception {
> 红色方框有一段注释,大致翻译为 “它是一个拦截器,所以我们只需调用即可:在构造此对象之前,将静态地计算切入点。”
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopbs04cKPPJFDP7VecYRXQELIFwiaScflkna0v0Gia07G2vJ8cBgXUE9ug/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-3995e575-2cd3-4f62-80d9-c0fbba3c99b9.jpg)
this 是 ReflectiveMethodInvocation 对象,成员对象包含 UserController 类、testSuccess() 方法、入参和代理对象等。
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopVqnRtRiaOYgRgt8EuTcvpTa4w8qF1mXYRMsEuoyyj5WjUv9UegATqpw/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-47edf894-16e1-423b-a59a-cbb1bf1f5fba.jpg)
进入 invoke() 方法后:
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopM497ibd1wRP8Gws4NricpFj8RFp19XpU5Qcxk7bu3T5ehglVUkibSXqdQ/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-a4464977-3c8f-4800-8ffd-c4e458828b0e.jpg)
**前方高能!!!这里就是事务的核心逻辑,包括判断事务是否开启、目标方法执行、事务回滚、事务提交。**
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopM8OJcL69clRRX9d1KOWIGmBYROA8Rdl4m6Iu81ic8lZE03Zc8TDOPng/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-4b46998f-2c1a-4e7c-abc7-e34113ea33f3.jpg)
### private 导致事务不生效原因
在上面这幅图中,第一个红框区域调用了方法 getTransactionAttribute(),主要是为了获取 txAttr 变量,它是用于读取 @Transactional 的配置,如果这个 txAttr = null,后面就不会走事务逻辑,我们看一下这个变量的含义:
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopJggk7HaG9qCLvs30bab352r5KmDvXMfkF2riaXRXS2jVoaZ2Qg8X3Iw/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-d87a548a-cdf7-4027-8a8c-6e37a2cb908f.jpg)
我们直接进入 getTransactionAttribute(),重点关注获取事务配置的方法。
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopY7BlcjB0kLMHu6jo8G5icLBOIbDttx9lu10iadOW3R2Dg9g6P2BGtq0w/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-d5397df4-4a66-4187-92fe-6b7f991aff1a.jpg)
**前方高能!!!这里就是 private 导致事务不生效的原因所在**,allowPublicMethodsOnly() 一直返回 false,所以重点只关注 isPublic() 方法。
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopFkjibxDQiauLE9X8t7EWGkVUMoZmaqHILat6tmCuibVptQmdNAOQmKuLg/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-be1475dd-5e66-4685-9009-34c2bc1eed00.jpg)
下面通过位与计算,判断是否为 Public,对应的几类修饰符如下:
......@@ -259,7 +259,7 @@ this 是 ReflectiveMethodInvocation 对象,成员对象包含 UserController
* PRIVATE: 2
* PROTECTED: 4
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgop7ibzbiaBHUBRvETKc6crCEKDk766uLXxzvKLkYxicqg7p7az6sibhXz9Bw/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-5b4f2786-0507-4c14-af2b-79a492317bb0.jpg)
看到这里,是不是豁然开朗了,有没有觉得很有意思呢~~
......@@ -267,23 +267,23 @@ this 是 ReflectiveMethodInvocation 对象,成员对象包含 UserController
我们继续回到事务的核心逻辑,因为主方法抛出 Exception() 异常,进入事务回滚的逻辑:
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopHpoxhBiaWGeeZ95eo9FEVbIOqTwc9jYzfRYZQ45arQr2Q11Ms8ZQ5xQ/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-244514a1-f700-4781-aebe-cc6617c682e2.jpg)
进入 rollbackOn() 方法,判断该异常是否能进行回滚,这个需要判断主方法抛出的 Exception() 异常,是否在 @Transactional 的配置中:
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopyJMlEsvfdOAt3dpSQkenABSVgDBSAYyibibiatb7wBCNHmeMJt5DLbo7Q/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-32c3759b-7673-4a0a-8e6d-d9e4b0d5cd7d.jpg)
我们进入 getDepth() 看一下异常规则匹配逻辑,因为我们对 @Transactional 配置了 rollbackFor = Exception.class,所以能匹配成功:
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopsoFDrWJqhk8D18wTiah3JrKEj6TZ9yHo1YqticIKtPCySRGrhbLHjMqA/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-d4e16c96-45b4-49b6-bd5e-8061469b7aab.jpg)
示例中的 winner 不为 null,所以会跳过下面的环节。但是当 winner = null 时,也就是没有设置 rollbackFor 属性时,会走默认的异常捕获方式。
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopAV842D3iaJkPtq3oLq3GIeC8ElHSiaqxlhxnSwSTiauCZw2TSiaGpcaNrg/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-f08272b5-7365-47c1-a0a1-72307200c08a.jpg)
**前方高能!!!这里就是异常不匹配原因的原因所在**,我们看一下默认的异常捕获方式:
![](https://mmbiz.qpic.cn/mmbiz_png/sXFqMxQoVLEVXv0Hy3YPBibWjlhRHrgopwIWT3Vwo03zOTrLrUeTSvUlPmJxxHeJqWe3b8TkWdPWJw5rrvwKU5g/640?wx_fmt=png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-5cc091e2-bcd6-4d81-92ba-ee04509990b7.jpg)
是不是豁然开朗,**当没有设置 rollbackFor 属性时,默认只对 RuntimeException 和 Error 的异常执行回滚。**
......@@ -292,19 +292,19 @@ this 是 ReflectiveMethodInvocation 对象,成员对象包含 UserController
一个人可以走得很快,但一群人才能走得更远。欢迎加入[二哥的编程星球](https://mp.weixin.qq.com/s/e5Q4aJCX9xccTzBBGepx4g),里面的每个球友都非常的友善,除了鼓励你,还会给你提出合理的建议。星球提供的三份专属专栏《Java 面试指南》、《编程喵 🐱(Spring Boot+Vue 前后端分离)实战项目笔记》、《Java 版 LeetCode 刷题笔记》,干货满满,价值连城。
![](https://files.mdnice.com/user/3903/0c9e5f37-f702-4799-9a56-0ad87173e875.png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-3ad64454-3033-40f1-840a-8bd90880b065.png)
![](https://files.mdnice.com/user/3903/93ef3f1d-10f1-433a-932e-d5ba45cd61ee.png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-853616ff-76bd-40f6-9261-5e45285d3d56.png)
![](https://files.mdnice.com/user/3903/16199e4c-5e44-4924-abcb-46a6e1e1bc0f.png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-976002e6-741c-4948-a4a4-a32ec44a903a.png)
已经有 **670 多名** 小伙伴加入[二哥的编程星球](https://mp.weixin.qq.com/s/e5Q4aJCX9xccTzBBGepx4g)了,如果你也需要一个良好的学习氛围,[戳链接](https://mp.weixin.qq.com/s/e5Q4aJCX9xccTzBBGepx4g)加入我们的大家庭吧!这是一个 Java 学习指南 + 编程实战 + LeetCode 刷题的私密圈子,你可以向二哥提问、帮你制定学习计划、跟着二哥一起做实战项目,冲冲冲。
![](https://files.mdnice.com/user/3903/7df1a7b2-320e-410a-96fa-9000550419b9.png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-6938df3e-6bd3-423e-879a-2b4dafa86bee.png)
![](https://files.mdnice.com/user/3903/aca75f2f-6ade-40b2-93ce-668c41db5d2e.png)
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-gongsxlygtsbtransactionalswzxyydlhcq-3e6e49be-d833-4c4d-9e23-5d54f4132a9a.png)
* * *
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册