Skip to content

Commit

Permalink
Verify status quo when searching for non-inherited composed annotations
Browse files Browse the repository at this point in the history
Issue: SPR-11475
  • Loading branch information
sbrannen committed Feb 23, 2014
1 parent f895096 commit 90b938a
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 5 deletions.
@@ -0,0 +1,104 @@
/*
* Copyright 2002-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.core.annotation;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.junit.Test;

import static org.junit.Assert.*;

/**
* Unit tests for {@link AnnotatedElementUtils}.
*
* @author Sam Brannen
* @since 4.0.3
*/
public class AnnotatedElementUtilsTests {

@Test
public void getAnnotationAttributesFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
AnnotationAttributes attributes = AnnotatedElementUtils.getAnnotationAttributes(
SubSubClassWithInheritedAnnotation.class, Transactional.class.getName());
assertNotNull(attributes);

// By inspecting SubSubClassWithInheritedAnnotation, one might expect that the
// readOnly flag should be true, since the immediate superclass is annotated with
// @Composed2; however, with the current implementation the readOnly flag will be
// false since @Transactional is declared as @Inherited.
assertFalse("readOnly flag for SubSubClassWithInheritedAnnotation", attributes.getBoolean("readOnly"));
}

@Test
public void getAnnotationAttributesFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
AnnotationAttributes attributes = AnnotatedElementUtils.getAnnotationAttributes(
SubSubClassWithInheritedComposedAnnotation.class, Transactional.class.getName());
assertNotNull(attributes);

// By inspecting SubSubClassWithInheritedComposedAnnotation, one might expect that
// the readOnly flag should be true, since the immediate superclass is annotated
// with @Composed2; however, with the current implementation the readOnly flag
// will be false since @Composed1 is declared as @Inherited.
assertFalse("readOnly flag", attributes.getBoolean("readOnly"));
}


// -------------------------------------------------------------------------

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Transactional {

boolean readOnly() default false;
}

@Transactional
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Composed1 {
}

@Transactional(readOnly = true)
@Retention(RetentionPolicy.RUNTIME)
@interface Composed2 {
}

@Transactional
static class ClassWithInheritedAnnotation {
}

@Composed2
static class SubClassWithInheritedAnnotation extends ClassWithInheritedAnnotation {
}

static class SubSubClassWithInheritedAnnotation extends SubClassWithInheritedAnnotation {
}

@Composed1
static class ClassWithInheritedComposedAnnotation {
}

@Composed2
static class SubClassWithInheritedComposedAnnotation extends ClassWithInheritedComposedAnnotation {
}

static class SubSubClassWithInheritedComposedAnnotation extends SubClassWithInheritedComposedAnnotation {
}

}
Expand Up @@ -100,14 +100,40 @@ public void findMethodAnnotationOnBridgeMethod() throws Exception {
// }

@Test
public void findAnnotationPrefersInterfacesOverLocalMetaAnnotations() {
public void findAnnotationFavorsInterfacesOverLocalMetaAnnotations() {
Component component = AnnotationUtils.findAnnotation(
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
assertNotNull(component);

// By inspecting ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface, one
// might expect that "meta2" should be found; however, with the current
// implementation "meta1" will be found.
assertEquals("meta1", component.value());
}

@Test
public void findAnnotationFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
Transactional transactional = AnnotationUtils.findAnnotation(SubSubClassWithInheritedAnnotation.class,
Transactional.class);
assertNotNull(transactional);

// By inspecting SubSubClassWithInheritedAnnotation, one might expect that the
// readOnly flag should be true, since the immediate superclass is annotated with
// @Composed2; however, with the current implementation the readOnly flag will be
// false since @Transactional is declared as @Inherited.
assertFalse("readOnly flag for SubSubClassWithInheritedAnnotation", transactional.readOnly());
}

@Test
public void findAnnotationFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
Component component = AnnotationUtils.findAnnotation(SubSubClassWithInheritedMetaAnnotation.class,
Component.class);
assertNotNull(component);

// By inspecting SubSubClassWithInheritedMetaAnnotation, one might expect that
// "meta2" should be found, since the immediate superclass is annotated with
// @Meta2; however, with the current implementation "meta1" will be found since
// @Meta1 is declared as @Inherited.
assertEquals("meta1", component.value());
}

Expand Down Expand Up @@ -350,14 +376,15 @@ public void getRepeatableFromMethod() throws Exception {
}


@Component(value="meta1")
@Component(value = "meta1")
@Order
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Meta1 {
}

@Component(value="meta2")
@Transactional
@Component(value = "meta2")
@Transactional(readOnly = true)
@Retention(RetentionPolicy.RUNTIME)
@interface Meta2 {
}
Expand Down Expand Up @@ -395,6 +422,28 @@ static interface InterfaceWithMetaAnnotation {
static class ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface implements InterfaceWithMetaAnnotation {
}

@Meta1
static class ClassWithInheritedMetaAnnotation {
}

@Meta2
static class SubClassWithInheritedMetaAnnotation extends ClassWithInheritedMetaAnnotation {
}

static class SubSubClassWithInheritedMetaAnnotation extends SubClassWithInheritedMetaAnnotation {
}

@Transactional
static class ClassWithInheritedAnnotation {
}

@Meta2
static class SubClassWithInheritedAnnotation extends ClassWithInheritedAnnotation {
}

static class SubSubClassWithInheritedAnnotation extends SubClassWithInheritedAnnotation {
}

@MetaMeta
static class MetaMetaAnnotatedClass {
}
Expand Down Expand Up @@ -453,6 +502,8 @@ public void overrideWithoutNewAnnotation() {
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Transactional {

boolean readOnly() default false;
}

public static abstract class Foo<T> {
Expand Down

0 comments on commit 90b938a

Please sign in to comment.