Skip to content

Commit b7819e6

Browse files
committed
AnnotationTransactionAttributeSource applies class-level metadata to user-level methods only
Issue: SPR-14095
1 parent ea09e57 commit b7819e6

File tree

3 files changed

+90
-13
lines changed

3 files changed

+90
-13
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ project("spring-tx") {
556556
optional("com.ibm.websphere:uow:6.0.2.17")
557557
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
558558
testCompile("org.eclipse.persistence:javax.persistence:2.0.0")
559+
testCompile("org.codehaus.groovy:groovy-all:${groovyVersion}")
559560
}
560561
}
561562

spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ protected TransactionAttribute computeTransactionAttribute(Method method, Class<
155155

156156
// Second try is the transaction attribute on the target class.
157157
txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
158-
if (txAtt != null) {
158+
if (txAtt != null && ClassUtils.isUserLevelMethod(method)) {
159159
return txAtt;
160160
}
161161

@@ -166,8 +166,12 @@ protected TransactionAttribute computeTransactionAttribute(Method method, Class<
166166
return txAtt;
167167
}
168168
// Last fallback is the class of the original method.
169-
return findTransactionAttribute(method.getDeclaringClass());
169+
txAtt = findTransactionAttribute(method.getDeclaringClass());
170+
if (txAtt != null && ClassUtils.isUserLevelMethod(method)) {
171+
return txAtt;
172+
}
170173
}
174+
171175
return null;
172176
}
173177

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

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,9 +21,10 @@
2121
import java.lang.annotation.Retention;
2222
import java.lang.annotation.RetentionPolicy;
2323
import java.lang.reflect.Method;
24-
2524
import javax.ejb.TransactionAttributeType;
2625

26+
import groovy.lang.GroovyObject;
27+
import groovy.lang.MetaClass;
2728
import org.junit.Test;
2829

2930
import org.springframework.aop.framework.Advised;
@@ -54,7 +55,7 @@ public void serializable() throws Exception {
5455
TransactionInterceptor ti = new TransactionInterceptor(ptm, tas);
5556

5657
ProxyFactory proxyFactory = new ProxyFactory();
57-
proxyFactory.setInterfaces(new Class[] {ITestBean.class});
58+
proxyFactory.setInterfaces(ITestBean.class);
5859
proxyFactory.addAdvice(ti);
5960
proxyFactory.setTarget(tb);
6061
ITestBean proxy = (ITestBean) proxyFactory.getProxy();
@@ -369,6 +370,20 @@ public void transactionAttributeDeclaredOnInterfaceWithJta() throws Exception {
369370
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
370371
}
371372

373+
@Test
374+
public void transactionAttributeDeclaredOnGroovyClass() throws Exception {
375+
Method getAgeMethod = ITestBean.class.getMethod("getAge");
376+
Method getNameMethod = ITestBean.class.getMethod("getName");
377+
Method getMetaClassMethod = GroovyObject.class.getMethod("getMetaClass");
378+
379+
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
380+
TransactionAttribute getAgeAttr = atas.getTransactionAttribute(getAgeMethod, GroovyTestBean.class);
381+
assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior());
382+
TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, GroovyTestBean.class);
383+
assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getNameAttr.getPropagationBehavior());
384+
assertNull(atas.getTransactionAttribute(getMetaClassMethod, GroovyTestBean.class));
385+
}
386+
372387

373388
interface ITestBean {
374389

@@ -470,7 +485,7 @@ public void setName(String name) {
470485
}
471486

472487
@Override
473-
@Transactional(rollbackFor=Exception.class)
488+
@Transactional(rollbackFor = Exception.class)
474489
public int getAge() {
475490
return age;
476491
}
@@ -543,8 +558,8 @@ public void setName(String name) {
543558
}
544559

545560
@Override
546-
@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.REPEATABLE_READ, timeout=5,
547-
readOnly=true, rollbackFor=Exception.class, noRollbackFor={IOException.class})
561+
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.REPEATABLE_READ,
562+
timeout = 5, readOnly = true, rollbackFor = Exception.class, noRollbackFor = IOException.class)
548563
public int getAge() {
549564
return age;
550565
}
@@ -556,7 +571,7 @@ public void setAge(int age) {
556571
}
557572

558573

559-
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class})
574+
@Transactional(rollbackFor = Exception.class, noRollbackFor = IOException.class)
560575
static class TestBean4 implements ITestBean3 {
561576

562577
private String name;
@@ -594,7 +609,7 @@ public void setAge(int age) {
594609

595610

596611
@Retention(RetentionPolicy.RUNTIME)
597-
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class})
612+
@Transactional(rollbackFor = Exception.class, noRollbackFor = IOException.class)
598613
@interface Tx {
599614
}
600615

@@ -618,13 +633,13 @@ public int getAge() {
618633

619634

620635
@Retention(RetentionPolicy.RUNTIME)
621-
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class})
636+
@Transactional(rollbackFor = Exception.class, noRollbackFor = IOException.class)
622637
@interface TxWithAttribute {
623638
boolean readOnly();
624639
}
625640

626641

627-
@TxWithAttribute(readOnly=true)
642+
@TxWithAttribute(readOnly = true)
628643
static class TestBean7 {
629644

630645
public int getAge() {
@@ -641,11 +656,14 @@ public int getAge() {
641656
}
642657
}
643658

659+
644660
@TxWithAttribute(readOnly = true)
645661
interface TestInterface9 {
662+
646663
int getAge();
647664
}
648665

666+
649667
static class TestBean9 implements TestInterface9 {
650668

651669
@Override
@@ -654,12 +672,14 @@ public int getAge() {
654672
}
655673
}
656674

675+
657676
interface TestInterface10 {
658677

659-
@TxWithAttribute(readOnly=true)
678+
@TxWithAttribute(readOnly = true)
660679
int getAge();
661680
}
662681

682+
663683
static class TestBean10 implements TestInterface10 {
664684

665685
@Override
@@ -888,4 +908,56 @@ public void setAge(int age) {
888908
}
889909
}
890910

911+
912+
@Transactional
913+
static class GroovyTestBean implements ITestBean, GroovyObject {
914+
915+
private String name;
916+
917+
private int age;
918+
919+
@Override
920+
public String getName() {
921+
return name;
922+
}
923+
924+
@Override
925+
public void setName(String name) {
926+
this.name = name;
927+
}
928+
929+
@Override
930+
public int getAge() {
931+
return age;
932+
}
933+
934+
@Override
935+
public void setAge(int age) {
936+
this.age = age;
937+
}
938+
939+
@Override
940+
public Object invokeMethod(String name, Object args) {
941+
return null;
942+
}
943+
944+
@Override
945+
public Object getProperty(String propertyName) {
946+
return null;
947+
}
948+
949+
@Override
950+
public void setProperty(String propertyName, Object newValue) {
951+
}
952+
953+
@Override
954+
public MetaClass getMetaClass() {
955+
return null;
956+
}
957+
958+
@Override
959+
public void setMetaClass(MetaClass metaClass) {
960+
}
961+
}
962+
891963
}

0 commit comments

Comments
 (0)