私は最近、Springが@Configurationクラスのクラス内関数呼び出しを正常にインターセプトするが、通常のBeanではインターセプトしないことに気づきました。
このような電話
@Repository
public class CustomerDAO {
@Transactional(value=TxType.REQUIRED)
public void saveCustomer() {
// some DB stuff here...
saveCustomer2();
}
@Transactional(value=TxType.REQUIRES_NEW)
public void saveCustomer2() {
// more DB stuff here
}
}
saveCustomer()のコードがCustomerDAOプロキシで実行されている間、saveCustomer2()のコードがラップされていないCustomerDAOクラスで実行されるため、新しいトランザクションの開始に失敗します。 Springは、saveCustomer2の呼び出しをインターセプトする機会がありません。
ただし、次の例では、transactionManager()がcreateDataSource()を呼び出すと、正しくインターセプトされ、ラップされていないクラスではなく、プロキシのcreateDataSource()が呼び出されます。
@Configuration
public class PersistenceJPAConfig {
@Bean
public DriverManagerDataSource createDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource.set ... DB stuff here
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager( ){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(createDataSource());
return transactionManager;
}
}
だから私の質問は、2番目の例ではSpringがクラス内関数呼び出しを正しくインターセプトできるのはなぜですか?異なるタイプの動的プロキシを使用していますか?
編集:ここの回答と他のソースから、私は次のことを理解しました。@ TransactionalはSpring AOPを使用して実装され、プロキシパターンはユーザークラスのラッピング/構成によって実行されます。 AOPプロキシーは、多くのアスペクトを一緒にチェーンできるように十分に汎用的であり、CGLibプロキシーまたはJava動的プロキシーの場合があります。
@Configurationクラスでは、SpringはCGLibを使用してユーザーの@Configurationクラスから継承する拡張クラスも作成し、ユーザーの@Bean関数を、ユーザーのスーパー関数を呼び出す前に追加の作業を行う関数でオーバーライドします。関数の最初の呼び出しかどうか。このクラスはプロキシですか?それは定義に依存します。これは、コンポジションを使用してラップする代わりに、実際のオブジェクトからの継承を使用するプロキシであると言えます。
要約すると、ここで与えられた答えから、これらは2つのまったく異なるメカニズムであることがわかります。なぜこれらの設計の選択が行われたかは、もう1つのオープンな問題です。
Springはメソッド呼び出しにプロキシを使用し、これを使用すると...そのプロキシをバイパスします。 @Beanアノテーションの場合、Springはリフレクションを使用してアノテーションを見つけます。