custom stereotype annotations can be meta-annotated with @Service, @Controller etc as well; @Scope and @Transactional are now supported as meta-annotations on custom annotations

This commit is contained in:
Juergen Hoeller
2009-04-26 11:41:06 +00:00
parent ac16101f98
commit cea8f7f69e
18 changed files with 289 additions and 144 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.springframework.transaction.annotation;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
@@ -35,6 +36,14 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
Transactional ann = ae.getAnnotation(Transactional.class);
if (ann == null) {
for (Annotation metaAnn : ae.getAnnotations()) {
ann = metaAnn.annotationType().getAnnotation(Transactional.class);
if (ann != null) {
break;
}
}
}
if (ann != null) {
return parseTransactionAnnotation(ann);
}
@@ -51,23 +60,23 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
rbta.setReadOnly(ann.readOnly());
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
Class[] rbf = ann.rollbackFor();
for (int i = 0; i < rbf.length; ++i) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbf[i]);
for (Class rbRule : rbf) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] rbfc = ann.rollbackForClassName();
for (int i = 0; i < rbfc.length; ++i) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbfc[i]);
for (String rbRule : rbfc) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
Class[] nrbf = ann.noRollbackFor();
for (int i = 0; i < nrbf.length; ++i) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(nrbf[i]);
for (Class rbRule : nrbf) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] nrbfc = ann.noRollbackForClassName();
for (int i = 0; i < nrbfc.length; ++i) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(nrbfc[i]);
for (String rbRule : nrbfc) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
rbta.getRollbackRules().addAll(rollBackRules);

View File

@@ -18,11 +18,15 @@ package org.springframework.transaction.annotation;
import java.io.IOException;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import javax.ejb.TransactionAttributeType;
import junit.framework.TestCase;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
@@ -38,8 +42,9 @@ import org.springframework.util.SerializationTestUtils;
* @author Colin Sampaleanu
* @author Juergen Hoeller
*/
public class AnnotationTransactionAttributeSourceTests extends TestCase {
public class AnnotationTransactionAttributeSourceTests {
@Test
public void testSerializable() throws Exception {
TestBean1 tb = new TestBean1();
CallCountingTransactionManager ptm = new CallCountingTransactionManager();
@@ -63,6 +68,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
assertEquals(2, serializedPtm.commits);
}
@Test
public void testNullOrEmpty() throws Exception {
Method method = Empty.class.getMethod("getAge", (Class[]) null);
@@ -77,6 +83,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
* Test the important case where the invocation is on a proxied interface method
* but the attribute is defined on the target class.
*/
@Test
public void testTransactionAttributeDeclaredOnClassMethod() throws Exception {
Method classMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
@@ -91,6 +98,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
/**
* Test case where attribute is on the interface method.
*/
@Test
public void testTransactionAttributeDeclaredOnInterfaceMethodOnly() throws Exception {
Method interfaceMethod = ITestBean2.class.getMethod("getAge", (Class[]) null);
@@ -104,6 +112,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
/**
* Test that when an attribute exists on both class and interface, class takes precedence.
*/
@Test
public void testTransactionAttributeOnTargetClassMethodOverridesAttributeOnInterfaceMethod() throws Exception {
Method interfaceMethod = ITestBean3.class.getMethod("getAge", (Class[]) null);
Method interfaceMethod2 = ITestBean3.class.getMethod("getName", (Class[]) null);
@@ -124,6 +133,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, actual2.getPropagationBehavior());
}
@Test
public void testRollbackRulesAreApplied() throws Exception {
Method method = TestBean3.class.getMethod("getAge", (Class[]) null);
@@ -153,6 +163,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
* Test that transaction attribute is inherited from class
* if not specified on method.
*/
@Test
public void testDefaultsToClassTransactionAttribute() throws Exception {
Method method = TestBean4.class.getMethod("getAge", (Class[]) null);
@@ -165,6 +176,33 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules());
}
@Test
public void testCustomClassAttributeDetected() throws Exception {
Method method = TestBean5.class.getMethod("getAge", (Class[]) null);
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
TransactionAttribute actual = atas.getTransactionAttribute(method, TestBean5.class);
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class));
assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules());
}
@Test
public void testCustomMethodAttributeDetected() throws Exception {
Method method = TestBean6.class.getMethod("getAge", (Class[]) null);
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
TransactionAttribute actual = atas.getTransactionAttribute(method, TestBean5.class);
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class));
assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules());
}
@Test
public void testTransactionAttributeDeclaredOnClassMethodWithEjb3() throws Exception {
Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null);
@@ -176,6 +214,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
}
@Test
public void testTransactionAttributeDeclaredOnClassWithEjb3() throws Exception {
Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null);
@@ -187,6 +226,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
}
@Test
public void testTransactionAttributeDeclaredOnInterfaceWithEjb3() throws Exception {
Method getAgeMethod = ITestEjb.class.getMethod("getAge", (Class[]) null);
Method getNameMethod = ITestEjb.class.getMethod("getName", (Class[]) null);
@@ -401,6 +441,31 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class})
public @interface Tx {
}
@Tx
public static class TestBean5 {
public int getAge() {
return 10;
}
}
public static class TestBean6 {
@Tx
public int getAge() {
return 10;
}
}
public static interface Foo<T> {
void doSomething(T theArgument);