AnnotationTransactionAttributeSource applies class-level metadata to user-level methods only

Issue: SPR-14095
This commit is contained in:
Juergen Hoeller 2016-03-30 10:21:22 +02:00
parent ea09e578b9
commit b7819e6ec8
3 changed files with 90 additions and 13 deletions

View File

@ -556,6 +556,7 @@ project("spring-tx") {
optional("com.ibm.websphere:uow:6.0.2.17") optional("com.ibm.websphere:uow:6.0.2.17")
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}") testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
testCompile("org.eclipse.persistence:javax.persistence:2.0.0") testCompile("org.eclipse.persistence:javax.persistence:2.0.0")
testCompile("org.codehaus.groovy:groovy-all:${groovyVersion}")
} }
} }

View File

@ -155,7 +155,7 @@ public abstract class AbstractFallbackTransactionAttributeSource implements Tran
// Second try is the transaction attribute on the target class. // Second try is the transaction attribute on the target class.
txAtt = findTransactionAttribute(specificMethod.getDeclaringClass()); txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAtt != null) { if (txAtt != null && ClassUtils.isUserLevelMethod(method)) {
return txAtt; return txAtt;
} }
@ -166,8 +166,12 @@ public abstract class AbstractFallbackTransactionAttributeSource implements Tran
return txAtt; return txAtt;
} }
// Last fallback is the class of the original method. // Last fallback is the class of the original method.
return findTransactionAttribute(method.getDeclaringClass()); txAtt = findTransactionAttribute(method.getDeclaringClass());
if (txAtt != null && ClassUtils.isUserLevelMethod(method)) {
return txAtt;
}
} }
return null; return null;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2016 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.
@ -21,9 +21,10 @@ import java.io.Serializable;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.ejb.TransactionAttributeType; import javax.ejb.TransactionAttributeType;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.Advised;
@ -54,7 +55,7 @@ public class AnnotationTransactionAttributeSourceTests {
TransactionInterceptor ti = new TransactionInterceptor(ptm, tas); TransactionInterceptor ti = new TransactionInterceptor(ptm, tas);
ProxyFactory proxyFactory = new ProxyFactory(); ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setInterfaces(new Class[] {ITestBean.class}); proxyFactory.setInterfaces(ITestBean.class);
proxyFactory.addAdvice(ti); proxyFactory.addAdvice(ti);
proxyFactory.setTarget(tb); proxyFactory.setTarget(tb);
ITestBean proxy = (ITestBean) proxyFactory.getProxy(); ITestBean proxy = (ITestBean) proxyFactory.getProxy();
@ -369,6 +370,20 @@ public class AnnotationTransactionAttributeSourceTests {
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior()); assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
} }
@Test
public void transactionAttributeDeclaredOnGroovyClass() throws Exception {
Method getAgeMethod = ITestBean.class.getMethod("getAge");
Method getNameMethod = ITestBean.class.getMethod("getName");
Method getMetaClassMethod = GroovyObject.class.getMethod("getMetaClass");
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
TransactionAttribute getAgeAttr = atas.getTransactionAttribute(getAgeMethod, GroovyTestBean.class);
assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getAgeAttr.getPropagationBehavior());
TransactionAttribute getNameAttr = atas.getTransactionAttribute(getNameMethod, GroovyTestBean.class);
assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, getNameAttr.getPropagationBehavior());
assertNull(atas.getTransactionAttribute(getMetaClassMethod, GroovyTestBean.class));
}
interface ITestBean { interface ITestBean {
@ -470,7 +485,7 @@ public class AnnotationTransactionAttributeSourceTests {
} }
@Override @Override
@Transactional(rollbackFor=Exception.class) @Transactional(rollbackFor = Exception.class)
public int getAge() { public int getAge() {
return age; return age;
} }
@ -543,8 +558,8 @@ public class AnnotationTransactionAttributeSourceTests {
} }
@Override @Override
@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.REPEATABLE_READ, timeout=5, @Transactional(propagation = Propagation.REQUIRES_NEW, isolation=Isolation.REPEATABLE_READ,
readOnly=true, rollbackFor=Exception.class, noRollbackFor={IOException.class}) timeout = 5, readOnly = true, rollbackFor = Exception.class, noRollbackFor = IOException.class)
public int getAge() { public int getAge() {
return age; return age;
} }
@ -556,7 +571,7 @@ public class AnnotationTransactionAttributeSourceTests {
} }
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class}) @Transactional(rollbackFor = Exception.class, noRollbackFor = IOException.class)
static class TestBean4 implements ITestBean3 { static class TestBean4 implements ITestBean3 {
private String name; private String name;
@ -594,7 +609,7 @@ public class AnnotationTransactionAttributeSourceTests {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class}) @Transactional(rollbackFor = Exception.class, noRollbackFor = IOException.class)
@interface Tx { @interface Tx {
} }
@ -618,13 +633,13 @@ public class AnnotationTransactionAttributeSourceTests {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class}) @Transactional(rollbackFor = Exception.class, noRollbackFor = IOException.class)
@interface TxWithAttribute { @interface TxWithAttribute {
boolean readOnly(); boolean readOnly();
} }
@TxWithAttribute(readOnly=true) @TxWithAttribute(readOnly = true)
static class TestBean7 { static class TestBean7 {
public int getAge() { public int getAge() {
@ -641,11 +656,14 @@ public class AnnotationTransactionAttributeSourceTests {
} }
} }
@TxWithAttribute(readOnly = true) @TxWithAttribute(readOnly = true)
interface TestInterface9 { interface TestInterface9 {
int getAge(); int getAge();
} }
static class TestBean9 implements TestInterface9 { static class TestBean9 implements TestInterface9 {
@Override @Override
@ -654,12 +672,14 @@ public class AnnotationTransactionAttributeSourceTests {
} }
} }
interface TestInterface10 { interface TestInterface10 {
@TxWithAttribute(readOnly=true) @TxWithAttribute(readOnly = true)
int getAge(); int getAge();
} }
static class TestBean10 implements TestInterface10 { static class TestBean10 implements TestInterface10 {
@Override @Override
@ -888,4 +908,56 @@ public class AnnotationTransactionAttributeSourceTests {
} }
} }
@Transactional
static class GroovyTestBean implements ITestBean, GroovyObject {
private String name;
private int age;
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public int getAge() {
return age;
}
@Override
public void setAge(int age) {
this.age = age;
}
@Override
public Object invokeMethod(String name, Object args) {
return null;
}
@Override
public Object getProperty(String propertyName) {
return null;
}
@Override
public void setProperty(String propertyName, Object newValue) {
}
@Override
public MetaClass getMetaClass() {
return null;
}
@Override
public void setMetaClass(MetaClass metaClass) {
}
}
} }