Недавно я заметил, что Spring успешно перехватывает вызовы функций внутри класса в классе @Configuration, но не в обычном компоненте.
Звонок как это
@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, как я могу видеть, посмотрев "this" в отладчике, и так Spring не имеет возможности перехватить вызов saveCustomer2.
Однако в следующем примере, когда транзакцияManager() вызывает метод 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;
}
}
Поэтому мой вопрос заключается в том, почему Spring может правильно перехватывать вызовы функций внутри класса во втором примере, а не в первом. Используются ли разные типы динамических прокси?
Редактировать: Из ответов здесь и других источников теперь я понимаю следующее: @Transactional реализован с использованием Spring AOP, где шаблон прокси выполняется путем переноса/компоновки пользовательского класса. Прокси-сервер AOP достаточно универсален, так что многие аспекты могут быть объединены в цепочку и может быть прокси-сервером CGLib или динамическим прокси-сервером Java.
В классе @Configuration Spring также использует CGLib для создания расширенного класса, который наследуется от пользовательского класса @Configuration, и переопределяет пользовательские функции @Bean теми, которые выполняют дополнительную работу перед вызовом пользовательской/суперфункции, такой как проверка, это первый вызов функции или нет. Это класс прокси? Это зависит от определения. Вы можете сказать, что это прокси, который использует наследование от реального объекта, а не оборачивает его с помощью композиции.
Подводя итог, из ответов, приведенных здесь, я понимаю, что это два совершенно разных механизма. Почему был сделан этот выбор дизайна - другой, открытый вопрос.