Transactional失效場景
/**
* @author zhoujy
**/
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;
@Transactional
void insertTestWrongModifier() {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}
}java
@Component
public class InvokcationService {
@Resource
private TestServiceImpl testService;
public void invokeInsertTestWrongModifier(){
//調用@Transactional標註的默認訪問符方法
testService.insertTestWrongModifier();
}
}web
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Resource
InvokcationService invokcationService;
@Test
public void testInvoke(){
invokcationService.invokeInsertTestWrongModifier();
}
}spring
TestServiceImpl#insertTestWrongModifier
方法改成public的話將會正常開啓事務,testMapper.insert(new Test(10,20,30));將會進行回滾。
第二種失效場景
/**
* @author zhoujy
**/
@Component
public class TestServiceImpl implements TestService {
@Resource
TestMapper testMapper;
@Transactional
public void insertTestInnerInvoke() {
//正常public修飾符的事務方法
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}
public void testInnerInvoke(){
//類內部調用@Transactional標註的方法。
insertTestInnerInvoke();
}
}數據庫
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Resource
TestServiceImpl testService;
/**
* 測試內部調用@Transactional標註方法
*/
@Test
public void testInnerInvoke(){
//測試外部調用事務方法是否正常
//testService.insertTestInnerInvoke();
//測試內部調用事務方法是否正常
testService.testInnerInvoke();
}
}微信
第三種失效場景
/**
* @author zhoujy
**/
@Component
public class TestServiceImpl implements TestService {
@Resource
TestMapper testMapper;
@Transactional
public void insertTestCatchException() {
try {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
//運行期間拋異常
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}catch (Exception e){
System.out.println("i catch exception");
}
}
}app
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Resource
TestServiceImpl testService;
@Test
public void testCatchException(){
testService.insertTestCatchException();
}
}編輯器
@Transactional註解不起做用原理分析
第一種場景分析
@Transactional
註解標註方法修飾符爲非public時,
@Transactional
註解將會不起做用。這裏分析 的緣由是,
@Transactional
是基於動態代理實現的,
@Transactional
註解實現原理中分析了實現方法,在bean初始化過程當中,對含有
@Transactional
標註的bean實例建立代理對象,這裏就存在一個spring掃描
@Transactional
註解信息的過程,不幸的是源碼中體現,標註@Transactional的方法若是修飾符不是public,那麼就默認方法的
@Transactional
信息爲空,那麼將不會對bean進行代理對象建立或者不會對方法進行代理調用
@Transactional
註解實現原理中,介紹瞭如何斷定一個bean是否建立代理對象,大概邏輯是。根據spring建立好一個aop切點
BeanFactoryTransactionAttributeSourceAdvisor
實例,遍歷當前bean的class的方法對象,判斷方法上面的註解信息是否包含
@Transactional
,若是bean任何一個方法包含
@Transactional
註解信息,那麼就是適配這個BeanFactoryTransactionAttributeSourceAdvisor切點。則須要建立代理對象,而後代理邏輯爲咱們管理事務開閉邏輯。
AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
//遍歷class的方法對象
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
//適配查詢方法上的@Transactional註解信息
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}ide
AbstractFallbackTransactionAttributeSource#getTransactionAttribute
-
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
//非public 方法,返回@Transactional信息一概是null
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
//後面省略.......
}函數
不建立代理對象


不進行代理調用
/**
* @author zhoujy
**/
@Component
public class TestServiceImpl implements TestService {
@Resource
TestMapper testMapper;
@Override
@Transactional
public void insertTest() {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}
@Transactional
void insertTestWrongModifier() {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}
}
源碼分析
CglibAopProxy.DynamicAdvisedInterceptor#intercept
,有一個邏輯以下,目的是獲取當前被代理對象的當前須要執行的method適配的aop邏輯。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
AbstractFallbackTransactionAttributeSource#getTransactionAttribute
-
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute
第二種場景分析
既然事務管理是基於動態代理對象的代理邏輯實現的,那麼若是在類內部調用類內部的事務方法,這個調用事務方法的過程並非經過代理對象來調用的,而是直接經過this對象來調用方法,繞過的代理對象,確定就是沒有代理邏輯了。
/**
* @author zhoujy
**/
@Component
public class TestServiceImpl implements TestService {
@Resource
TestMapper testMapper;
@Resource
TestServiceImpl testServiceImpl;
@Transactional
public void insertTestInnerInvoke() {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}
public void testInnerInvoke(){
//內部調用事務方法
testServiceImpl.insertTestInnerInvoke();
}
}
第三種場景分析
TransactionAspectSupport#invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
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
//異常時,在catch邏輯中回滾事務
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//提交事務
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
//....................
}
}
有道無術,術可成;有術無道,止於術
歡迎你們關注Java之道公衆號
好文章,我在看❤️
本文分享自微信公衆號 - Hollis(hollischuang)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。