springboot
打包
mvn install:install-file -Dfile=<path-to-jar> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=jar
<path-to-jar> 是你的 JAR 文件的路徑。
<group-id> 是你的項目的組 ID。
<artifact-id> 是你的項目的構件 ID。
<version> 是你的項目的版本號。mvn install:install-file -Dfile=D:\ojdbc6-11.2.0.1.0.jar -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.1.0 -Dpackaging=jar
AOP
根據目標對象是否實現接口自動選擇動態代理機制。
-
JDK 動態代理:適用于目標對象實現了接口的情況。
JDK 動態代理是 Java 提供的一種動態創建代理對象的機制。它允許在運行時動態地創建一個代理類,該代理類實現了與目標對象相同的接口,并且可以攔截對目標對象方法的調用。通過這種方式,可以在不修改目標對象代碼的情況下,對目標對象的方法調用進行增強(如添加日志、事務管理等)。
-
CGLIB 動態代理:適用于目標對象沒有實現接口的情況。
通過aspect注解定義切面,可以結合自定義注解實現 不改變源代碼情況下,在方法執行前后增加功能。
IOC
通過 IoC,Spring 容器負責管理對象的生命周期和依賴關系,而不是由開發者手動管理。
@Repository
:標記一個類為數據訪問層組件,通常用于數據庫操作。@Qualifier
:用于指定注入的 Bean 的名稱,當存在多個同類型的 Bean 時,可以使用該注解來明確指定注入哪一個 Bean。@Primary
:用于指定默認注入的 Bean,當存在多個同類型的 Bean 時,Spring 容器會優先注入標記為@Primary
的 Bean。- @Cacheable` 是 Spring 緩存框架提供的一個注解,用于聲明一個方法的結果是可緩存的。當方法被調用時,Spring 會檢查緩存中是否已經存在相應的值。如果存在,則直接從緩存中返回結果,而不會執行方法體;如果不存在,則執行方法體并將結果存入緩存,以便下次調用時可以直接從緩存中獲取。
- @Transactional: 被標記的方法如果出現**
RuntimeException
和Error
**會回滾事物。- Spring 事務的隔離級別有哪些?
READ_UNCOMMITTED
:最低的隔離級別,允許讀取未提交的數據,可能出現臟讀。READ_COMMITTED
:允許讀取已提交的數據,避免了臟讀,但可能出現不可重復讀。REPEATABLE_READ
(默認級別):保證在同一個事務中多次讀取數據的結果是一致的,避免了不可重復讀,但可能出現幻讀。SERIALIZABLE
:最高的隔離級別,完全隔離并發事務,避免了臟讀、不可重復讀和幻讀,但性能開銷最大。
- 什么是臟讀、不可重復讀和幻讀?
- 臟讀:一個事務讀取了另一個事務未提交的數據。
- 不可重復讀:一個事務在兩次讀取同一數據時,數據被另一個事務修改,導致兩次讀取的結果不一致。
- 幻讀:一個事務在兩次讀取同一范圍的數據時,數據被另一個事務插入或刪除,導致兩次讀取的結果不一致。
- Spring 事務的隔離級別有哪些?
線程池
線程池的基本概念
- 什么是線程池?
- 線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然后在創建了線程后,從隊列中取出任務并執行。
- 線程池的主要目的是減少線程創建和銷毀的開銷,提高線程的復用性。
- 線程池的主要優點是什么?
- 減少線程創建和銷毀的開銷:線程池會復用已創建的線程,減少線程創建和銷毀的次數。
- 提高響應速度:任務提交后可以直接從線程池中獲取線程執行,減少了線程創建的時間。
- 提高線程的可管理性:線程池可以對線程進行統一管理,如設置線程的最大數量、隊列大小等。
- 避免資源耗盡:通過限制線程的最大數量,避免系統資源耗盡。
- 線程池的主要組成部分是什么?
- 線程池管理器:負責創建和管理線程池。
- 工作線程:線程池中實際執行任務的線程。
- 任務隊列:用于存儲待執行任務的隊列。
- 任務接口:所有任務必須實現的接口,以便工作線程可以執行。
線程池的實現原理
- Java 中的線程池是如何實現的?
- Java 的線程池主要通過
java.util.concurrent
包中的Executor
框架實現。 Executor
是一個接口,定義了執行任務的方法。ExecutorService
是Executor
的子接口,提供了更豐富的功能,如線程池的管理。ThreadPoolExecutor
是ExecutorService
的實現類,提供了線程池的核心實現。
- Java 的線程池主要通過
ThreadPoolExecutor
的構造參數有哪些?corePoolSize
:核心線程數,線程池中始終保持的線程數量。maximumPoolSize
:最大線程數,線程池中允許的最大線程數量。keepAliveTime
:非核心線程的空閑存活時間。unit
:keepAliveTime
的時間單位。workQueue
:任務隊列,用于存儲待執行任務的隊列。threadFactory
:線程工廠,用于創建線程。handler
:拒絕策略,當任務隊列滿且線程數達到最大值時,如何處理新任務。
- 線程池的拒絕策略有哪些?
AbortPolicy
:直接拋出RejectedExecutionException
。CallerRunsPolicy
:由調用線程執行任務。DiscardPolicy
:直接丟棄任務。DiscardOldestPolicy
:丟棄隊列中最老的任務,然后嘗試提交新任務。
線程池的使用方法
-
如何創建一個線程池?
-
使用
ThreadPoolExecutor
的構造方法創建線程池。 -
示例:
import java.util.concurrent.*;public class ThreadPoolExample {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心線程數4, // 最大線程數60L, // 空閑存活時間TimeUnit.SECONDS, // 時間單位new LinkedBlockingQueue<Runnable>(100) // 任務隊列);for (int i = 0; i < 10; i++) {executor.execute(() -> {System.out.println("Task executed by: " + Thread.currentThread().getName());});}executor.shutdown();} }
-
-
如何關閉線程池?
- 使用
shutdown()
方法關閉線程池,等待所有任務完成。 - 使用
shutdownNow()
方法立即關閉線程池,嘗試中斷正在執行的任務。
- 使用
-
如何提交任務到線程池?
- 使用
execute(Runnable command)
方法提交任務。 - 使用
submit(Callable<T> task)
方法提交任務并返回Future
對象。
- 使用
線程池的性能優化
- 如何優化線程池的性能?
- 合理設置線程池大小:根據系統的硬件資源和任務類型,合理設置核心線程數和最大線程數。
- 選擇合適的任務隊列:根據任務的特點選擇合適的任務隊列,如
LinkedBlockingQueue
或ArrayBlockingQueue
。 - 設置合理的拒絕策略:根據業務需求選擇合適的拒絕策略,避免任務丟失。
- 監控線程池狀態:通過
ThreadPoolExecutor
提供的方法監控線程池的狀態,如getActiveCount()
、getCompletedTaskCount()
等。
線程池的高級特性
-
什么是線程池的飽和策略?
- 當任務隊列滿且線程數達到最大值時,線程池會采取的策略。可以通過
RejectedExecutionHandler
接口自定義飽和策略。
- 當任務隊列滿且線程數達到最大值時,線程池會采取的策略。可以通過
-
如何自定義線程工廠?
-
實現
ThreadFactory
接口,自定義線程的創建邏輯。 -
示例:
import java.util.concurrent.*;public class CustomThreadFactory implements ThreadFactory {private final String threadNamePrefix;public CustomThreadFactory(String threadNamePrefix) {this.threadNamePrefix = threadNamePrefix;}@Overridepublic Thread newThread(Runnable r) {return new Thread(r, threadNamePrefix + "-Thread-" + Thread.currentThread().getId());}public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100),new CustomThreadFactory("MyCustomThread"));for (int i = 0; i < 10; i++) {executor.execute(() -> {System.out.println("Task executed by: " + Thread.currentThread().getName());});}executor.shutdown();} }
-
線程池的常見問題
- 線程池中的線程是如何復用的?
- 線程池中的線程在執行完一個任務后,會返回線程池,等待下一個任務。如果線程池中的線程數量超過核心線程數且空閑時間超過
keepAliveTime
,則會銷毀多余的線程。
- 線程池中的線程在執行完一個任務后,會返回線程池,等待下一個任務。如果線程池中的線程數量超過核心線程數且空閑時間超過
- 線程池中的線程是如何銷毀的?
- 當線程池中的線程數量超過核心線程數且空閑時間超過
keepAliveTime
時,多余的線程會被銷毀。 - 當調用
shutdown()
方法時,線程池會等待所有任務完成后再銷毀所有線程。 - 當調用
shutdownNow()
方法時,線程池會嘗試中斷正在執行的任務,并立即銷毀所有線程。
- 當線程池中的線程數量超過核心線程數且空閑時間超過
- 線程池中的任務是如何調度的?
- 線程池中的任務通過任務隊列進行調度。任務隊列可以是阻塞隊列(如
LinkedBlockingQueue
、ArrayBlockingQueue
)或非阻塞隊列(如SynchronousQueue
)。 - 線程池會從任務隊列中取出任務并分配給空閑的線程執行。
- 線程池中的任務通過任務隊列進行調度。任務隊列可以是阻塞隊列(如
線程池的監控
- 如何監控線程池的狀態?
- 使用
ThreadPoolExecutor
提供的方法監控線程池的狀態,如:getActiveCount()
:獲取當前活躍的線程數。getCompletedTaskCount()
:獲取已完成的任務數。getTaskCount()
:獲取任務總數。getPoolSize()
:獲取線程池中的線程數。getLargestPoolSize()
:獲取線程池中曾經出現的最大線程數。
- 使用
設計模式
裝飾器加適配器
理解:
準備一個接口,兩個實現類,實現類一實現基本功能,實現類二中將實現類一注入進去,調用實現類一的方法,在方法前后添加特殊邏輯。
應用場景:
流程里用不用適配器,就看一點:各環節接口對不上,但又得一起干活。 對不上(參數、格式、方法名不一樣),又改不了其中一方,就用適配器當“翻譯”;能對上,或者能直接改接口,就不用折騰。
示例:
public interface ZSQservice {String ZSQService(String arg0, String arg1);
}
@Service
public class ZSQserviceImpl implements ZSQservice {@Overridepublic String ZSQService(String arg0, String arg1) {System.out.println("ZSQservice impl, arg0=" + arg0 + ", arg1=" + arg1);return "return, arg0=" + arg0 + ", arg1=" + arg1;}
}
@Slf4j
@Service
public class ZSQLOGIMPL implements ZSQservice {private ZSQservice zsqservice;public ZSQLOGIMPL(@Qualifier("ZSQEncryImpl")ZSQservice zsqservice) {this.zsqservice = zsqservice;}@Overridepublic String ZSQService(String arg0, String arg1) {log.info("執行前記錄參數 arg0=" + arg0 + ", arg1=" + arg1);String returnJson = zsqservice.ZSQService(arg0, arg1);log.info("執行后記錄結果 "+returnJson);return returnJson;}
}
@Slf4j
@Service
public class ZSQEncryImpl implements ZSQservice {private ZSQservice zsqservice;public ZSQEncryImpl(@Qualifier("ZSQserviceImpl") ZSQservice zsqservice) {this.zsqservice = zsqservice;}@Overridepublic String ZSQService(String arg0, String arg1) {log.info("數據加密中");String json = zsqservice.ZSQService(arg0, arg1);log.info("數據加密完成");return json;}
}
@Testvoid zsqTest(){ZSQservice zsQservice = new ZSQLOGIMPL(new ZSQEncryImpl(new ZSQserviceImpl()));zsQservice.ZSQService("1","2");}
: 執行前記錄參數 arg0=1, arg1=2
: 數據加密中ZSQservice impl, arg0=1, arg1=2
: 數據加密完成
: 執行后記錄結果 return, arg0=1, arg1=2
代理模式
只要裝飾器模式不添加特定功能就是代理,也可以看aop是spring自動進行代理的。
AOP加自定義注解
示例:
1. 添加依賴
在 pom.xml
中添加 Spring AOP 的依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 自定義注解
2.1 @LogAction
作用:記錄方法的執行日志,包括方法名、入參和出參。
屬性:
action
:描述日志內容,可選,默認值為"日志記錄"
。
package com.example.annotation;import java.lang.annotation.*;@Target({ElementType.METHOD, ElementType.TYPE}) // 可以標注在方法或類上
@Retention(RetentionPolicy.RUNTIME) // 運行時保留
public @interface LogAction {String action() default "日志記錄"; // 默認值
}
3. 自定義注解例子
3.1 @LogAction
示例
package com.example.service;import com.example.annotation.LogAction;
import org.springframework.stereotype.Service;