From 2d23f42609c2e6a2ee6b0507f4b800870a63ca26 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 13 Jun 2015 16:07:29 +0200 Subject: [PATCH] Introduce alias for 'value' attribute in @Scope Issue: SPR-11393 --- .../AnnotationScopeMetadataResolver.java | 26 ++--- ...onfigurationClassBeanDefinitionReader.java | 11 +- .../context/annotation/Scope.java | 20 +++- .../AnnotationScopeMetadataResolverTests.java | 102 ++++++++---------- .../ConfigurationClassPostProcessorTests.java | 4 +- 5 files changed, 84 insertions(+), 79 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java index 5d4ce9c9522..fe56aa2f458 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -25,13 +25,14 @@ import org.springframework.util.Assert; /** * A {@link ScopeMetadataResolver} implementation that by default checks for - * the presence of Spring's {@link Scope} annotation on the bean class. + * the presence of Spring's {@link Scope @Scope} annotation on the bean class. * - *

The exact type of annotation that is checked for is configurable via the - * {@link #setScopeAnnotationType(Class)} property. + *

The exact type of annotation that is checked for is configurable via + * {@link #setScopeAnnotationType(Class)}. * * @author Mark Fisher * @author Juergen Hoeller + * @author Sam Brannen * @since 2.5 * @see org.springframework.context.annotation.Scope */ @@ -43,27 +44,27 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver { /** - * Create a new instance of the {@code AnnotationScopeMetadataResolver} class. + * Construct a new {@code AnnotationScopeMetadataResolver}. * @see #AnnotationScopeMetadataResolver(ScopedProxyMode) * @see ScopedProxyMode#NO */ public AnnotationScopeMetadataResolver() { - this.defaultProxyMode = ScopedProxyMode.NO; + this(ScopedProxyMode.NO); } /** - * Create a new instance of the {@code AnnotationScopeMetadataResolver} class. - * @param defaultProxyMode the desired scoped-proxy mode + * Construct a new {@code AnnotationScopeMetadataResolver} using the + * supplied default {@link ScopedProxyMode}. + * @param defaultProxyMode the default scoped-proxy mode */ public AnnotationScopeMetadataResolver(ScopedProxyMode defaultProxyMode) { Assert.notNull(defaultProxyMode, "'defaultProxyMode' must not be null"); this.defaultProxyMode = defaultProxyMode; } - /** * Set the type of annotation that is checked for by this - * {@link AnnotationScopeMetadataResolver}. + * {@code AnnotationScopeMetadataResolver}. * @param scopeAnnotationType the target annotation type */ public void setScopeAnnotationType(Class scopeAnnotationType) { @@ -71,7 +72,6 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver { this.scopeAnnotationType = scopeAnnotationType; } - @Override public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) { ScopeMetadata metadata = new ScopeMetadata(); @@ -79,9 +79,9 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver { AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition; AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType); if (attributes != null) { - metadata.setScopeName(attributes.getString("value")); + metadata.setScopeName(attributes.getAliasedString("value", this.scopeAnnotationType, definition.getSource())); ScopedProxyMode proxyMode = attributes.getEnum("proxyMode"); - if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) { + if ((proxyMode == null) || (proxyMode == ScopedProxyMode.DEFAULT)) { proxyMode = this.defaultProxyMode; } metadata.setScopedProxyMode(proxyMode); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 033d27e58eb..7af760f41c4 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -65,6 +65,7 @@ import org.springframework.util.StringUtils; * @author Chris Beams * @author Juergen Hoeller * @author Phillip Webb + * @author Sam Brannen * @since 3.0 * @see ConfigurationClassParser */ @@ -242,10 +243,12 @@ class ConfigurationClassBeanDefinitionReader { // Consider scoping ScopedProxyMode proxyMode = ScopedProxyMode.NO; - AnnotationAttributes scope = AnnotationConfigUtils.attributesFor(metadata, Scope.class); - if (scope != null) { - beanDef.setScope(scope.getString("value")); - proxyMode = scope.getEnum("proxyMode"); + // TODO Determine why type is hard coded to org.springframework.context.annotation.Scope, + // since AnnotationScopeMetadataResolver supports a custom scope annotation type. + AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class); + if (attributes != null) { + beanDef.setScope(attributes.getAliasedString("value", Scope.class, configClass.getResource())); + proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = ScopedProxyMode.NO; } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Scope.java b/spring-context/src/main/java/org/springframework/context/annotation/Scope.java index c9c45f83d9b..8df50327ade 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Scope.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Scope.java @@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.core.annotation.AliasFor; /** * When used as a type-level annotation in conjunction with @@ -57,14 +58,25 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; public @interface Scope { /** - * Specifies the scope to use for the annotated component/bean. - *

Defaults to {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}. - * @see ConfigurableBeanFactory#SCOPE_SINGLETON + * Alias for {@link #name}. + * @see #name + */ + @AliasFor(attribute = "name") + String value() default ""; + + /** + * Specifies the name of the scope to use for the annotated component/bean. + *

Defaults to an empty string ({@code ""}) which implies + * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}. + * @since 4.2 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE + * @see ConfigurableBeanFactory#SCOPE_SINGLETON * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION + * @see #value */ - String value() default ConfigurableBeanFactory.SCOPE_SINGLETON; + @AliasFor(attribute = "value") + String name() default ""; /** * Specifies whether a component should be configured as a scoped proxy diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java index 5f0de59982c..6423156c81f 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationScopeMetadataResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -17,12 +17,9 @@ package org.springframework.context.annotation; import java.io.IOException; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; @@ -33,137 +30,130 @@ import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import static org.junit.Assert.*; +import static org.springframework.context.annotation.ScopedProxyMode.*; /** + * Unit tests for {@link AnnotationScopeMetadataResolver}. + * * @author Rick Evans * @author Chris Beams * @author Juergen Hoeller + * @author Sam Brannen */ -public final class AnnotationScopeMetadataResolverTests { - - private AnnotationScopeMetadataResolver scopeMetadataResolver; - - - @Before - public void setUp() throws Exception { - this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(); - } +public class AnnotationScopeMetadataResolverTests { + private AnnotationScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); @Test - public void testThatResolveScopeMetadataDoesNotApplyScopedProxyModeToASingleton() { + public void resolveScopeMetadataShouldNotApplyScopedProxyModeToSingleton() { AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithSingletonScope.class); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd); assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata); assertEquals(BeanDefinition.SCOPE_SINGLETON, scopeMetadata.getScopeName()); - assertEquals(ScopedProxyMode.NO, scopeMetadata.getScopedProxyMode()); + assertEquals(NO, scopeMetadata.getScopedProxyMode()); } @Test - public void testThatResolveScopeMetadataDoesApplyScopedProxyModeToAPrototype() { - this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(ScopedProxyMode.INTERFACES); + public void resolveScopeMetadataShouldApplyScopedProxyModeToPrototype() { + this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(INTERFACES); AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithPrototypeScope.class); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd); assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata); assertEquals(BeanDefinition.SCOPE_PROTOTYPE, scopeMetadata.getScopeName()); - assertEquals(ScopedProxyMode.INTERFACES, scopeMetadata.getScopedProxyMode()); + assertEquals(INTERFACES, scopeMetadata.getScopedProxyMode()); } @Test - public void testThatResolveScopeMetadataDoesReadScopedProxyModeFromTheAnnotation() { + public void resolveScopeMetadataShouldReadScopedProxyModeFromAnnotation() { this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(); AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithScopedProxy.class); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd); assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata); assertEquals("request", scopeMetadata.getScopeName()); - assertEquals(ScopedProxyMode.TARGET_CLASS, scopeMetadata.getScopedProxyMode()); + assertEquals(TARGET_CLASS, scopeMetadata.getScopedProxyMode()); } @Test - public void testCustomRequestScope() { + public void customRequestScope() { AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithCustomRequestScope.class); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd); assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata); assertEquals("request", scopeMetadata.getScopeName()); - assertEquals(ScopedProxyMode.NO, scopeMetadata.getScopedProxyMode()); + assertEquals(NO, scopeMetadata.getScopedProxyMode()); } @Test - public void testCustomRequestScopeViaAsm() throws IOException { + public void customRequestScopeViaAsm() throws IOException { MetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(); MetadataReader reader = readerFactory.getMetadataReader(AnnotatedWithCustomRequestScope.class.getName()); AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(reader.getAnnotationMetadata()); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd); assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata); assertEquals("request", scopeMetadata.getScopeName()); - assertEquals(ScopedProxyMode.NO, scopeMetadata.getScopedProxyMode()); + assertEquals(NO, scopeMetadata.getScopedProxyMode()); } @Test - public void testCustomRequestScopeWithAttribute() { - AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnnotatedWithCustomRequestScopeWithAttribute.class); + public void customRequestScopeWithAttribute() { + AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition( + AnnotatedWithCustomRequestScopeWithAttributeOverride.class); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd); assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata); assertEquals("request", scopeMetadata.getScopeName()); - assertEquals(ScopedProxyMode.TARGET_CLASS, scopeMetadata.getScopedProxyMode()); + assertEquals(TARGET_CLASS, scopeMetadata.getScopedProxyMode()); } @Test - public void testCustomRequestScopeWithAttributeViaAsm() throws IOException { + public void customRequestScopeWithAttributeViaAsm() throws IOException { MetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(); - MetadataReader reader = readerFactory.getMetadataReader(AnnotatedWithCustomRequestScopeWithAttribute.class.getName()); + MetadataReader reader = readerFactory.getMetadataReader(AnnotatedWithCustomRequestScopeWithAttributeOverride.class.getName()); AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(reader.getAnnotationMetadata()); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(bd); assertNotNull("resolveScopeMetadata(..) must *never* return null.", scopeMetadata); assertEquals("request", scopeMetadata.getScopeName()); - assertEquals(ScopedProxyMode.TARGET_CLASS, scopeMetadata.getScopedProxyMode()); + assertEquals(TARGET_CLASS, scopeMetadata.getScopedProxyMode()); } - @Test(expected=IllegalArgumentException.class) - public void testCtorWithNullScopedProxyMode() { + @Test(expected = IllegalArgumentException.class) + public void ctorWithNullScopedProxyMode() { new AnnotationScopeMetadataResolver(null); } - @Test(expected=IllegalArgumentException.class) - public void testSetScopeAnnotationTypeWithNullType() { + @Test(expected = IllegalArgumentException.class) + public void setScopeAnnotationTypeWithNullType() { scopeMetadataResolver.setScopeAnnotationType(null); } - @Scope("singleton") - private static final class AnnotatedWithSingletonScope { - } - - @Scope("prototype") - private static final class AnnotatedWithPrototypeScope { - } - - @Scope(value="request", proxyMode = ScopedProxyMode.TARGET_CLASS) - private static final class AnnotatedWithScopedProxy { + @Retention(RetentionPolicy.RUNTIME) + @Scope("request") + @interface CustomRequestScope { } - - @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Scope("request") - public @interface CustomRequestScope { + @interface CustomRequestScopeWithAttributeOverride { + ScopedProxyMode proxyMode(); } - @CustomRequestScope - private static final class AnnotatedWithCustomRequestScope { + @Scope("singleton") + private static class AnnotatedWithSingletonScope { } + @Scope("prototype") + private static class AnnotatedWithPrototypeScope { + } - @Target({ElementType.TYPE, ElementType.METHOD}) - @Retention(RetentionPolicy.RUNTIME) - @Scope("request") - public @interface CustomRequestScopeWithAttribute { + @Scope(name = "request", proxyMode = TARGET_CLASS) + private static class AnnotatedWithScopedProxy { + } - ScopedProxyMode proxyMode(); + @CustomRequestScope + private static class AnnotatedWithCustomRequestScope { } - @CustomRequestScopeWithAttribute(proxyMode = ScopedProxyMode.TARGET_CLASS) - private static final class AnnotatedWithCustomRequestScopeWithAttribute { + @CustomRequestScopeWithAttributeOverride(proxyMode = TARGET_CLASS) + private static class AnnotatedWithCustomRequestScopeWithAttributeOverride { } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 251fcabd0b1..502524c18fc 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -787,7 +787,7 @@ public class ConfigurationClassPostProcessorTests { public static class ScopedProxyRepositoryConfiguration { @Bean - @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) + @Scope(name = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) public Repository stringRepo() { return new Repository() { @Override @@ -798,7 +798,7 @@ public class ConfigurationClassPostProcessorTests { } @Bean - @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) + @Scope(name = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) public Repository integerRepo() { return new Repository() { @Override