Browse Source

Transaction annotations on interface methods with CGLIB proxies

Issue: SPR-14322
pull/1408/head
Juergen Hoeller 9 years ago
parent
commit
42d6d7ec4e
  1. 88
      spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java
  2. 12
      spring-tx/src/main/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSource.java
  3. 5
      spring-tx/src/main/java/org/springframework/transaction/annotation/SpringTransactionAnnotationParser.java
  4. 3
      spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java

88
spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java

@ -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 @Test
public void testDoesNotResolveTxAnnotationOnMethodFromClassImplementingAnnotatedInterface() throws Exception { public void noRollbackOnImplementationOfAnnotatedInterface() throws Throwable {
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
Method method = ImplementsAnnotatedInterface.class.getMethod("echo", Throwable.class);
TransactionAttribute ta = atas.getTransactionAttribute(method, ImplementsAnnotatedInterface.class);
assertNull(ta);
}
@Test
public void testDefaultRollbackOnImplementationOfAnnotatedInterface() 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);

12
spring-tx/src/main/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSource.java

@ -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;

5
spring-tx/src/main/java/org/springframework/transaction/annotation/SpringTransactionAnnotationParser.java

@ -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);
} }

3
spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java

@ -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);

Loading…
Cancel
Save