2024年12月30日 星期一

海科面試問題

 1 關於 java中的spring 有ioc和aop可以介紹一下分別是在做什麼嗎?

在Java的Spring框架中,IoC(控制反轉)和AOP(面向切面編程)是兩個非常重要的概念。

1. IoC(控制反轉)

IoC是一種設計模式,主要用於改進代碼的可維護性和可測試性。在IoC中,對象的創建和管理脫離了外部組件的控制,而是由Spring容器來進行管理。這意味著對象的依賴關係(例如,依賴於其他對象的服務)不再由對象自己負責,而是由IoC容器在運行時動態注入。

這樣的好處包括:

  • 減少了代碼的耦合度。
  • 使得測試更容易,可以使用Mock對象來替代真實對象。
  • 促進了代碼的可重用性和清晰性。

2. AOP(面向切面編程)

AOP是一種程序設計範式,用於分離橫切關注點(cross-cutting concerns),例如日誌記錄、性能監控和安全控制等。這些功能通常跨越多個類,而在每個類中重複實現會導致代碼複雜性。

在Spring中,AOP允許你定義“切面”(aspect),這是由一系列的“通知”(advice)和“切入點”(pointcuts)組成的。通知是實際要執行的行為,切入點則是一組條件,用於指定哪些方法應該被通知影響。

AOP的好處包括:

  • 提高代碼的模塊化,將橫切關注點與業務邏輯分離。
  • 使得代碼更清晰易讀。
  • 方便在需要時添加或修改功能,而不需要改變核心業務邏輯。

總結來說,IoC專注於對象的創建和管理,而AOP則專注於橫切關注點的處理。這兩者共同協作,可以讓你的應用程序更加靈活和可維護。


2那關於IOC中, DI是指什麼, 有什麼用處?


在IoC(控制反轉)中,DI指的是依賴注入(Dependency Injection)。這是一種設計模式,旨在實現對象及其依賴之間的解耦。

依賴注入(DI)的概念

依賴注入的核心思想是將對象所需的依賴(其他對象)由外部注入,而不是由該對象自己創建或查找依賴。這通常通過以下三種方式來實現:

  1. 構造函數注入通過類的構造函數傳遞需要的依賴。
  2. Setter方法注入通過setter方法來注入依賴。
  3. 接口注入通過實現某個接口來獲取依賴(這種方式相對少見)。

依賴注入的用處

  1. 降低耦合度:由於對象不再直接創建其依賴,它們變得更獨立,這使得系統的各個部分之間的耦合度降低。

  2. 提高可測試性:在測試中,可以通過注入mock或stub對象來替代真實的依賴,從而簡化單元測試的過程。

  3. 促進代碼重用:當對象及其依賴都能相對獨立時,將更容易重複使用這些對象。

  4. 簡化配置管理:通過使用IoC容器來管理依賴,可以減少手動配置這些依賴的方法,並且讓它們集中管理,便於維護。

  5. 靈活性:可以在運行時輕鬆替換依賴,以滿足不同的需求,這對於高可擴展性且變化頻繁的項目非常有用。

總結來說,依賴注入作為IoC的一部分,幫助實現碼的模塊化和靈活性,同時也提升了測試的便利性和可靠性。


3 spring如何解決循環依賴問題, 為什麼autowired不會因循環依賴產生問題?


在Spring框架中,循環依賴(Circular Dependency)問題指的是兩個或多個bean相互依賴,形成一種循環關係。Spring通過不同的方式來解決這個問題,尤其是在使用Autowired進行依賴注入時。

Spring解決循環依賴的方法

  1. 使用暫時的代理對象

    • Spring使用的主要方法是建立一個“暫時的代理”對象,這是一個不完整的bean引用,當一個bean在創建的過程中遇到另一個依賴時,Spring能夠立即返回這個代理對象,而不是等待完整的對象創建完成。這樣,即使存在循環依賴,至少還可以提供一個參考來完成bean的注入。
  2. 構造器依賴注入的限制

    • 當使用構造器注入時,如果存在循環依賴,Spring容器在創建bean時會無法完成整個對象的創建,因此會拋出 BeanCurrentlyInCreationException。這意味著循環依賴在構造器注入中無法解決,因此通常推薦使用setter方法注入。
  3. 使用@Lazy註解

    • Spring提供了 @Lazy 注解,可以在依賴注入中延遲加載。當一個bean使用 @Lazy 注解時,它不會在應用啟動時立即創建,而是在首次用到這個bean時才創建。這在一定程度上可以緩解循環依賴問題。

為什麼@Autowired不會因循環依賴產生問題?

使用@Autowired進行依賴注入時,當Spring容器處理bean的創建時,對於需要的依賴,Spring可以首先創建該bean的代理對象,然後再進行依賴的注入。這意味著,即使某些bean在創建時互相依賴,只要不是在構造函數中直接依賴,Spring仍然能夠解決這個問題。

例如:

  • 假設有兩個類 A 和 B,它們互相依賴:

@Component
public class A {
    @Autowired
    private B b; // A 依賴於 B
}

@Component
public class B {
    @Autowired
    private A a; // B 依賴於 A
}

在這種情況下,Spring首先會創建 A 的代理對象,在注入 B 時,又會創建 B 的代理對象。由於這是一種基於接口的依賴,因此可以在建構過程中解決這種循環關係。

總結來說,Spring利用了代理機制和懶加載的特性來優雅地解決循環依賴問題,使得使用@Autowired的依賴注入不會產生困擾。


4 關於spring @Transactional的預設機制及有幾種機制?



在Spring中,@Transactional 用於管理事務,提供了一種簡單而靈活的方式來開發具有事務控制的應用。使用 @Transactional 標註的類或方法可以自動處理事務的開始、提交和回滾。

預設機制

當你在類或方法上使用 @Transactional 註解時,Spring的事務管理器會在該方法被調用時自動處理以下步驟:

  1. 開始事務:當方法被調用時,Spring會啟動一個新的事務。
  2. 執行方法:調用被標註的方法並執行其業務邏輯。
  3. 提交或回滾
    • 如果方法正常完成(即沒有拋出受檢異常),則提交該事務。
    • 如果方法拋出運行時異常(如 RuntimeException)或是 Error,則回滾該事務。
    • 如果拋出受檢異常(如 SQLException),則可以根據配置選擇回滾或提交。

事務傳播機制

Spring提供了多種事務傳播行為來控制事務的邊界和行為,共有以下幾種常用的傳播機制:

  1. REQUIRED(預設)

    • 如果已經有事務在運行,則加入該事務;如果沒有,則創建一個新的事務。
  2. REQUIRES_NEW

    • 無論是否已有事務,總是創建一個新的事務。如果有現有事務,則會暫停它。
  3. NESTED

    • 如果已有事務,則這個方法會在一個嵌套事務中運行。如果沒有事務,則類似於 REQUIRED
  4. SUPPORTS

    • 如果有事務在運行,就加入該事務;否則在無事務的狀況下正常執行。
  5. NOT_SUPPORTED

    • 總是以非事務方式執行,若已經有事務,則會暫停它。
  6. NEVER

    • 總是以非事務方式執行,如果已經有事務,則會拋出異常。
  7. MANDATORY

    • 如果已有事務,則只在該事務中運行;如果沒有,則拋出異常。

其他設定

除了傳播機制,@Transactional 還有其他一些配置選項,例如:

  • 隔離級別:定義不同事務之間的隔離級別。
  • 超時:指定事務最大執行時間。
  • 只讀事務:標記該事務為只讀,以優化性能。
  • 回滾規則:定義哪些異常應該導致事務回滾。

總結來說,@Transactional 在Spring中是一個強大的註解,允許開發者以簡單的方式控制事務的行為,並且提供各種機制來應對不同的事務場景。


5關於spring data JPA, 是否有遇到save但是沒存入資料庫的問題, 要如何解決這問題?

在使用 Spring Data JPA 時,發生 save 方法未將資料存入資料庫的問題可能由多種原因造成。以下是一些可能的原因及其解決方案:

1. 事務未正確管理

  • 問題:如果 @Transactional 註解沒有正確使用,可能會導致資料未能提交到資料庫。
  • 解決方案:確保在調用 save 方法的服務層方法上標註 @Transactional。例如:
@Service
public class YourService {
    @Autowired
    private YourRepository repository;

    @Transactional
    public void saveEntity(YourEntity entity) {
        repository.save(entity);
    }
}

2. 錯誤的 Entity 狀態

  • 問題:若要保存的實體(Entity)未被正確設置狀態(如主鍵未設置),可能導致 save 方法不起作用。
  • 解決方案:確保實體的主鍵在進行保存前已正確設置,且所有必要的字段皆已填寫。

3. 資料庫約束問題

  • 問題:當您嘗試儲存的資料違反資料庫約束(如唯一性約束、外鍵約束等)時,可能會導致保存失敗。
  • 解決方案:檢查資料庫的約束條件,確保保存的資料不會違反這些約束。此外,可以通過捕捉異常來獲取具體錯誤信息。
try {
    repository.save(entity);
} catch (DataIntegrityViolationException e) {
    // 處理符合約束條件錯誤的情況
}

4. 資料庫連接問題

  • 問題:若資料庫offline或連接配置錯誤,則資料不能成功保存。
  • 解決方案:檢查資料庫服務是否運行,以及您在 application.properties 或 application.yml 中的資料庫連接配置是否正確。

5. 使用了錯誤的 Repository

  • 問題:如果意外使用了錯誤的 Repository 進行資料操作,可能會導致資料未存入預期的資料表。
  • 解決方案:確認您正在使用的 Repository 是正確對應於所需的實體。

6. Entity 沒有被 managed

  • 問題:如果實體不是由 EntityManager 管理,則 save 不會生效。
  • 解決方案:確保實體是從持久上下文中獲得並且已被持久化。

偵錯建議

  • Log:在執行保存操作時加上日誌,記錄保存前後的資訊,以便於檢查問題。
  • DataSource Configuration:通過配置等級或顯示SQL查詢來查看是否有進行真正的插入操作。

若檢查以上項目後問題仍未解決,可以查看資料庫日誌,或增加更多的日誌輸出以獲取詳細的失敗原因。


6在使用java Threadpool 的時候, 如果每次都去取Thread是否會被取完而造成沒有Thread可以使用? 如何在使用完時關閉Thread?


在Java中,使用線程池(如 ExecutorService)時,線程池會管理一組線程以供重覆使用。這種方式減少了每次創建和銷毀線程的開銷,提高了性能。但你提到的情況,如果線程池中的線程被“取完”,就可能會導致沒有可用的線程來處理新的任務。這與線程池的大小、任務的執行時間以及提交任務的速率等多個因素有關。 1. 線程池的大小 線程池通常會設定一個最大線程數(maximumPoolSize)。當線程池中的線程達到這個最大值,並且隊列(BlockingQueue)在處理新任務時沒有空閒線程可用,就會發生任務被阻塞,直到有線程完成其任務並返回池中。 如何處理:你可以根據應用的需求調整線程池的大小。在創建線程池時,可以使用 Executors.newFixedThreadPool(int nThreads) 或 ThreadPoolExecutor 來設置核心線程數和最大線程數。 2. 關閉線程池 在使用完線程池後,建議關閉線程池以釋放資源。ExecutorService 提供了兩種關閉方式: shutdown():會停止接收新任務,並等待已提交的任務完成執行。這是一種優雅的關閉方式。 java executorService.shutdown(); shutdownNow():會試圖停止所有正在執行的任務,並返回未執行的任務。它是一種強制的關閉方式。 java List<Runnable> pendingTasks = executorService.shutdownNow(); 示例 以下是一個簡單的示例,展示如何使用線程池、提交任務並在使用完池後關閉它:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        // 提交一些任务
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executorService.submit(() -> {
                System.out.println("Executing task " + taskId);
                try {
                    // 模拟任务处理时间
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executorService.shutdown();
        try {
            // 等待所有任务完成
            if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                executorService.shutdownNow(); // 强制关闭
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt(); // 恢复中断状态
        }
    }
}

總結 線程池會管理一定數量的線程允許並發執行任務,而不是每次都創建新線程。 任務將在達到最大線程數時被阻塞,直到有線程完成。 在使用完線程池後,務必通過 shutdown() 或 shutdownNow() 方法來關閉它,以便釋放資源。 通過合理配置線程池的大小以及妥善管理其生命周期,可以有效避免線程不足的問題。

海科面試問題

 1 關於 java中的spring 有ioc和aop可以介紹一下分別是在做什麼嗎? 在Java的Spring框架中,IoC(控制反轉)和AOP(面向切面編程)是兩個非常重要的概念。 1. IoC(控制反轉) IoC是一種設計模式,主要用於改進代碼的可維護性和可測試性。在IoC中...