Обзор
@Transactional — это аннотация, используемая управлением транзакциями Spring.Если эту аннотацию добавить к методу, то метод будет транзакционным, а операции в методе будут либо зафиксированы, либо откатываться вместе; о работе, стоящей за этим Принцип включает много знаний Целью этой статьи является разобраться во всем процессе Более конкретные свойства конфигурации транзакций и интеграция транзакций Spring и сторонних платформ ORM не являются предметом этой статьи;
Используйте @Transactional
Включите поддержку аннотации @Transactional в файле конфигурации application-context.xml, настройте путь сканирования и т. д.
<tx:annotation-driven />
Аннотировать метод службы
@Service
public class UserServiceImpl implements UserService {
@Transactional
public int register(User user) {
addUser(user);
auditLog(user);
}
}
Выше приведен простой способ настройки и использования, чтобы @Transactional работал нормально;
Принципиальный анализ
То, что делает @Transactional, примерно так:
try {
tx.begin();
businessLogic();
tx.commit();
} catch(Exception ex) {
tx.rollback();
throw ex;
}
Это также нормальная работа диспетчера транзакций TransactionManager, но здесь аннотация используется для «завершения» способа ручного написания кода управления транзакциями; все другие методы службы, которым необходимо открывать транзакции, настраиваются только с такой аннотацией, которая может сохранить способ написания кода, это идея аспектного программирования, и она также дополнена Spring AOP в сочетании с аннотациями; для приведенной выше конфигурации xml и кода дополнительно проанализируйте, как Spring наконец реализует этот аспект;
BeanDefinition and BeanPostProcessor
Во-первых, это поддержка пространства имен tx,TxNamespaceHandler
зарегистрирован вAnnotationDrivenBeanDefinitionParser
Parser, который обеспечивает поддержку разбора свойств на основе аннотаций; здесь создаются три важных BeanDefinition и BeanPostProcessor, а именно BeanDefinition,
- TransactionAttributeSource // используется для получения атрибутов аннотации @Transactional
-
TransactionInterceptor // Перехватчик метода зависит от TransactionManager Bean; зависит от transactionAttributeSource
- TransactionAttributeSourceAdvisor // Зависит от transactionAttributeSource; Зависит от TransactionInterceptor; TransactionAttributeSourceAdvisor — это PointcutAdvisor, который является содержимым Spring AOP, включая pointCut и совет BeanPostProcessor,
- InfrastructureAdvisorAutoProxyCreator // используется для создания прокси
postProcessAfterInitialization
Конечно, когда bean-компонент зависит от bean-компонента UserService, ему сначала необходимо инициализировать UserServiceImpl и применить постпроцессор.При применении постпроцессора InfrastructureAdvisorAutoProxyCreator обнаруживается, что UserServiceImpl использует аннотацию @Transaction, поэтому его следует проксировать, то есть здесь создается прокси, а экземпляр прокси возвращается в Бин, который зависит от UserService; анализируем процесс создания прокси,
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
Получите перехватчик метода TransactionInterceptor перед созданием прокси, а затем передайте его методу createProxy для завершения; процесс в createProxy является процессом Spring AOP, перехватчик метода организован в Advisor, а InvocationHandler создается в зависимости от того, используется интерфейс прокси.InvocationHandler здесь JdkDynamicAopProxy, и, наконец, он используется для создания экземпляра прокси,
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
invoke on proxy instance
Когда вызывается метод службы, фактически вызывается метод класса прокси.Экземпляр прокси, созданный JdkDynamicAopProxy в качестве InvocationHandler, в конечном итоге будет вызываться для метода вызова JdkDynamicAopProxy (Примечание: динамический прокси JDK), и метод вызова будет использовать предыдущий TransactionInterceptor,
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
ReflectiveMethodInvocation в конечном итоге вызовет метод вызова TransactionInterceptor.
TransactionInterceptor
Вызов TransactionInterceptor может найти следы управления транзакциями, для выполнения такой операции он вызывает invokeWithinTransaction родительского класса.
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
tm — это TransactionManager, от которого зависит TransactionInterceptor на этапе регистрации BeanDefinition; здесь он не показан.
tx.begin()
tx.commit()
tx.rollback()
Подождите, на самом деле все эти методы, связанные с транзакциями, выполняются TransactionManager, отслеживая вышеуказанное.createTransactionIfNecessary
метод, вы обнаружите, что операция doBegin будет выполняться в методе getTransaction диспетчера транзакций.Метод doBegin создаст соединение с базой данных из источника данных диспетчера транзакций и, наконец, свяжет источник данных с соединением и привяжет его к текущему поток, то есть он существует в ThreadLocal,
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
commitTransactionAfterReturning
Это эквивалентноtx.commit
,completeTransactionAfterThrowing
эквивалентноtx.rollback
;
TransactionInterceptor vs TransactionManager vs JdbcTemplate vs TransactionSynchronizationManager
Это было разобрано здесь. Spring использует BeanPostProcessor для прокси-классов, аннотированных с помощью @Transactional, а InvocationHandler, созданный BeanPostProcessor, использует перехватчик метода TransactionInterceptor. В этом перехватчике метода реализована операция управления транзакциями. Операция для достижения управления транзакциями Это делается диспетчером транзакций TransactionInterceptor, а диспетчер транзакций использует источник данных для создания ссылки на базу данных;
Однако остается проблема, как отдать Connection, созданный TransactionInterceptorbusinessLogic
, даваяUserServiceImpl#register
используется; здесь мы используемJdbcTemplate
В качестве примера для анализа,JdbcTemplate
Операция должна получить соединение с базой данных Connection, которое реализовано следующим образом:
Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
Отслеживая код DataSourceUtils, вы в конечном итоге найдете такой код
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
Это соответствует предыдущим привязкам, привязка к ThreadLocal, а получение из ThreadLocal связано с TransactionSynchronizationManager, на самом деле mybatis использует управление транзакциями Spring, и его способ получения ссылок также использует DataSourceUtils;