Transaction annotations on interface methods with CGLIB proxies
Issue: SPR-14322
This commit is contained in:
@@ -16,14 +16,10 @@
|
|||||||
|
|
||||||
package org.springframework.transaction.aspectj;
|
package org.springframework.transaction.aspectj;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.tests.transaction.CallCountingTransactionManager;
|
import org.springframework.tests.transaction.CallCountingTransactionManager;
|
||||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
|
||||||
import org.springframework.transaction.interceptor.TransactionAttribute;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@@ -65,7 +61,7 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCommitOnAnnotatedProtectedMethod() throws Throwable {
|
public void commitOnAnnotatedProtectedMethod() throws Throwable {
|
||||||
txManager.clear();
|
txManager.clear();
|
||||||
assertEquals(0, txManager.begun);
|
assertEquals(0, txManager.begun);
|
||||||
beanWithAnnotatedProtectedMethod.doInTransaction();
|
beanWithAnnotatedProtectedMethod.doInTransaction();
|
||||||
@@ -73,7 +69,7 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCommitOnAnnotatedPrivateMethod() throws Throwable {
|
public void commitOnAnnotatedPrivateMethod() throws Throwable {
|
||||||
txManager.clear();
|
txManager.clear();
|
||||||
assertEquals(0, txManager.begun);
|
assertEquals(0, txManager.begun);
|
||||||
beanWithAnnotatedPrivateMethod.doSomething();
|
beanWithAnnotatedPrivateMethod.doSomething();
|
||||||
@@ -81,15 +77,15 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoCommitOnNonAnnotatedNonPublicMethodInTransactionalType() throws Throwable {
|
public void commitOnNonAnnotatedNonPublicMethodInTransactionalType() throws Throwable {
|
||||||
txManager.clear();
|
txManager.clear();
|
||||||
assertEquals(0,txManager.begun);
|
assertEquals(0, txManager.begun);
|
||||||
annotationOnlyOnClassWithNoInterface.nonTransactionalMethod();
|
annotationOnlyOnClassWithNoInterface.nonTransactionalMethod();
|
||||||
assertEquals(0,txManager.begun);
|
assertEquals(0, txManager.begun);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCommitOnAnnotatedMethod() throws Throwable {
|
public void commitOnAnnotatedMethod() throws Throwable {
|
||||||
txManager.clear();
|
txManager.clear();
|
||||||
assertEquals(0, txManager.begun);
|
assertEquals(0, txManager.begun);
|
||||||
methodAnnotationOnly.echo(null);
|
methodAnnotationOnly.echo(null);
|
||||||
@@ -97,7 +93,7 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotTransactional() throws Throwable {
|
public void notTransactional() throws Throwable {
|
||||||
txManager.clear();
|
txManager.clear();
|
||||||
assertEquals(0, txManager.begun);
|
assertEquals(0, txManager.begun);
|
||||||
new NotTransactional().noop();
|
new NotTransactional().noop();
|
||||||
@@ -105,15 +101,10 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultCommitOnAnnotatedClass() throws Throwable {
|
public void defaultCommitOnAnnotatedClass() throws Throwable {
|
||||||
final Exception ex = new Exception();
|
final Exception ex = new Exception();
|
||||||
try {
|
try {
|
||||||
testRollback(new TransactionOperationCallback() {
|
testRollback(() -> annotationOnlyOnClassWithNoInterface.echo(ex), false);
|
||||||
@Override
|
|
||||||
public Object performTransactionalOperation() throws Throwable {
|
|
||||||
return annotationOnlyOnClassWithNoInterface.echo(ex);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
fail("Should have thrown Exception");
|
fail("Should have thrown Exception");
|
||||||
}
|
}
|
||||||
catch (Exception ex2) {
|
catch (Exception ex2) {
|
||||||
@@ -122,15 +113,10 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultRollbackOnAnnotatedClass() throws Throwable {
|
public void defaultRollbackOnAnnotatedClass() throws Throwable {
|
||||||
final RuntimeException ex = new RuntimeException();
|
final RuntimeException ex = new RuntimeException();
|
||||||
try {
|
try {
|
||||||
testRollback(new TransactionOperationCallback() {
|
testRollback(() -> annotationOnlyOnClassWithNoInterface.echo(ex), true);
|
||||||
@Override
|
|
||||||
public Object performTransactionalOperation() throws Throwable {
|
|
||||||
return annotationOnlyOnClassWithNoInterface.echo(ex);
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
fail("Should have thrown RuntimeException");
|
fail("Should have thrown RuntimeException");
|
||||||
}
|
}
|
||||||
catch (RuntimeException ex2) {
|
catch (RuntimeException ex2) {
|
||||||
@@ -139,15 +125,10 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultCommitOnSubclassOfAnnotatedClass() throws Throwable {
|
public void defaultCommitOnSubclassOfAnnotatedClass() throws Throwable {
|
||||||
final Exception ex = new Exception();
|
final Exception ex = new Exception();
|
||||||
try {
|
try {
|
||||||
testRollback(new TransactionOperationCallback() {
|
testRollback(() -> new SubclassOfClassWithTransactionalAnnotation().echo(ex), false);
|
||||||
@Override
|
|
||||||
public Object performTransactionalOperation() throws Throwable {
|
|
||||||
return new SubclassOfClassWithTransactionalAnnotation().echo(ex);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
fail("Should have thrown Exception");
|
fail("Should have thrown Exception");
|
||||||
}
|
}
|
||||||
catch (Exception ex2) {
|
catch (Exception ex2) {
|
||||||
@@ -156,15 +137,10 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultCommitOnSubclassOfClassWithTransactionalMethodAnnotated() throws Throwable {
|
public void defaultCommitOnSubclassOfClassWithTransactionalMethodAnnotated() throws Throwable {
|
||||||
final Exception ex = new Exception();
|
final Exception ex = new Exception();
|
||||||
try {
|
try {
|
||||||
testRollback(new TransactionOperationCallback() {
|
testRollback(() -> new SubclassOfClassWithTransactionalMethodAnnotation().echo(ex), false);
|
||||||
@Override
|
|
||||||
public Object performTransactionalOperation() throws Throwable {
|
|
||||||
return new SubclassOfClassWithTransactionalMethodAnnotation().echo(ex);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
fail("Should have thrown Exception");
|
fail("Should have thrown Exception");
|
||||||
}
|
}
|
||||||
catch (Exception ex2) {
|
catch (Exception ex2) {
|
||||||
@@ -173,41 +149,19 @@ public class TransactionAspectTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultCommitOnImplementationOfAnnotatedInterface() throws Throwable {
|
public void noCommitOnImplementationOfAnnotatedInterface() throws Throwable {
|
||||||
final Exception ex = new Exception();
|
final Exception ex = new Exception();
|
||||||
testNotTransactional(new TransactionOperationCallback() {
|
testNotTransactional(() -> new ImplementsAnnotatedInterface().echo(ex), ex);
|
||||||
@Override
|
|
||||||
public Object performTransactionalOperation() throws Throwable {
|
|
||||||
return new ImplementsAnnotatedInterface().echo(ex);
|
|
||||||
}
|
|
||||||
}, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: resolution does not occur. Thus we can't make a class transactional if
|
|
||||||
* it implements a transactionally annotated interface. This behavior could only
|
|
||||||
* be changed in AbstractFallbackTransactionAttributeSource in Spring proper.
|
|
||||||
* See SPR-14322.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testDoesNotResolveTxAnnotationOnMethodFromClassImplementingAnnotatedInterface() throws Exception {
|
|
||||||
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
|
|
||||||
Method method = ImplementsAnnotatedInterface.class.getMethod("echo", Throwable.class);
|
|
||||||
TransactionAttribute ta = atas.getTransactionAttribute(method, ImplementsAnnotatedInterface.class);
|
|
||||||
assertNull(ta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultRollbackOnImplementationOfAnnotatedInterface() throws Throwable {
|
public void noRollbackOnImplementationOfAnnotatedInterface() throws Throwable {
|
||||||
final Exception rollbackProvokingException = new RuntimeException();
|
final Exception rollbackProvokingException = new RuntimeException();
|
||||||
testNotTransactional(new TransactionOperationCallback() {
|
testNotTransactional(() -> new ImplementsAnnotatedInterface().echo(rollbackProvokingException),
|
||||||
@Override
|
rollbackProvokingException);
|
||||||
public Object performTransactionalOperation() throws Throwable {
|
|
||||||
return new ImplementsAnnotatedInterface().echo(rollbackProvokingException);
|
|
||||||
}
|
|
||||||
}, rollbackProvokingException);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void testRollback(TransactionOperationCallback toc, boolean rollback) throws Throwable {
|
protected void testRollback(TransactionOperationCallback toc, boolean rollback) throws Throwable {
|
||||||
txManager.clear();
|
txManager.clear();
|
||||||
assertEquals(0, txManager.begun);
|
assertEquals(0, txManager.begun);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -150,12 +150,10 @@ public class AnnotationTransactionAttributeSource extends AbstractFallbackTransa
|
|||||||
* or {@code null} if none was found
|
* or {@code null} if none was found
|
||||||
*/
|
*/
|
||||||
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
|
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
|
||||||
if (ae.getAnnotations().length > 0) {
|
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
|
||||||
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
|
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
|
||||||
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
|
if (attr != null) {
|
||||||
if (attr != null) {
|
return attr;
|
||||||
return attr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -39,7 +39,8 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
|
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
|
||||||
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
|
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
|
||||||
|
ae, Transactional.class, false, false);
|
||||||
if (attributes != null) {
|
if (attributes != null) {
|
||||||
return parseTransactionAnnotation(attributes);
|
return parseTransactionAnnotation(attributes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.springframework.transaction.annotation;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
@@ -166,7 +165,7 @@ public class EnableTransactionManagementTests {
|
|||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test @Ignore // TODO
|
@Test
|
||||||
public void spr14322FindsOnInterfaceWithCglibProxy() {
|
public void spr14322FindsOnInterfaceWithCglibProxy() {
|
||||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Spr14322ConfigB.class);
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Spr14322ConfigB.class);
|
||||||
TransactionalTestInterface bean = ctx.getBean(TransactionalTestInterface.class);
|
TransactionalTestInterface bean = ctx.getBean(TransactionalTestInterface.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user