@Autowired, @Value and qualifiers may be used as meta-annotations for custom injection annotations

This commit is contained in:
Juergen Hoeller 2012-10-31 12:10:17 +01:00
parent 985cb9df11
commit 914a1b2088
3 changed files with 168 additions and 29 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 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.
@ -174,8 +174,31 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
for (Annotation annotation : annotationsToSearch) {
Class<? extends Annotation> type = annotation.annotationType();
boolean checkMeta = true;
boolean fallbackToMeta = false;
if (isQualifier(type)) {
if (!checkQualifier(bdHolder, annotation, typeConverter)) {
fallbackToMeta = true;
}
else {
checkMeta = false;
}
}
if (checkMeta) {
boolean foundMeta = false;
for (Annotation metaAnn : type.getAnnotations()) {
Class<? extends Annotation> metaType = metaAnn.annotationType();
if (isQualifier(metaType)) {
foundMeta = true;
// Only accept fallback match if @Qualifier annotation has a value...
// Otherwise it is just a marker for a custom qualifier annotation.
if ((fallbackToMeta && AnnotationUtils.getValue(metaAnn) == null) ||
!checkQualifier(bdHolder, metaAnn, typeConverter)) {
return false;
}
}
}
if (fallbackToMeta && !foundMeta) {
return false;
}
}
@ -210,18 +233,18 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
if (qualifier == null) {
Annotation targetAnnotation = null;
if (bd.getResolvedFactoryMethod() != null) {
targetAnnotation = bd.getResolvedFactoryMethod().getAnnotation(type);
targetAnnotation = AnnotationUtils.getAnnotation(bd.getResolvedFactoryMethod(), type);
}
if (targetAnnotation == null) {
// look for matching annotation on the target class
if (this.beanFactory != null) {
Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
if (beanType != null) {
targetAnnotation = ClassUtils.getUserClass(beanType).getAnnotation(type);
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
}
}
if (targetAnnotation == null && bd.hasBeanClass()) {
targetAnnotation = ClassUtils.getUserClass(bd.getBeanClass()).getAnnotation(type);
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);
}
}
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
@ -286,14 +309,27 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
protected Object findValue(Annotation[] annotationsToSearch) {
for (Annotation annotation : annotationsToSearch) {
if (this.valueAnnotationType.isInstance(annotation)) {
Object value = AnnotationUtils.getValue(annotation);
if (value == null) {
throw new IllegalStateException("Value annotation must have a value attribute");
}
return value;
return extractValue(annotation);
}
}
for (Annotation annotation : annotationsToSearch) {
Annotation metaAnn = annotation.annotationType().getAnnotation(this.valueAnnotationType);
if (metaAnn != null) {
return extractValue(metaAnn);
}
}
return null;
}
/**
* Extract the value attribute from the given annotation.
*/
protected Object extractValue(Annotation valueAnnotation) {
Object value = AnnotationUtils.getValue(valueAnnotation);
if (value == null) {
throw new IllegalStateException("Value annotation must have a value attribute");
}
return value;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2012 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.
@ -21,7 +21,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.aop.scope.ScopedProxyUtils;
@ -35,6 +34,8 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
import static org.junit.Assert.*;
/**
* Integration tests for handling {@link Qualifier} annotations.
*
@ -279,7 +280,7 @@ public class QualifierAnnotationAutowireContextTests {
RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
context.registerBeanDefinition(JUERGEN, person1);
context.registerBeanDefinition(MARK, person2);
context.registerBeanDefinition("autowired",
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
context.refresh();
@ -287,6 +288,26 @@ public class QualifierAnnotationAutowireContextTests {
assertEquals(JUERGEN, bean.getPerson().getName());
}
@Test
public void testAutowiredFieldResolvesMetaQualifiedCandidate() {
GenericApplicationContext context = new GenericApplicationContext();
ConstructorArgumentValues cavs1 = new ConstructorArgumentValues();
cavs1.addGenericArgumentValue(JUERGEN);
RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null);
person1.addQualifier(new AutowireCandidateQualifier(TestQualifier.class));
ConstructorArgumentValues cavs2 = new ConstructorArgumentValues();
cavs2.addGenericArgumentValue(MARK);
RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null);
context.registerBeanDefinition(JUERGEN, person1);
context.registerBeanDefinition(MARK, person2);
context.registerBeanDefinition("autowired",
new RootBeanDefinition(MetaQualifiedFieldTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
context.refresh();
MetaQualifiedFieldTestBean bean = (MetaQualifiedFieldTestBean) context.getBean("autowired");
assertEquals(JUERGEN, bean.getPerson().getName());
}
@Test
public void testAutowiredMethodParameterResolvesQualifiedCandidate() {
GenericApplicationContext context = new GenericApplicationContext();
@ -596,6 +617,24 @@ public class QualifierAnnotationAutowireContextTests {
}
private static class MetaQualifiedFieldTestBean {
@MyAutowired
private Person person;
public Person getPerson() {
return this.person;
}
}
@Autowired
@TestQualifier
@Retention(RetentionPolicy.RUNTIME)
public static @interface MyAutowired {
}
private static class QualifiedMethodParameterTestBean {
private Person person;
@ -706,7 +745,6 @@ public class QualifierAnnotationAutowireContextTests {
}
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public static @interface TestQualifier {

View File

@ -1,52 +1,117 @@
/*
* Copyright 2002-2012 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.context.annotation.configuration;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import test.beans.TestBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import test.beans.TestBean;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/**
* Tests proving that @Qualifier annotations work when used
* with @Configuration classes on @Bean methods.
*
* @author Chris Beams
* @author Juergen Hoeller
*/
public class BeanMethodQualificationTests {
@Test
public void test() {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(Config.class, Pojo.class);
Pojo pojo = ctx.getBean(Pojo.class);
public void testStandard() {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(StandardConfig.class, StandardPojo.class);
assertFalse(ctx.getBeanFactory().containsSingleton("testBean1"));
StandardPojo pojo = ctx.getBean(StandardPojo.class);
assertThat(pojo.testBean.getName(), equalTo("interesting"));
}
@Test
public void testCustom() {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(CustomConfig.class, CustomPojo.class);
assertFalse(ctx.getBeanFactory().containsSingleton("testBean1"));
CustomPojo pojo = ctx.getBean(CustomPojo.class);
assertThat(pojo.testBean.getName(), equalTo("interesting"));
}
@Configuration
static class Config {
@Bean
@Qualifier("interesting")
static class StandardConfig {
@Bean @Lazy @Qualifier("interesting")
public TestBean testBean1() {
return new TestBean("interesting");
}
@Bean
@Qualifier("boring")
@Bean @Qualifier("boring")
public TestBean testBean2() {
return new TestBean("boring");
}
}
@Component
static class Pojo {
@Component @Lazy
static class StandardPojo {
@Autowired @Qualifier("interesting") TestBean testBean;
}
@Configuration
static class CustomConfig {
@InterestingBean
public TestBean testBean1() {
return new TestBean("interesting");
}
@Bean @Qualifier("boring")
public TestBean testBean2() {
return new TestBean("boring");
}
}
@InterestingPojo
static class CustomPojo {
@InterestingNeed TestBean testBean;
}
@Bean @Lazy @Qualifier("interesting")
@Retention(RetentionPolicy.RUNTIME)
public @interface InterestingBean {
}
@Autowired @Qualifier("interesting")
@Retention(RetentionPolicy.RUNTIME)
public @interface InterestingNeed {
}
@Component @Lazy
@Retention(RetentionPolicy.RUNTIME)
public @interface InterestingPojo {
}
}