
??????教程全知識點(diǎn)簡介:基礎(chǔ)篇 1. 二分查找 2. 冒泡排序 7. ArrayList 8. Iterator 9. LinkedList 10. HashMap 1)基本數(shù)據(jù)結(jié)構(gòu) 2)樹化與退化 3)索引計(jì)算 4)put 與擴(kuò)容 5)并發(fā)問題 11. 單例模式 并發(fā)篇 1. 線程狀態(tài) 3. wait vs sleep 4. lock vs synchronized 虛擬機(jī)篇 1. JVM 內(nèi)存結(jié)構(gòu) 4. 內(nèi)存溢出 5. 類加載 6. 四種引用 7. finalize 框架篇 1. Spring refresh 流程 2. Spring bean 生命周期 6. Spring 注解 7. SpringBoot 自動(dòng)配置原理 數(shù)據(jù)庫篇 1. 隔離級別 2. 快照讀與當(dāng)前讀 3. InnoDB vs MyISAM 4. 索引 索引基礎(chǔ) 5. 查詢語句執(zhí)行流程 6. undo log 與 redo log 7. 鎖 緩存篇 1. Redis 數(shù)據(jù)類型 2. keys 命令問題 3. 過期 key 的刪除策略 5. 緩存問題 6. 緩存原子性 7. LRU Cache 實(shí)現(xiàn) 分布式篇 1. CAP 定理 2. Paxos 算法 4. Gossip 協(xié)議 5. 分布式通用設(shè)計(jì) 6. 一致性 Hash(補(bǔ)充)

????倉庫code.zip ??直接-->:???https://gitlab.com/yiqing112/backend/-/blob/main/Java/Java面試七大專題/note.md ???????
? 本教程項(xiàng)目亮點(diǎn)
?? 知識體系完整:覆蓋從基礎(chǔ)原理、核心方法到高階應(yīng)用的全流程內(nèi)容
?? 全技術(shù)鏈覆蓋:完整前后端技術(shù)棧,涵蓋開發(fā)必備技能
?? 從零到實(shí)戰(zhàn):適合 0 基礎(chǔ)入門到提升,循序漸進(jìn)掌握核心能力
?? 豐富文檔與代碼示例:涵蓋多種場景,可運(yùn)行、可復(fù)用
?? 工作與學(xué)習(xí)雙參考:不僅適合系統(tǒng)化學(xué)習(xí),更可作為日常開發(fā)中的查閱手冊
?? 模塊化知識結(jié)構(gòu):按知識點(diǎn)分章節(jié),便于快速定位和復(fù)習(xí)
?? 長期可用的技術(shù)積累:不止一次學(xué)習(xí),而是能伴隨工作與項(xiàng)目長期參考
??????全教程總章節(jié)

??????本篇主要內(nèi)容
3. Spring bean 循環(huán)依賴
要求
- 掌握單例 set 方式循環(huán)依賴的原理
- 掌握其它循環(huán)依賴的解決方法
循環(huán)依賴的產(chǎn)生
- 首先要明白,bean 的創(chuàng)建要遵循一定的步驟,必須是創(chuàng)建、注入、初始化三步,這些順序不能亂
<img src="https://upload-images.jianshu.io/upload_images/29644671-185a3cd1973ef8e0.png" alt="image-20210903085238916" style="zoom:50%;" />
-
set 方法(包括成員變量)的循環(huán)依賴如圖所示
可以在【a 創(chuàng)建】和【a set 注入 b】之間加入 b 的整個(gè)流程來解決
【b set 注入 a】 時(shí)可以成功,因?yàn)橹?a 的實(shí)例已經(jīng)創(chuàng)建完畢
a 的順序,及 b 的順序都能得到保障
<img src="" alt="image-20210903085454603" style="zoom: 33%;" />
- 構(gòu)造方法的循環(huán)依賴如圖所示,顯然無法用前面的方法解決
<img src="https://upload-images.jianshu.io/upload_images/29644671-c864197003dd29e7.png" alt="image-20210903085906315" style="zoom: 50%;" />
構(gòu)造循環(huán)依賴的解決
- 思路1
- a 注入 b 的對象,這樣能夠保證 a 的流程走通
- 后續(xù)需要用到 b 的真實(shí)對象時(shí),可以通過間接訪問
<img src="https://upload-images.jianshu.io/upload_images/29644671-3e87b7190042f7f0.png" alt="image-20210903091627659" style="zoom: 50%;" />
- 思路2
- a 注入 b 的工廠對象,讓 b 的實(shí)例創(chuàng)建被推遲,這樣能夠保證 a 的流程先走通
- 后續(xù)需要用到 b 的真實(shí)對象時(shí),再通過 ObjectFactory 工廠間接訪問
<img src="https://upload-images.jianshu.io/upload_images/29644671-a1710e4712de56e2.png" alt="image-20210903091743366" style="zoom:50%;" />
- 示例1:用 @Lazy 為構(gòu)造方法參數(shù)生成
public class App60_1 {
static class A {
private static final Logger log = LoggerFactory.getLogger("A");
private B b;
public A(@Lazy B b) {
log.debug("A(B b) {}", b.getClass());
this.b = b;
}
@PostConstruct
public void init() {
log.debug("init()");
}
}
static class B {
private static final Logger log = LoggerFactory.getLogger("B");
private A a;
public B(A a) {
log.debug("B({})", a);
this.a = a;
}
@PostConstruct
public void init() {
log.debug("init()");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("a", A.class);
context.registerBean("b", B.class);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.refresh();
System.out.println();
}
}
- 示例2:用 ObjectProvider 延遲依賴對象的創(chuàng)建
public class App60_2 {
static class A {
private static final Logger log = LoggerFactory.getLogger("A");
private ObjectProvider<B> b;
public A(ObjectProvider<B> b) {
log.debug("A({})", b);
this.b = b;
}
@PostConstruct
public void init() {
log.debug("init()");
}
}
static class B {
private static final Logger log = LoggerFactory.getLogger("B");
private A a;
public B(A a) {
log.debug("B({})", a);
this.a = a;
}
@PostConstruct
public void init() {
log.debug("init()");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("a", A.class);
context.registerBean("b", B.class);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBe
## 4. Spring 事務(wù)失效
**要求**
* 掌握事務(wù)失效的八種場景
**1. 拋出檢查異常導(dǎo)致事務(wù)不能正確回滾**
```java
@Service
public class Service1 {
@Autowired
private AccountMapper accountMapper;
@Transactional
public void transfer(int from, int to, int amount) throws FileNotFoundException {
int fromBalance = accountMapper.findBalanceBy(from);
if (fromBalance - amount >= 0) {
accountMapper.update(from, -1 * amount);
new FileInputStream("aaa");
accountMapper.update(to, amount);
}
}
}
原因:Spring 默認(rèn)只會回滾非檢查異常
-
解法:配置 rollbackFor 屬性
@Transactional(rollbackFor = Exception.class)
2. 業(yè)務(wù)方法內(nèi)自己 try-catch 異常導(dǎo)致事務(wù)不能正確回滾
[JDK 21 API 文檔](https://docs.oracle.com/en/java/javase/21/docs/api/)
@Service
public class Service2 {
@Autowired
private AccountMapper accountMapper;
@Transactional(rollbackFor = Exception.class)
public void transfer(int from, int to, int amount) {
try {
int fromBalance = accountMapper.findBalanceBy(from);
if (fromBalance - amount >= 0) {
accountMapper.update(from, -1 * amount);
new FileInputStream("aaa");
accountMapper.update(to, amount);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
原因:事務(wù)通知只有捉到了目標(biāo)拋出的異常,才能進(jìn)行后續(xù)的回滾處理,如果目標(biāo)自己處理掉異常,事務(wù)通知無法知悉
-
解法1:異常原樣拋出
- 在 catch 塊添加
throw new RuntimeException(e);
- 在 catch 塊添加
-
解法2:手動(dòng)設(shè)置 TransactionStatus.setRollbackOnly()
- 在 catch 塊添加
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
- 在 catch 塊添加
3. aop 切面順序?qū)е聦?dǎo)致事務(wù)不能正確回滾
@Service
public class Service3 {
@Autowired
private AccountMapper accountMapper;
[Testcontainers 文檔](https://testcontainers.com/)
@Transactional(rollbackFor = Exception.class)
public void transfer(int from, int to, int amount) throws FileNotFoundException {
int fromBalance = accountMapper.findBalanceBy(from);
if (fromBalance - amount >= 0) {
accountMapper.update(from, -1 * amount);
new FileInputStream("aaa");
accountMapper.update(to, amount);
}
}
}
@Aspect
public class MyAspect {
@Around("execution(* transfer(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
LoggerUtils.get().debug("log:{}", pjp.getTarget());
try {
return pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
[JDK 8 API 文檔](https://docs.oracle.com/javase/8/docs/api/)
return null;
}
}
[JDK 17 API 文檔](https://docs.oracle.com/en/java/javase/17/docs/api/)
}
原因:事務(wù)切面優(yōu)先級最低,但如果自定義的切面優(yōu)先級和他一樣,則還是自定義切面在內(nèi)層,這時(shí)若自定義切面沒有正確拋出異常…
解法1、2:同情況2 中的解法:1、2
解法3:調(diào)整切面順序,在 MyAspect 上添加
@Order(Ordered.LOWEST_PRECEDENCE - 1)(不推薦)
4. 非 public 方法導(dǎo)致的事務(wù)失效
@Service
public class Service4 {
@Autowired
private AccountMapper accountMapper;
@Transactional
void transfer(int from, int to, int amount) throws FileNotFoundException {
int fromBalance = accountMapper.findBalanceBy(from);
if (fromBalance - amount >= 0) {
accountMapper.update(from, -1 * amount);
accountMapper.update(to, amount);
}
}
}
原因:Spring 為方法創(chuàng)建、添加事務(wù)通知、前提條件都是該方法是 public 的
解法1:改為 public 方法
解法2:添加 bean 配置如下(不推薦)
@Bean
[Log4j 2 文檔](https://logging.apache.org/log4j/2.x/)
public Transa