Infer AnnotationAttributes method return types

- Drop 'expectedType' parameter from #getClass and #getEnum methods and
   rely on compiler inference based on type of assigned variable, e.g.

     public @interface Example {
         Color color();
         Class<? extends UserType> userType();
         int order() default 0;
     }

     AnnotationAttributes example =
        AnnotationUtils.getAnnotationAttributes(Example.class, true, true);

     Color color = example.getEnum("color");
     Class<? extends UserType> userType = example.getClass("userType");

   or in cases where there is no variable assignment (and thus no
   inference possible), use explicit generic type, e.g.

     bean.setColor(example.<Color>getEnum("color"));

 - Rename #get{Int=>Number} and update return type from int to
   <N extends Number>, allowing invocations such as:

     int order = example.getNumber("order");

These changes reduce the overall number of methods exposed by
AnnotationAttributes, while at the same time providing comprehensive
access to all possible annotation attribute types -- that is, instead of
requiring explicit #getInt, #getFloat, #getDouble methods, the
single #getNumber method is capabable of handling them all, and without
any casting required. And the obvious additional benefit is more concise
invocation as no redundant 'expectedType' parameters are required.
This commit is contained in:
Chris Beams 2012-02-10 11:33:50 +01:00
parent 997c6c56f7
commit e25f1cbca9
13 changed files with 45 additions and 47 deletions

View File

@ -44,7 +44,7 @@ public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
new BeanFactoryCacheOperationSourceAdvisor();
advisor.setCacheOperationSource(cacheOperationSource());
advisor.setAdvice(cacheInterceptor());
advisor.setOrder(this.enableCaching.getInt("order"));
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
return advisor;
}

View File

@ -73,8 +73,7 @@ public abstract class AdviceModeImportSelector<A extends Annotation> implements
"@%s is not present on importing class '%s' as expected",
annoType.getSimpleName(), importingClassMetadata.getClassName()));
AdviceMode adviceMode =
attributes.getEnum(this.getAdviceModeAttributeName(), AdviceMode.class);
AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());
String[] imports = selectImports(adviceMode);
Assert.notNull(imports, String.format("Unknown AdviceMode: '%s'", adviceMode));

View File

@ -232,8 +232,8 @@ public class AnnotationConfigUtils {
}
if (abd instanceof AbstractBeanDefinition) {
if (metadata.isAnnotated(Role.class.getName())) {
((AbstractBeanDefinition)abd).setRole(
attributesFor(metadata, Role.class).getInt("value"));
int role = attributesFor(metadata, Role.class).getNumber("value");
((AbstractBeanDefinition)abd).setRole(role);
}
}
}

View File

@ -81,7 +81,7 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode", ScopedProxyMode.class);
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}

View File

@ -70,15 +70,15 @@ class ComponentScanAnnotationParser {
Assert.notNull(this.resourceLoader, "ResourceLoader must not be null");
scanner.setResourceLoader(this.resourceLoader);
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(
componentScan.getClass("nameGenerator", BeanNameGenerator.class)));
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy", ScopedProxyMode.class);
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
} else {
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(
componentScan.getClass("scopeResolver", ScopeMetadataResolver.class)));
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
@ -118,7 +118,7 @@ class ComponentScanAnnotationParser {
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
List<TypeFilter> typeFilters = new ArrayList<TypeFilter>();
FilterType filterType = filterAttributes.getEnum("type", FilterType.class);
FilterType filterType = filterAttributes.getEnum("type");
for (Class<?> filterClass : filterAttributes.getClassArray("value")) {
switch (filterType) {

View File

@ -198,7 +198,7 @@ class ConfigurationClassBeanDefinitionReader {
// consider role
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
beanDef.setRole(role.getInt("value"));
beanDef.setRole(role.<Integer>getNumber("value"));
}
// consider name and any aliases
@ -246,7 +246,7 @@ class ConfigurationClassBeanDefinitionReader {
}
}
Autowire autowire = bean.getEnum("autowire", Autowire.class);
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
@ -266,7 +266,7 @@ class ConfigurationClassBeanDefinitionReader {
AnnotationAttributes scope = attributesFor(metadata, Scope.class);
if (scope != null) {
beanDef.setScope(scope.getString("value"));
proxyMode = scope.getEnum("proxyMode", ScopedProxyMode.class);
proxyMode = scope.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}

View File

@ -216,8 +216,7 @@ class ConfigurationClassParser {
if (metadata.isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource = attributesFor(metadata, ImportResource.class);
String[] resources = importResource.getStringArray("value");
Class<? extends BeanDefinitionReader> readerClass =
importResource.getClass("reader", BeanDefinitionReader.class);
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
configClass.addImportedResource(resource, readerClass);
}

View File

@ -18,8 +18,6 @@ package org.springframework.context.annotation;
import static org.springframework.context.weaving.AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE;
import java.util.Map;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
@ -80,7 +78,8 @@ public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoade
loadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader);
}
switch (this.enableLTW.getEnum("aspectjWeaving", AspectJWeaving.class)) {
AspectJWeaving aspectJWeaving = this.enableLTW.getEnum("aspectjWeaving");
switch (aspectJWeaving) {
case DISABLED:
// AJ weaving is disabled -> do nothing
break;

View File

@ -46,7 +46,7 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
Class<? extends Annotation> customAsyncAnnotation = enableAsync.getClass("annotation", Annotation.class);
Class<? extends Annotation> customAsyncAnnotation = enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
@ -56,8 +56,7 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.getInt("order"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}

View File

@ -92,16 +92,18 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
return doGet(attributeName, Boolean.class);
}
public int getInt(String attributeName) {
return doGet(attributeName, Integer.class);
}
public <E extends Enum<?>> E getEnum(String attributeName, Class<E> enumType) {
return doGet(attributeName, enumType);
@SuppressWarnings("unchecked")
public <N extends Number> N getNumber(String attributeName) {
return (N) doGet(attributeName, Integer.class);
}
@SuppressWarnings("unchecked")
public <T> Class<? extends T> getClass(String attributeName, Class<T> expectedType) {
public <E extends Enum<?>> E getEnum(String attributeName) {
return (E) doGet(attributeName, Enum.class);
}
@SuppressWarnings("unchecked")
public <T> Class<? extends T> getClass(String attributeName) {
return (Class<T>)doGet(attributeName, Class.class);
}

View File

@ -55,11 +55,11 @@ public class AnnotationAttributesTests {
assertThat(a.getStringArray("names"), equalTo(new String[] { "dave", "frank", "hal" }));
assertThat(a.getBoolean("bool1"), equalTo(true));
assertThat(a.getBoolean("bool2"), equalTo(false));
assertThat(a.getEnum("color", Color.class), equalTo(Color.RED));
assertTrue(a.getClass("clazz", Number.class).equals(Integer.class));
assertThat(a.<Color>getEnum("color"), equalTo(Color.RED));
assertTrue(a.getClass("clazz").equals(Integer.class));
assertThat(a.getClassArray("classes"), equalTo(new Class[] { Number.class, Short.class, Integer.class }));
assertThat(a.getInt("number"), equalTo(42));
assertThat(a.getAnnotation("anno").getInt("value"), equalTo(10));
assertThat(a.<Integer>getNumber("number"), equalTo(42));
assertThat(a.getAnnotation("anno").<Integer>getNumber("value"), equalTo(10));
assertThat(a.getAnnotationArray("annoArray")[0].getString("name"), equalTo("algernon"));
}
@ -68,13 +68,13 @@ public class AnnotationAttributesTests {
AnnotationAttributes a = new AnnotationAttributes();
a.put("color", "RED");
try {
a.getEnum("", Color.class);
a.getEnum("");
fail();
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), equalTo("attributeName must not be null or empty"));
}
try {
a.getEnum(null, Color.class);
a.getEnum(null);
fail();
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), equalTo("attributeName must not be null or empty"));
@ -86,7 +86,7 @@ public class AnnotationAttributesTests {
AnnotationAttributes a = new AnnotationAttributes();
a.put("color", "RED");
try {
a.getEnum("colour", Color.class);
a.getEnum("colour");
fail();
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), equalTo("Attribute 'colour' not found"));
@ -98,11 +98,11 @@ public class AnnotationAttributesTests {
AnnotationAttributes a = new AnnotationAttributes();
a.put("color", "RED");
try {
a.getEnum("color", Color.class);
a.getEnum("color");
fail();
} catch (IllegalArgumentException ex) {
String expected =
"Attribute 'color' is of type [String], but [Color] was expected";
"Attribute 'color' is of type [String], but [Enum] was expected";
assertThat(ex.getMessage().substring(0, expected.length()), equalTo(expected));
}
}

View File

@ -109,34 +109,34 @@ public class AnnotationMetadataTests {
{ // perform tests with classValuesAsString = false (the default)
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName());
assertThat(specialAttrs.size(), is(6));
assertTrue(String.class.isAssignableFrom(specialAttrs.getClass("clazz", Object.class)));
assertThat(specialAttrs.getEnum("state", Thread.State.class), is(Thread.State.NEW));
assertTrue(String.class.isAssignableFrom(specialAttrs.getClass("clazz")));
assertTrue(specialAttrs.getEnum("state").equals(Thread.State.NEW));
AnnotationAttributes nestedAnno = specialAttrs.getAnnotation("nestedAnno");
assertThat("na", is(nestedAnno.getString("value")));
assertThat(nestedAnno.getEnum("anEnum", SomeEnum.class), is(SomeEnum.LABEL1));
assertTrue(nestedAnno.getEnum("anEnum").equals(SomeEnum.LABEL1));
assertArrayEquals(new Class[]{String.class}, (Class[])nestedAnno.get("classArray"));
AnnotationAttributes[] nestedAnnoArray = specialAttrs.getAnnotationArray("nestedAnnoArray");
assertThat(nestedAnnoArray.length, is(2));
assertThat(nestedAnnoArray[0].getString("value"), is("default"));
assertThat(nestedAnnoArray[0].getEnum("anEnum", SomeEnum.class), is(SomeEnum.DEFAULT));
assertTrue(nestedAnnoArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
assertArrayEquals(new Class[]{Void.class}, (Class[])nestedAnnoArray[0].get("classArray"));
assertThat(nestedAnnoArray[1].getString("value"), is("na1"));
assertThat(nestedAnnoArray[1].getEnum("anEnum", SomeEnum.class), is(SomeEnum.LABEL2));
assertTrue(nestedAnnoArray[1].getEnum("anEnum").equals(SomeEnum.LABEL2));
assertArrayEquals(new Class[]{Number.class}, (Class[])nestedAnnoArray[1].get("classArray"));
assertArrayEquals(new Class[]{Number.class}, nestedAnnoArray[1].getClassArray("classArray"));
AnnotationAttributes optional = specialAttrs.getAnnotation("optional");
assertThat(optional.getString("value"), is("optional"));
assertThat(optional.getEnum("anEnum", SomeEnum.class), is(SomeEnum.DEFAULT));
assertTrue(optional.getEnum("anEnum").equals(SomeEnum.DEFAULT));
assertArrayEquals(new Class[]{Void.class}, (Class[])optional.get("classArray"));
assertArrayEquals(new Class[]{Void.class}, optional.getClassArray("classArray"));
AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray");
assertThat(optionalArray.length, is(1));
assertThat(optionalArray[0].getString("value"), is("optional"));
assertThat(optionalArray[0].getEnum("anEnum", SomeEnum.class), is(SomeEnum.DEFAULT));
assertTrue(optionalArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
assertArrayEquals(new Class[]{Void.class}, (Class[])optionalArray[0].get("classArray"));
assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray"));
}

View File

@ -44,7 +44,7 @@ public class ProxyTransactionManagementConfiguration extends AbstractTransaction
new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());
advisor.setAdvice(transactionInterceptor());
advisor.setOrder(this.enableTx.getInt("order"));
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
return advisor;
}