Spring:現代Java開發的必備框架

2023-03-14 06:00:27

Spring:現代Java開發的必備框架

Spring是一個輕量級的Java框架,它提供了各種企業級應用程式開發的工具和技術。Spring框架的核心是IoC容器和AOP框架。IoC容器使得Java應用程式的元件化變得更加容易,AOP框架使得Java應用程式的切面程式設計變得更加容易。Spring框架還提供了許多其他的功能,例如資料存取、Web開發、安全性、事務管理等。

Spring建立bean的生命週期以及對應的介面和註解

Spring建立bean的生命週期包含以下步驟:

  1. 範例化Bean:Spring通過構造器或工廠方法來建立Bean範例。
  2. 設定Bean屬性:Spring通過setter方法或直接存取欄位來設定Bean的屬性值。
  3. BeanNameAware介面:如果Bean實現了BeanNameAware介面,Spring將Bean的ID傳遞給setBeanName()方法。
  4. BeanFactoryAware介面:如果Bean實現了BeanFactoryAware介面,Spring將BeanFactory範例傳遞給setBeanFactory()方法。
  5. ApplicationContextAware介面:如果Bean實現了ApplicationContextAware介面,Spring將ApplicationContext範例傳遞給setApplicationContext()方法。
  6. Pre-Initialization BeanPostProcessor:在Bean初始化之前,Spring通過呼叫PostProcessBeforeInitialization()方法提供了一個擴充套件點,可以在Bean初始化之前對Bean進行客製化。
  7. InitializingBean介面:如果Bean實現了InitializingBean介面,Spring將呼叫afterPropertiesSet()方法。
  8. 自定義初始化方法:Bean可以自定義初始化方法,只需要在Bean定義中指定該方法的名稱。
  9. Post-Initialization BeanPostProcessor:在Bean初始化之後,Spring通過呼叫PostProcessAfterInitialization()方法提供了一個擴充套件點,可以在Bean初始化之後對Bean進行客製化。
  10. DisposableBean介面:如果Bean實現了DisposableBean介面,Spring將呼叫destroy()方法。
  11. 自定義銷燬方法:Bean可以自定義銷燬方法,只需要在Bean定義中指定該方法的名稱。

在Spring中,可以使用以下註解來控制Bean的生命週期:

  • @PostConstruct:指定初始化方法,相當於InitializingBean介面的afterPropertiesSet()方法。
  • @PreDestroy:指定銷燬方法,相當於DisposableBean介面的destroy()方法。
  • @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod"):指定初始化方法和銷燬方法。

Spring使用三級快取解決迴圈依賴的原理

  1. singletonObjects:儲存已經完全建立好的單例Bean。
  2. earlySingletonObjects:儲存已經範例化、但是還未填充屬性的單例Bean。
  3. singletonFactories:儲存Bean的工廠方法。

當兩個Bean相互依賴時,Spring會先使用工廠方法建立一個Bean的代理物件,然後將代理物件放入到singletonFactories快取中。接著,Spring會繼續建立被依賴的Bean。如果建立被依賴的Bean時,需要參照到代理物件,Spring會先到singletonFactories快取中查詢代理物件,如果找到了,就使用代理物件,否則就繼續建立代理物件。當被依賴的Bean建立完成後,Spring會將被依賴的Bean放入到singletonObjects快取中。接著,Spring會回到代理物件,填充代理物件的屬性,然後將代理物件放入到earlySingletonObjects快取中。當代理物件的屬性填充完成後,Spring會將代理物件替換成真正的Bean物件,然後將真正的Bean物件放入到singletonObjects快取中,並清除earlySingletonObjects快取和singletonFactories快取中的代理物件。

Spring使用三級快取建立bean的過程

Spring使用三級快取建立Bean的詳細過程如下:

  1. 首先,Spring會檢查該Bean是否已經建立並儲存在singletonObjects快取中。如果Bean已經存在,則直接返回該Bean範例。這是最快的情況。
  2. 如果Bean不存在於singletonObjects快取中,Spring會檢查該Bean是否正在建立中。如果正在建立中,則返回一個代理物件,該代理物件會在真正的Bean建立完成後被替換成真正的Bean物件。這是為了防止迴圈依賴的情況。
  3. 如果該Bean既不存在於singletonObjects快取中,也沒有正在建立中,則Spring會建立一個ObjectFactory,該ObjectFactory負責建立該Bean範例。ObjectFactory是一個工廠方法,它負責建立和返回Bean範例。
  4. 然後,Spring會將該ObjectFactory儲存到singletonFactories快取中,以便下次獲取該Bean範例時使用。這是為了提高建立Bean範例的效率。
  5. 接著,Spring會檢查該Bean是否依賴於其他Bean。如果依賴於其他Bean,則會先建立依賴的Bean範例。依賴項可以是其他Bean,也可以是基本型別或集合。
  6. 如果依賴的Bean範例還不存在,則Spring會遞迴地建立依賴的Bean範例,直到所有依賴的Bean範例都已經建立完成。這是為了保證依賴關係正確。
  7. 如果所有依賴的Bean範例都已經建立完成,則Spring會從singletonFactories快取中獲取該Bean的ObjectFactory,並使用該ObjectFactory建立該Bean範例。這是Bean範例的建立過程。
  8. 建立該Bean範例後,Spring會將該Bean範例儲存到earlySingletonObjects快取中,以便後續填充該Bean的屬性。earlySingletonObjects快取包含已經建立的單例Bean範例,但是還沒有填充屬性。
  9. 接著,Spring會遞迴地填充該Bean的屬性。如果該Bean的屬性依賴於其他Bean,則會先建立依賴的Bean範例。填充屬性之前,Spring會呼叫所有實現了BeanPostProcessor介面的類的postProcessBeforeInitialization()方法做一些預處理工作。這是為了提供一個擴充套件點,可以在Bean初始化之前對Bean進行客製化。
  10. 如果所有依賴的Bean範例都已經建立完成,則Spring會從singletonObjects快取中獲取依賴的Bean範例,並將依賴的Bean範例填充到該Bean的屬性中。填充屬性之後,Spring會再次呼叫所有實現了BeanPostProcessor介面的類的postProcessAfterInitialization()方法做一些後處理工作。這是為了提供一個擴充套件點,可以在Bean初始化之後對Bean進行客製化。
  11. 填充該Bean的屬性後,Spring會將該Bean範例儲存到singletonObjects快取中,並從earlySingletonObjects快取中移除該Bean範例。singletonObjects快取包含已經建立的單例Bean範例,並已經填充了屬性。
  12. 最後,Spring會遞迴地處理該Bean的後置處理器,然後返回該Bean範例。如果Bean實現了InitializingBean介面,Spring會呼叫其afterPropertiesSet()方法,這是Bean初始化之後的最後一步。如果Bean實現了DisposableBean介面,Spring會在Bean銷燬之前呼叫其destroy()方法。

Spring使用AOP

基本過程如下:

  1. 在設定類上新增@EnableAspectJAutoProxy註解,啟用AspectJ自動代理。
  2. 建立一個切面類,並且在該類上使用@Aspect註解來標識該類為切面類。
  3. 在切面類中定義一個或多個切點,用來匹配需要攔截的方法。
  4. 在切面類中定義一個或多個通知,用來在方法執行前、執行後或丟擲異常時執行一些額外的邏輯。
  5. 將切面類新增到Spring的容器中。

以下是一個使用註解方式實現AOP的範例:

@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceLayer(){}

    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before " + joinPoint.getSignature().getName());
    }

    @After("serviceLayer()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After " + joinPoint.getSignature().getName());
    }
}

@Service
public class DemoService {
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example.demo")
public class AppConfig {
}

在上面的範例中,LoggingAspect是一個切面類,它定義了一個切點serviceLayer()和兩個通知logBefore()和logAfter()。serviceLayer()用來匹配com.example.demo.service包中所有方法,logBefore()和logAfter()分別在方法執行前和執行後輸出一條紀錄檔。DemoService是一個普通的服務類,它的doSomething()方法會被LoggingAspect攔截。最後,AppConfig是一個設定類,它啟用了AspectJ自動代理,並將LoggingAspect和DemoService新增到Spring的容器中。

當呼叫DemoService的doSomething()方法時,LoggingAspect會攔截該方法,並在方法執行前輸出一條紀錄檔,同時在方法執行後輸出一條紀錄檔。

SpringAOP的實現原理

Spring AOP的實現原理是使用動態代理技術。動態代理是在執行時建立代理物件,代理物件實現了目標物件的介面,並且攔截所有方法呼叫。Spring AOP使用了兩種型別的代理:JDK動態代理和CGLIB動態代理。如果目標物件實現了介面,則使用JDK動態代理;否則,使用CGLIB動態代理。

Spring使用事務管理

Spring的事務管理可以通過如下方式來使用:

  1. 設定事務管理器:Spring提供了多種事務管理器,如DataSourceTransactionManager、HibernateTransactionManager等。可以通過設定事務管理器來選擇適合的事務管理器。
  2. 設定事務屬性:可以通過設定事務屬性來指定事務的傳播行為、隔離級別、超時時間、唯讀等特性。
  3. 使用@Transactional註解:可以在需要事務管理的方法上新增@Transactional註解,Spring會自動管理該方法的事務。@Transactional註解可以指定事務的傳播行為、隔離級別、超時時間、唯讀等特性。

Spring事務的傳播行為指的是當一個事務方法呼叫另外一個事務方法時,如何管理事務。Spring定義了七種傳播行為,包括:

  • REQUIRED:如果當前存在事務,則加入該事務,否則建立一個新的事務。
  • SUPPORTS:如果當前存在事務,則加入該事務,否則以非事務的方式執行。
  • MANDATORY:必須在一個已有的事務中執行,否則丟擲異常。
  • REQUIRES_NEW:建立一個新的事務,並且暫停當前事務(如果存在)。
  • NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,則暫停該事務。
  • NEVER:以非事務方式執行操作,如果當前存在事務,則丟擲異常。
  • NESTED:如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行REQUIRED類似的操作。

Spring事務的隔離級別指的是多個事務之間的隔離程度。Spring定義了五種隔離級別,包括:

  • DEFAULT:使用底層資料庫的預設隔離級別。
  • READ_UNCOMMITTED:允許髒讀、不可重複讀和幻讀。
  • READ_COMMITTED:禁止髒讀,但允許不可重複讀和幻讀。
  • REPEATABLE_READ:禁止髒讀和不可重複讀,但允許幻讀。
  • SERIALIZABLE:禁止髒讀、不可重複讀和幻讀,最嚴格的隔離級別。

Spring事務的超時時間指的是事務的執行時間超過該時間時,事務將被回滾。Spring預設的事務超時時間為-1,表示事務沒有超時限制。唯讀事務指的是事務中唯讀取資料,不修改資料。唯讀事務可以提高資料庫的並行效能。

可以使用@Transactional註解來指定事務的傳播行為、隔離級別、超時時間、唯讀等特性。例如:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, readOnly = false)
public void doSomething() {
    // some code
}

在這個例子中,@Transactional註解指定了事務的傳播行為為REQUIRED、隔離級別為DEFAULT、超時時間為3600秒、唯讀為false。

使用Spring事務管理可以提高應用程式的效能和可靠性。它可以確保在事務範圍內的所有操作要麼全部成功,要麼全部失敗,從而保持資料的完整性和一致性。

Spring事務的原理

Spring事務的實現原理是使用AOP技術。在Spring中,事務管理是通過將事務管理器和事務通知應用到目標方法上來實現的。事務管理器是負責管理事務的物件,事務通知是負責在目標方法執行前後開啟、提交或回滾事務的物件。在Spring中,事務通知是使用AspectJ的@Aspect註解來實現的。

當一個方法被標記為@Transactional時,Spring會建立一個代理物件來代理該方法。該代理物件會將目標方法的呼叫轉發給事務通知。在事務通知中,會根據事務管理器的設定來開啟、提交或回滾事務。如果方法執行過程中發生了異常,則會回滾事務。如果方法執行成功,則會提交事務。事務管理器會將事務的狀態儲存在ThreadLocal中,以便在同一執行緒中的其他方法也可以存取該事務。

Spring事務的實現依賴於底層的資料庫事務。Spring提供了多種事務管理器,如DataSourceTransactionManager、HibernateTransactionManager等。事務管理器可以設定事務的傳播行為、隔離級別、超時時間、唯讀等特性。

DataSourceTransactionManager的實現

DataSourceTransactionManager的實現主要包括以下內容:

  1. 實現了PlatformTransactionManager介面,用於管理事務的生命週期。PlatformTransactionManager介面是Spring事務管理器的核心介面,它定義了事務的生命週期和狀態轉換規則。DataSourceTransactionManager實現了PlatformTransactionManager介面,用於管理基於DataSource的事務。PlatformTransactionManager介面包括以下方法:
    • getTransaction(TransactionDefinition definition):獲取一個新的事務或將當前執行緒中的事務加入到當前方法的事務中。
    • commit(TransactionStatus status):提交當前事務。
    • rollback(TransactionStatus status):回滾當前事務。
  2. 實現了TransactionDefinition介面,用於定義事務的傳播行為、隔離級別、超時時間、唯讀等特性。TransactionDefinition介面定義了事務的屬性,包括傳播行為、隔離級別、超時時間、唯讀等。DataSourceTransactionManager實現了TransactionDefinition介面,用於指定基於DataSource的事務的屬性。
  3. 實現了TransactionStatus介面,用於跟蹤事務的狀態。TransactionStatus介面定義了事務的狀態,包括未提交、已提交和已回滾。DataSourceTransactionManager實現了TransactionStatus介面,用於跟蹤基於DataSource的事務的狀態。
  4. 實現了TransactionSynchronizationManager類,用於管理事務的同步狀態。TransactionSynchronizationManager類用於管理事務的同步狀態,包括事務的開始、結束和回滾等操作。DataSourceTransactionManager使用TransactionSynchronizationManager類來管理基於DataSource的事務的同步狀態。
  5. 實現了TransactionAspectSupport類,用於支援基於AspectJ的事務通知。TransactionAspectSupport類是Spring事務管理的核心類,它實現了基於AspectJ的事務通知邏輯。DataSourceTransactionManager使用TransactionAspectSupport類來支援基於AspectJ的事務通知。

DataSourceTransactionManager的工作流程

  1. 當一個方法被標記為@Transactional時,Spring會建立一個代理物件來代理該方法。
  2. 代理物件會呼叫TransactionAspectSupport類的invokeWithinTransaction()方法,該方法會在事務的上下文中執行目標方法。
  3. 在invokeWithinTransaction()方法中,會呼叫DataSourceTransactionManager的doBegin()方法來開啟事務。
  4. 在doBegin()方法中,會獲取當前執行緒中的Connection物件,並將其設定為事務的連線。
  5. 如果當前執行緒中沒有Connection物件,則會從DataSource中獲取一個新的Connection物件,並將其設定為事務的連線。
  6. 如果事務的隔離級別不是預設值,則會將事務的隔離級別設定到Connection物件上。
  7. 如果事務的超時時間不是預設值,則會將事務的超時時間設定到Connection物件上。
  8. 在目標方法執行完畢後,會呼叫TransactionAspectSupport類的invokeWithinTransaction()方法的afterCompletion()方法來提交或回滾事務。
  9. 在afterCompletion()方法中,會呼叫DataSourceTransactionManager的doCleanupAfterCompletion()方法來清理事務狀態。
  10. 在doCleanupAfterCompletion()方法中,會將事務的連線關閉,並將其從當前執行緒中移除。

Spring不太常用的註解

  • @Lazy:指定Bean是否延遲初始化。預設情況下,Spring會在容器啟動時建立所有的單例Bean,通過設定@Lazy註解可以將Bean的建立推遲到第一次使用時。這對於那些啟動時間較長的應用程式來說非常有用,可以提高應用程式的啟動速度。
  • @DependsOn:指定Bean依賴的其他Bean。如果被依賴的Bean未被建立,則會先建立該Bean。@DependsOn註解可以用來控制Bean的建立順序,確保依賴的Bean在當前Bean之前被建立。
  • @Primary:在多個Bean實現某個介面時,指定預設使用哪個Bean。當一個介面有多個實現類時,可以使用@Primary註解來指定預設使用哪個實現類。如果沒有指定@Primary註解,則需要使用@Qualifier註解來指定使用哪個實現類。
  • @Qualifier:指定使用哪個Bean實現某個介面。當有多個Bean實現同一個介面時,可以使用@Qualifier註解來指定使用哪個Bean。@Qualifier註解需要與@Autowired註解一起使用,在注入Bean時指定使用哪個實現類。
  • @Profile:指定Bean在特定的環境中才會被建立。可以使用@Profile註解來指定Bean在哪些環境下被建立,例如開發環境、測試環境或生產環境。
  • @Value:從屬性檔案中獲取Bean的屬性值。可以使用@Value註解來指定Bean的屬性值。@Value註解可以用來注入簡單型別的值,例如字串、數位或布林值。
  • @RestController:將一個類宣告為RESTful Web服務的控制器。可以使用@RestController註解來將一個類宣告為RESTful Web服務的控制器,使其能夠處理HTTP請求並返回JSON或XML格式的資料。
  • @ExceptionHandler:處理異常。可以使用@ExceptionHandler註解來處理Controller中的異常,當Controller中丟擲異常時,會自動呼叫@ExceptionHandler註解中指定的方法來處理異常。

成功是一個長期的過程,需要不斷地努力和堅持。

  • 「成功的祕訣在於堅持,堅持,再堅持。」——張德芬
  • 「成功不是將來才有的,而是從決定去做的那一刻起,持續累積而成。」——阿斯頓·馬丁
  • 「只有在經歷了漫長跋涉之後,才能登上理想的山巔。」——菲茨傑拉德
  • 「成功的關鍵在於我們是否真正熱愛我們所做的事情,是否做到了最好。」——賀綠汀
  • 「成功者不是從不失敗,而是能夠從失敗中振作起來。」——喬治·愛德華·伯納德·肖
  • 「不要害怕失敗,失敗是通向成功的必經之路。」——邁克爾·喬丹

成功不是一蹴而就的,它需要我們一步一個腳印地向前走,不斷地嘗試和學習,不斷地改進和完善。在這個過程中,我們可能會遇到挫折和困難,但是隻要我們保持信心和勇氣,堅持不懈地努力,最終我們一定會迎來成功的喜悅。