提交 3126a4cf 编写于 作者: P Phillip Webb

Merge branch 'SPR-10534'

* SPR-10534:
  Add support for DeferredImportSelector
  Support for @Conditional configuration
  Extend AnnotationMetadata and MethodMetadata
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -24,11 +24,9 @@ import org.springframework.beans.factory.support.AutowireCandidateQualifier; ...@@ -24,11 +24,9 @@ import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
...@@ -39,6 +37,7 @@ import org.springframework.util.Assert; ...@@ -39,6 +37,7 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Chris Beams * @author Chris Beams
* @author Sam Brannen * @author Sam Brannen
* @author Phillip Webb
* @since 3.0 * @since 3.0
* @see AnnotationConfigApplicationContext#register * @see AnnotationConfigApplicationContext#register
*/ */
...@@ -134,13 +133,10 @@ public class AnnotatedBeanDefinitionReader { ...@@ -134,13 +133,10 @@ public class AnnotatedBeanDefinitionReader {
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) { public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
AnnotationMetadata metadata = abd.getMetadata(); if (ConditionalAnnotationHelper.shouldSkip(abd, this.registry,
if (metadata.isAnnotated(Profile.class.getName())) { this.environment, this.beanNameGenerator)) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
return; return;
} }
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName()); abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
......
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -52,6 +52,7 @@ import org.springframework.util.PatternMatchUtils; ...@@ -52,6 +52,7 @@ import org.springframework.util.PatternMatchUtils;
* @author Mark Fisher * @author Mark Fisher
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 2.5 * @since 2.5
* @see AnnotationConfigApplicationContext#scan * @see AnnotationConfigApplicationContext#scan
* @see org.springframework.stereotype.Component * @see org.springframework.stereotype.Component
...@@ -298,6 +299,10 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo ...@@ -298,6 +299,10 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* bean definition has been found for the specified name * bean definition has been found for the specified name
*/ */
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (ConditionalAnnotationHelper.shouldSkip(beanDefinition, getRegistry(),
getEnvironment(), this.beanNameGenerator)) {
return false;
}
if (!this.registry.containsBeanDefinition(beanName)) { if (!this.registry.containsBeanDefinition(beanName)) {
return true; return true;
} }
......
/*
* Copyright 2002-2013 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;
import org.jruby.internal.runtime.methods.MethodMethod;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
/**
* A single {@code condition} that must be {@linkplain #matches matched} in order
* for a component to be registered.
*
* <p>Conditions are checked immediately before a component bean-definition is due to be
* registered and are free to veto registration based on any criteria that can be
* determined at that point.
*
* <p>Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
* and take care to never interact with bean instances.
*
* @author Phillip Webb
* @since 4.0
* @see Conditional
* @see ConditionContext
*/
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata meta-data of the {@link AnnotationMetadata class} or
* {@link MethodMethod method} being checked.
* @return {@code true} if the condition matches and the component can be registered
* or {@code false} to veto registration.
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
/*
* Copyright 2002-2013 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;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
/**
* Context information for use by {@link Condition}s.
*
* @author Phillip Webb
* @since 4.0
*/
public interface ConditionContext {
/**
* Returns the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match.
* @return the registry (never {@code null})
*/
BeanDefinitionRegistry getRegistry();
/**
* Return the {@link Environment} for which the current application is running or
* {@code null} if no environment is available.
* @return the environment or {@code null}
*/
Environment getEnvironment();
/**
* Returns the {@link ConfigurableListableBeanFactory} that will hold the bean
* definition should the condition match. If a
* {@link ConfigurableListableBeanFactory} is unavailable an
* {@link IllegalStateException} will be thrown.
* @return the bean factory
* @throws IllegalStateException if the bean factory could not be obtained
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
/**
* Returns the {@link ResourceLoader} currently being used or {@code null} if the
* resource loader cannot be obtained.
* @return a resource loader or {@code null}
*/
ResourceLoader getResourceLoader();
/**
* Returns the {@link ClassLoader} that should be used to load additional classes
* or {@code null} if the default classloader should be used.
* @return the classloader or {@code null}
*/
ClassLoader getClassLoader();
}
/*
* Copyright 2002-2013 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;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that a component is is only eligible for registration when all
* {@linkplain #value() specified conditions} match.
*
* <p>A <em>condition</em> is any state that can be determined programmatically
* immediately before the bean is due to be created (see {@link Condition} for details).
*
* <p>The {@code @Conditional} annotation may be used in any of the following ways:
* <ul>
* <li>as a type-level annotation on any class directly or indirectly annotated with
* {@code @Component}, including {@link Configuration @Configuration} classes</li>
* <li>as a meta-annotation, for the purpose of composing custom stereotype
* annotations</li>
* <li>as a method-level annotation on any {@link Bean @Bean} method</li>
* </ul>
*
* <p>If a {@code @Configuration} class is marked with {@code @Conditional}, all of the
* {@code @Bean} methods and {@link Import @Import} annotations associated with that class
* will be subject to the conditions.
*
* @author Phillip Webb
* @since 4.0
* @see Condition
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match} in order for
* the component to be registered.
*/
Class<? extends Condition>[] value();
}
/*
* Copyright 2002-2013 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;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
/**
* Helper class used to determine if registration should be skipped based due to a
* {@code @Conditional} annotation.
*
* @author Phillip Webb
* @since 4.0
* @see Conditional
*/
abstract class ConditionalAnnotationHelper {
private static final String CONDITIONAL_ANNOTATION = Conditional.class.getName();
public static boolean shouldSkip(BeanDefinition beanDefinition,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(getMetadata(beanDefinition))) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(getMetadata(beanDefinition), context);
}
return false;
}
public static boolean shouldSkip(BeanMethod beanMethod,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(getMetadata(beanMethod))) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(getMetadata(beanMethod), context);
}
return false;
}
public static boolean shouldSkip(ConfigurationClass configurationClass,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(configurationClass)) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(configurationClass, context);
}
return false;
}
public static boolean shouldSkip(ConfigurationClass configClass,
ConditionContextImpl context) {
if (configClass == null) {
return false;
}
return shouldSkip(configClass.getMetadata(), context);
}
private static boolean shouldSkip(AnnotatedTypeMetadata metadata,
ConditionContextImpl context) {
if (metadata != null) {
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
if (!getCondition(conditionClass, context.getClassLoader()).matches(
context, metadata)) {
return true;
}
}
}
}
return false;
}
private static AnnotatedTypeMetadata getMetadata(BeanMethod beanMethod) {
return (beanMethod == null ? null : beanMethod.getMetadata());
}
private static AnnotatedTypeMetadata getMetadata(BeanDefinition beanDefinition) {
if (beanDefinition != null && beanDefinition instanceof AnnotatedBeanDefinition) {
return ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
}
return null;
}
private static boolean hasCondition(ConfigurationClass configurationClass) {
if (configurationClass == null) {
return false;
}
return hasCondition(configurationClass.getMetadata())
|| hasCondition(configurationClass.getImportedBy());
}
private static boolean hasCondition(AnnotatedTypeMetadata metadata) {
return (metadata != null) && metadata.isAnnotated(CONDITIONAL_ANNOTATION);
}
@SuppressWarnings("unchecked")
private static List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
CONDITIONAL_ANNOTATION, true);
Object values = attributes == null ? null : attributes.get("value");
return (List<String[]>) (values == null ? Collections.emptyList() : values);
}
private static Condition getCondition(String conditionClassName,
ClassLoader classloader) {
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
classloader);
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
/**
* Implementation of a {@link ConditionContext}.
*/
private static class ConditionContextImpl implements ConditionContext {
private BeanDefinitionRegistry registry;
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
public ConditionContextImpl(BeanDefinitionRegistry registry,
Environment environment, BeanNameGenerator beanNameGenerator) {
Assert.notNull(registry, "Registry must not be null");
this.registry = registry;
this.beanFactory = deduceBeanFactory(registry);
this.environment = environment;
if (this.environment == null) {
this.environment = deduceEnvironment(registry);
}
}
private ConfigurableListableBeanFactory deduceBeanFactory(Object source) {
if (source instanceof ConfigurableListableBeanFactory) {
return (ConfigurableListableBeanFactory) source;
}
else if (source instanceof ConfigurableApplicationContext) {
return deduceBeanFactory(((ConfigurableApplicationContext) source).getBeanFactory());
}
return null;
}
private Environment deduceEnvironment(BeanDefinitionRegistry registry) {
if (registry instanceof EnvironmentCapable) {
return ((EnvironmentCapable) registry).getEnvironment();
}
return null;
}
public BeanDefinitionRegistry getRegistry() {
return this.registry;
}
public Environment getEnvironment() {
return this.environment;
}
public ConfigurableListableBeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
return this.beanFactory;
}
public ResourceLoader getResourceLoader() {
if (registry instanceof ResourceLoader) {
return (ResourceLoader) registry;
}
return null;
}
public ClassLoader getClassLoader() {
ResourceLoader resourceLoader = getResourceLoader();
return (resourceLoader == null ? null : resourceLoader.getClassLoader());
}
}
}
...@@ -41,6 +41,7 @@ import org.springframework.util.ClassUtils; ...@@ -41,6 +41,7 @@ import org.springframework.util.ClassUtils;
* *
* @author Chris Beams * @author Chris Beams
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0 * @since 3.0
* @see BeanMethod * @see BeanMethod
* @see ConfigurationClassParser * @see ConfigurationClassParser
...@@ -58,7 +59,7 @@ final class ConfigurationClass { ...@@ -58,7 +59,7 @@ final class ConfigurationClass {
private String beanName; private String beanName;
private final boolean imported; private final ConfigurationClass importedBy;
/** /**
...@@ -66,28 +67,28 @@ final class ConfigurationClass { ...@@ -66,28 +67,28 @@ final class ConfigurationClass {
* @param metadataReader reader used to parse the underlying {@link Class} * @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null} * @param beanName must not be {@code null}
* @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1) * @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1)
* @see ConfigurationClass#ConfigurationClass(Class, boolean) * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/ */
public ConfigurationClass(MetadataReader metadataReader, String beanName) { public ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.hasText(beanName, "bean name must not be null"); Assert.hasText(beanName, "bean name must not be null");
this.metadata = metadataReader.getAnnotationMetadata(); this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource(); this.resource = metadataReader.getResource();
this.beanName = beanName; this.beanName = beanName;
this.imported = false; this.importedBy = null;
} }
/** /**
* Create a new {@link ConfigurationClass} representing a class that was imported * Create a new {@link ConfigurationClass} representing a class that was imported
* using the {@link Import} annotation or automatically processed as a nested * using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}). * configuration class (if importedBy is not {@code null}).
* @param metadataReader reader used to parse the underlying {@link Class} * @param metadataReader reader used to parse the underlying {@link Class}
* @param imported whether the given configuration class is being imported * @param importedBy the configuration class importing this one or {@code null}
* @since 3.1.1 * @since 3.1.1
*/ */
public ConfigurationClass(MetadataReader metadataReader, boolean imported) { public ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
this.metadata = metadataReader.getAnnotationMetadata(); this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource(); this.resource = metadataReader.getResource();
this.imported = imported; this.importedBy = importedBy;
} }
/** /**
...@@ -95,14 +96,14 @@ final class ConfigurationClass { ...@@ -95,14 +96,14 @@ final class ConfigurationClass {
* @param clazz the underlying {@link Class} to represent * @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean * @param beanName name of the {@code @Configuration} class bean
* @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1) * @throws IllegalArgumentException if beanName is null (as of Spring 3.1.1)
* @see ConfigurationClass#ConfigurationClass(Class, boolean) * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/ */
public ConfigurationClass(Class<?> clazz, String beanName) { public ConfigurationClass(Class<?> clazz, String beanName) {
Assert.hasText(beanName, "bean name must not be null"); Assert.hasText(beanName, "bean name must not be null");
this.metadata = new StandardAnnotationMetadata(clazz, true); this.metadata = new StandardAnnotationMetadata(clazz, true);
this.resource = new DescriptiveResource(clazz.toString()); this.resource = new DescriptiveResource(clazz.toString());
this.beanName = beanName; this.beanName = beanName;
this.imported = false; this.importedBy = null;
} }
/** /**
...@@ -110,13 +111,13 @@ final class ConfigurationClass { ...@@ -110,13 +111,13 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested * using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}). * configuration class (if imported is {@code true}).
* @param clazz the underlying {@link Class} to represent * @param clazz the underlying {@link Class} to represent
* @param imported whether the given configuration class is being imported * @param importedBy the configuration class importing this one or {@code null}
* @since 3.1.1 * @since 3.1.1
*/ */
public ConfigurationClass(Class<?> clazz, boolean imported) { public ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
this.metadata = new StandardAnnotationMetadata(clazz, true); this.metadata = new StandardAnnotationMetadata(clazz, true);
this.resource = new DescriptiveResource(clazz.toString()); this.resource = new DescriptiveResource(clazz.toString());
this.imported = imported; this.importedBy = importedBy;
} }
...@@ -136,9 +137,20 @@ final class ConfigurationClass { ...@@ -136,9 +137,20 @@ final class ConfigurationClass {
* Return whether this configuration class was registered via @{@link Import} or * Return whether this configuration class was registered via @{@link Import} or
* automatically registered due to being nested within another configuration class. * automatically registered due to being nested within another configuration class.
* @since 3.1.1 * @since 3.1.1
* @see #getImportedBy()
*/ */
public boolean isImported() { public boolean isImported() {
return this.imported; return this.importedBy != null;
}
/**
* Returns the configuration class that imported this class or {@code null} if
* this configuration was not imported.
* @since 4.0
* @see #isImported()
*/
public ConfigurationClass getImportedBy() {
return importedBy;
} }
public void setBeanName(String beanName) { public void setBeanName(String beanName) {
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.context.annotation; package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -26,7 +28,6 @@ import java.util.Set; ...@@ -26,7 +28,6 @@ import java.util.Set;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowire;
...@@ -51,8 +52,6 @@ import org.springframework.core.type.MethodMetadata; ...@@ -51,8 +52,6 @@ import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
/** /**
* Reads a given fully-populated set of ConfigurationClass instances, registering bean * Reads a given fully-populated set of ConfigurationClass instances, registering bean
* definitions with the given {@link BeanDefinitionRegistry} based on its contents. * definitions with the given {@link BeanDefinitionRegistry} based on its contents.
...@@ -63,6 +62,7 @@ import static org.springframework.context.annotation.MetadataUtils.*; ...@@ -63,6 +62,7 @@ import static org.springframework.context.annotation.MetadataUtils.*;
* *
* @author Chris Beams * @author Chris Beams
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0 * @since 3.0
* @see ConfigurationClassParser * @see ConfigurationClassParser
*/ */
...@@ -118,7 +118,7 @@ class ConfigurationClassBeanDefinitionReader { ...@@ -118,7 +118,7 @@ class ConfigurationClassBeanDefinitionReader {
* Read a particular {@link ConfigurationClass}, registering bean definitions for the * Read a particular {@link ConfigurationClass}, registering bean definitions for the
* class itself, all its {@link Bean} methods * class itself, all its {@link Bean} methods
*/ */
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { public void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
if (configClass.isImported()) { if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass); registerBeanDefinitionForImportedConfigurationClass(configClass);
} }
...@@ -153,6 +153,10 @@ class ConfigurationClassBeanDefinitionReader { ...@@ -153,6 +153,10 @@ class ConfigurationClassBeanDefinitionReader {
* with the BeanDefinitionRegistry based on its contents. * with the BeanDefinitionRegistry based on its contents.
*/ */
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
if (ConditionalAnnotationHelper.shouldSkip(beanMethod, this.registry,
this.environment, this.importBeanNameGenerator)) {
return;
}
ConfigurationClass configClass = beanMethod.getConfigurationClass(); ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata(); MethodMetadata metadata = beanMethod.getMetadata();
......
...@@ -25,6 +25,8 @@ import java.util.Comparator; ...@@ -25,6 +25,8 @@ import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Stack; import java.util.Stack;
...@@ -32,19 +34,23 @@ import java.util.Stack; ...@@ -32,19 +34,23 @@ import java.util.Stack;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.Aware; import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.NestedIOException; import org.springframework.core.NestedIOException;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
...@@ -76,11 +82,21 @@ import static org.springframework.context.annotation.MetadataUtils.*; ...@@ -76,11 +82,21 @@ import static org.springframework.context.annotation.MetadataUtils.*;
* *
* @author Chris Beams * @author Chris Beams
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0 * @since 3.0
* @see ConfigurationClassBeanDefinitionReader * @see ConfigurationClassBeanDefinitionReader
*/ */
class ConfigurationClassParser { class ConfigurationClassParser {
private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =
new Comparator<ConfigurationClassParser.DeferredImportSelectorHolder>() {
public int compare(DeferredImportSelectorHolder o1,
DeferredImportSelectorHolder o2) {
return AnnotationAwareOrderComparator.INSTANCE.compare(
o1.getImportSelector(), o2.getImportSelector());
}
};
private final MetadataReaderFactory metadataReaderFactory; private final MetadataReaderFactory metadataReaderFactory;
private final ProblemReporter problemReporter; private final ProblemReporter problemReporter;
...@@ -103,6 +119,10 @@ class ConfigurationClassParser { ...@@ -103,6 +119,10 @@ class ConfigurationClassParser {
private final ComponentScanAnnotationParser componentScanParser; private final ComponentScanAnnotationParser componentScanParser;
private final BeanNameGenerator beanNameGenerator;
private final List<DeferredImportSelectorHolder> deferredImportSelectors =
new LinkedList<DeferredImportSelectorHolder>();
/** /**
* Create a new {@link ConfigurationClassParser} instance that will be used * Create a new {@link ConfigurationClassParser} instance that will be used
...@@ -117,10 +137,28 @@ class ConfigurationClassParser { ...@@ -117,10 +137,28 @@ class ConfigurationClassParser {
this.environment = environment; this.environment = environment;
this.resourceLoader = resourceLoader; this.resourceLoader = resourceLoader;
this.registry = registry; this.registry = registry;
this.beanNameGenerator = componentScanBeanNameGenerator;
this.componentScanParser = new ComponentScanAnnotationParser( this.componentScanParser = new ComponentScanAnnotationParser(
resourceLoader, environment, componentScanBeanNameGenerator, registry); resourceLoader, environment, componentScanBeanNameGenerator, registry);
} }
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
}
}
processDeferredImportSelectors();
}
/** /**
* Parse the specified {@link Configuration @Configuration} class. * Parse the specified {@link Configuration @Configuration} class.
...@@ -138,18 +176,17 @@ class ConfigurationClassParser { ...@@ -138,18 +176,17 @@ class ConfigurationClassParser {
* @param clazz the Class to parse * @param clazz the Class to parse
* @param beanName must not be null (as of Spring 3.1.1) * @param beanName must not be null (as of Spring 3.1.1)
*/ */
public void parse(Class<?> clazz, String beanName) throws IOException { protected void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName)); processConfigurationClass(new ConfigurationClass(clazz, beanName));
} }
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
AnnotationMetadata metadata = configClass.getMetadata(); AnnotationMetadata metadata = configClass.getMetadata();
if (this.environment != null && metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class); if (ConditionalAnnotationHelper.shouldSkip(configClass, this.registry,
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) { this.environment, this.beanNameGenerator)) {
return; return;
} }
}
// recursively process the configuration class and its superclass hierarchy // recursively process the configuration class and its superclass hierarchy
do { do {
...@@ -173,7 +210,7 @@ class ConfigurationClassParser { ...@@ -173,7 +210,7 @@ class ConfigurationClassParser {
ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException { ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
// recursively process any member (nested) classes first // recursively process any member (nested) classes first
processMemberClasses(metadata); processMemberClasses(configClass, metadata);
// process any @PropertySource annotations // process any @PropertySource annotations
AnnotationAttributes propertySource = attributesFor(metadata, org.springframework.context.annotation.PropertySource.class); AnnotationAttributes propertySource = attributesFor(metadata, org.springframework.context.annotation.PropertySource.class);
...@@ -256,11 +293,12 @@ class ConfigurationClassParser { ...@@ -256,11 +293,12 @@ class ConfigurationClassParser {
* @param metadata the metadata representation of the containing class * @param metadata the metadata representation of the containing class
* @throws IOException if there is any problem reading metadata from a member class * @throws IOException if there is any problem reading metadata from a member class
*/ */
private void processMemberClasses(AnnotationMetadata metadata) throws IOException { private void processMemberClasses(ConfigurationClass configClass,
AnnotationMetadata metadata) throws IOException {
if (metadata instanceof StandardAnnotationMetadata) { if (metadata instanceof StandardAnnotationMetadata) {
for (Class<?> memberClass : ((StandardAnnotationMetadata) metadata).getIntrospectedClass().getDeclaredClasses()) { for (Class<?> memberClass : ((StandardAnnotationMetadata) metadata).getIntrospectedClass().getDeclaredClasses()) {
if (ConfigurationClassUtils.isConfigurationCandidate(new StandardAnnotationMetadata(memberClass))) { if (ConfigurationClassUtils.isConfigurationCandidate(new StandardAnnotationMetadata(memberClass))) {
processConfigurationClass(new ConfigurationClass(memberClass, true)); processConfigurationClass(new ConfigurationClass(memberClass, configClass));
} }
} }
} }
...@@ -269,7 +307,7 @@ class ConfigurationClassParser { ...@@ -269,7 +307,7 @@ class ConfigurationClassParser {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName);
AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata(); AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata();
if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) { if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) {
processConfigurationClass(new ConfigurationClass(reader, true)); processConfigurationClass(new ConfigurationClass(reader, configClass));
} }
} }
} }
...@@ -365,6 +403,21 @@ class ConfigurationClassParser { ...@@ -365,6 +403,21 @@ class ConfigurationClassParser {
} }
} }
private void processDeferredImportSelectors() {
Collections.sort(this.deferredImportSelectors, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : this.deferredImportSelectors) {
try {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
processImport(configClass, Arrays.asList(imports), false);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: ", ex);
}
}
deferredImportSelectors.clear();
}
private void processImport(ConfigurationClass configClass, Collection<?> classesToImport, boolean checkForCircularImports) throws IOException { private void processImport(ConfigurationClass configClass, Collection<?> classesToImport, boolean checkForCircularImports) throws IOException {
if (checkForCircularImports && this.importStack.contains(configClass)) { if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata())); this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
...@@ -380,8 +433,13 @@ class ConfigurationClassParser { ...@@ -380,8 +433,13 @@ class ConfigurationClassParser {
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate)); Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate));
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
invokeAwareMethods(selector); invokeAwareMethods(selector);
if(selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(
configClass, (DeferredImportSelector) selector));
} else {
processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false); processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false);
} }
}
else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) { else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions // the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate)); Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate));
...@@ -391,9 +449,11 @@ class ConfigurationClassParser { ...@@ -391,9 +449,11 @@ class ConfigurationClassParser {
} }
else { else {
// candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class // candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
this.importStack.registerImport(importingClassMetadata.getClassName(), (candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate)); this.importStack.registerImport(importingClassMetadata.getClassName(),
processConfigurationClass(candidateToCheck instanceof Class ? new ConfigurationClass((Class) candidateToCheck, true) : (candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
new ConfigurationClass((MetadataReader) candidateToCheck, true)); processConfigurationClass((candidateToCheck instanceof Class ?
new ConfigurationClass((Class) candidateToCheck, configClass) :
new ConfigurationClass((MetadataReader) candidateToCheck, configClass)));
} }
} }
} }
...@@ -533,4 +593,24 @@ class ConfigurationClassParser { ...@@ -533,4 +593,24 @@ class ConfigurationClassParser {
} }
} }
private static class DeferredImportSelectorHolder {
private ConfigurationClass configurationClass;
private DeferredImportSelector importSelector;
public DeferredImportSelectorHolder(ConfigurationClass configurationClass, DeferredImportSelector importSelector) {
this.configurationClass = configurationClass;
this.importSelector = importSelector;
}
public ConfigurationClass getConfigurationClass() {
return configurationClass;
}
public DeferredImportSelector getImportSelector() {
return importSelector;
}
}
} }
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -82,6 +82,7 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*; ...@@ -82,6 +82,7 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*;
* *
* @author Chris Beams * @author Chris Beams
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0 * @since 3.0
*/ */
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
...@@ -275,20 +276,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo ...@@ -275,20 +276,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
ConfigurationClassParser parser = new ConfigurationClassParser( ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment, this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry); this.resourceLoader, this.componentScanBeanNameGenerator, registry);
for (BeanDefinitionHolder holder : configCandidates) { parser.parse(configCandidates);
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parser.parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parser.parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
}
}
parser.validate(); parser.validate();
// Handle any @PropertySource annotations // Handle any @PropertySource annotations
...@@ -312,7 +300,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo ...@@ -312,7 +300,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory, registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
this.resourceLoader, this.environment, this.importBeanNameGenerator); this.resourceLoader, this.environment, this.importBeanNameGenerator);
} }
this.reader.loadBeanDefinitions(parser.getConfigurationClasses()); for (ConfigurationClass configurationClass : parser.getConfigurationClasses()) {
if (!ConditionalAnnotationHelper.shouldSkip(configurationClass, registry,
this.environment, this.importBeanNameGenerator)) {
reader.loadBeanDefinitionsForConfigurationClass(configurationClass);
}
}
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) { if (singletonRegistry != null) {
......
/*
* Copyright 2002-2013 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;
/**
* A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans
* have been processed. This type of selector can be particularly useful when the selected
* imports are {@code @Conditional}.
*
* <p>Implementations can also extend the {@link org.springframework.core.Ordered}
* interface or use the {@link org.springframework.core.annotation.Order} annotation to
* indicate a precedence against other {@link DeferredImportSelector}s.
*
* @author Phillip Webb
* @since 4.0
*/
public interface DeferredImportSelector extends ImportSelector {
}
...@@ -32,8 +32,14 @@ import org.springframework.core.type.AnnotationMetadata; ...@@ -32,8 +32,14 @@ import org.springframework.core.type.AnnotationMetadata;
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li> * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
* </ul> * </ul>
* *
* <p>ImportSelectors are usually processed in the same way as regular {@code @Import}
* annotations, however, it is also possible to defer selection of imports until all
* {@code @Configuration} classes have been processed (see {@link DeferredImportSelector}
* for details).
*
* @author Chris Beams * @author Chris Beams
* @since 3.1 * @since 3.1
* @see DeferredImportSelector
* @see Import * @see Import
* @see ImportBeanDefinitionRegistrar * @see ImportBeanDefinitionRegistrar
* @see Configuration * @see Configuration
......
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -38,8 +38,9 @@ import org.springframework.core.env.ConfigurableEnvironment; ...@@ -38,8 +38,9 @@ import org.springframework.core.env.ConfigurableEnvironment;
* <p>The {@code @Profile} annotation may be used in any of the following ways: * <p>The {@code @Profile} annotation may be used in any of the following ways:
* <ul> * <ul>
* <li>as a type-level annotation on any class directly or indirectly annotated with * <li>as a type-level annotation on any class directly or indirectly annotated with
* {@code @Component}, including {@link Configuration @Configuration} classes * {@code @Component}, including {@link Configuration @Configuration} classes</li>
* <li>as a meta-annotation, for the purpose of composing custom stereotype annotations * <li>as a meta-annotation, for the purpose of composing custom stereotype annotations</li>
* <li>as a method-level annotation on any {@link Bean @Bean} method</li>
* </ul> * </ul>
* *
* <p>If a {@code @Configuration} class is marked with {@code @Profile}, all of the * <p>If a {@code @Configuration} class is marked with {@code @Profile}, all of the
...@@ -65,6 +66,7 @@ import org.springframework.core.env.ConfigurableEnvironment; ...@@ -65,6 +66,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
* {@code spring-beans} XSD (version 3.1 or greater) for details. * {@code spring-beans} XSD (version 3.1 or greater) for details.
* *
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.1 * @since 3.1
* @see ConfigurableEnvironment#setActiveProfiles * @see ConfigurableEnvironment#setActiveProfiles
* @see ConfigurableEnvironment#setDefaultProfiles * @see ConfigurableEnvironment#setDefaultProfiles
...@@ -72,7 +74,8 @@ import org.springframework.core.env.ConfigurableEnvironment; ...@@ -72,7 +74,8 @@ import org.springframework.core.env.ConfigurableEnvironment;
* @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target({ ElementType.TYPE, ElementType.METHOD })
@Conditional(ProfileCondition.class)
public @interface Profile { public @interface Profile {
/** /**
......
/*
* Copyright 2002-2013 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;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* {@link Condition} that matches based on the value of a {@link Profile @Profile}
* annotation.
*
* @author Chris Beams
* @author Phillip Webb
* @since 4.0
*/
class ProfileCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null && metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(Profile.class.getName()));
if (!context.getEnvironment().acceptsProfiles(profile.getStringArray("value"))) {
return false;
}
}
return true;
}
}
/*
* Copyright 2002-2013 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;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.stereotype.Component;
/**
* Test for {@link Conditional} beans.
*
* @author Phillip Webb
*/
public class ConfigurationClassWithConditionTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void conditionalOnBeanMatch() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(BeanOneConfiguration.class, BeanTwoConfiguration.class);
ctx.refresh();
assertTrue(ctx.containsBean("bean1"));
assertFalse(ctx.containsBean("bean2"));
}
@Test
public void conditionalOnBeanNoMatch() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(BeanTwoConfiguration.class);
ctx.refresh();
assertTrue(ctx.containsBean("bean2"));
}
@Test
public void metaConditional() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConfigurationWithMetaCondition.class);
ctx.refresh();
assertTrue(ctx.containsBean("bean"));
}
@Test
public void nonConfigurationClass() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(NonConfigurationClass.class);
ctx.refresh();
thrown.expect(NoSuchBeanDefinitionException.class);
assertNull(ctx.getBean(NonConfigurationClass.class));
}
@Test
public void methodConditional() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConditionOnMethodConfiguration.class);
ctx.refresh();
thrown.expect(NoSuchBeanDefinitionException.class);
assertNull(ctx.getBean(ExampleBean.class));
}
@Configuration
static class BeanOneConfiguration {
@Bean
public ExampleBean bean1() {
return new ExampleBean();
}
}
@Configuration
@Conditional(NoBeanOneCondition.class)
static class BeanTwoConfiguration {
@Bean
public ExampleBean bean2() {
return new ExampleBean();
}
}
@Configuration
@MetaConditional("test")
static class ConfigurationWithMetaCondition {
@Bean
public ExampleBean bean() {
return new ExampleBean();
}
}
@Conditional(MetaConditionalFilter.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public static @interface MetaConditional {
String value();
}
@Conditional(NeverCondition.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public static @interface Never {
}
static class NoBeanOneCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return !context.getBeanFactory().containsBeanDefinition("bean1");
}
}
static class MetaConditionalFilter implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(MetaConditional.class.getName()));
assertThat(attributes.getString("value"), equalTo("test"));
return true;
}
}
static class NeverCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
@Component
@Never
static class NonConfigurationClass {
}
@Configuration
static class ConditionOnMethodConfiguration {
@Bean
@Never
public ExampleBean bean1() {
return new ExampleBean();
}
}
static class ExampleBean {
}
}
/*
* Copyright 2002-2013 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;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Test;
import org.mockito.InOrder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
/**
* Tests for {@link ImportSelector} and {@link DeferredImportSelector}.
*
* @author Phillip Webb
*/
public class ImportSelectorTests {
@Test
public void importSelectors() {
DefaultListableBeanFactory beanFactory = spy(new DefaultListableBeanFactory());
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
beanFactory);
context.register(Config.class);
context.refresh();
context.getBean(Config.class);
InOrder ordered = inOrder(beanFactory);
ordered.verify(beanFactory).registerBeanDefinition(eq("a"), any(BeanDefinition.class));
ordered.verify(beanFactory).registerBeanDefinition(eq("b"), any(BeanDefinition.class));
ordered.verify(beanFactory).registerBeanDefinition(eq("d"), any(BeanDefinition.class));
ordered.verify(beanFactory).registerBeanDefinition(eq("c"), any(BeanDefinition.class));
}
@Sample
@Configuration
static class Config {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({ DeferredImportSelector1.class, DeferredImportSelector2.class,
ImportSelector1.class, ImportSelector2.class })
public static @interface Sample {
}
public static class ImportSelector1 implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] { ImportedSelector1.class.getName() };
}
}
public static class ImportSelector2 implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] { ImportedSelector2.class.getName() };
}
}
public static class DeferredImportSelector1 implements DeferredImportSelector, Ordered {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] { DeferredImportedSelector1.class.getName() };
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
@Order(Ordered.HIGHEST_PRECEDENCE)
public static class DeferredImportSelector2 implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] { DeferredImportedSelector2.class.getName() };
}
}
@Configuration
public static class ImportedSelector1 {
@Bean
public String a() {
return "a";
}
}
@Configuration
public static class ImportedSelector2 {
@Bean
public String b() {
return "b";
}
}
@Configuration
public static class DeferredImportedSelector1 {
@Bean
public String c() {
return "c";
}
}
@Configuration
public static class DeferredImportedSelector2 {
@Bean
public String d() {
return "d";
}
}
}
/*
* Copyright 2002-2013 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.type;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Internal utility class used to collect all annotation values including those declared
* on meta-annotations.
*
* @author Phillip Webb
* @since 4.0
*/
class AnnotatedElementUtils {
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
String annotationType) {
final Set<String> types = new LinkedHashSet<String>();
process(element, annotationType, new Processor<Object>() {
public Object process(Annotation annotation, int depth) {
if (depth > 0) {
types.add(annotation.annotationType().getName());
}
return null;
}
});
return (types.size() == 0 ? null : types);
}
public static boolean hasMetaAnnotationTypes(AnnotatedElement element,
String annotationType) {
return Boolean.TRUE.equals(
process(element, annotationType, new Processor<Boolean>() {
public Boolean process(Annotation annotation, int depth) {
if (depth > 0) {
return true;
}
return null;
}
}));
}
public static boolean isAnnotated(AnnotatedElement element, String annotationType) {
return Boolean.TRUE.equals(
process(element, annotationType, new Processor<Boolean>() {
public Boolean process(Annotation annotation, int depth) {
return true;
}
}));
}
public static Map<String, Object> getAnnotationAttributes(AnnotatedElement element,
String annotationType, final boolean classValuesAsString,
final boolean nestedAnnotationsAsMap) {
return process(element, annotationType, new Processor<Map<String, Object>>() {
public Map<String, Object> process(Annotation annotation, int depth) {
return AnnotationUtils.getAnnotationAttributes(annotation,
classValuesAsString, nestedAnnotationsAsMap);
}
});
}
public static MultiValueMap<String, Object> getAllAnnotationAttributes(
AnnotatedElement element, final String annotationType,
final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
final MultiValueMap<String, Object> attributes = new LinkedMultiValueMap<String, Object>();
process(element, annotationType, new Processor<Object>() {
public Object process(Annotation annotation, int depth) {
if (annotation.annotationType().getName().equals(annotationType)) {
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(
annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
attributes.add(entry.getKey(), entry.getValue());
}
}
return null;
}
});
return (attributes.size() == 0 ? null : attributes);
}
/**
* Process all annotations of the specified annotation type and recursively all
* meta-annotations on the specified type.
* @param element the annotated element
* @param annotationType the annotation type to find. Only items of the specified type
* or meta-annotations of the specified type will be processed
* @param processor the processor
* @return the result of the processor
*/
private static <T> T process(AnnotatedElement element, String annotationType,
Processor<T> processor) {
return recursivelyProcess(element, annotationType, processor,
new HashSet<AnnotatedElement>(), 0);
}
private static <T> T recursivelyProcess(AnnotatedElement element,
String annotationType, Processor<T> processor, Set<AnnotatedElement> visited,
int depth) {
T result = null;
if (visited.add(element)) {
for (Annotation annotation : element.getAnnotations()) {
if (annotation.annotationType().getName().equals(annotationType) || depth > 0) {
result = result != null ? result :
processor.process(annotation, depth);
result = result != null ? result :
recursivelyProcess(annotation.annotationType(), annotationType,
processor, visited, depth + 1);
}
}
for (Annotation annotation : element.getAnnotations()) {
result = result != null ? result :
recursivelyProcess(annotation.annotationType(), annotationType,
processor, visited, depth);
}
}
return result;
}
/**
* Callback interface used to process an annotation.
* @param <T> the result type
*/
private static interface Processor<T> {
/**
* Called to process the annotation.
* @param annotation the annotation to process
* @param depth the depth of the annotation relative to the initial match. For
* example a matched annotation will have a depth of 0, a meta-annotation 1
* and a meta-meta-annotation 2
* @return the result of the processing or {@code null} to continue
*/
T process(Annotation annotation, int depth);
}
}
/*
* Copyright 2002-2013 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.type;
import java.util.Map;
import org.springframework.util.MultiValueMap;
/**
* Defines access to the annotations of a specific type ({@link AnnotationMetadata class}
* or {@link MethodMetadata method}), in a form that does not necessarily require the
* class-loading.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Mark Pollack
* @author Chris Beams
* @author Phillip Webb
* @since 4.0
* @see AnnotationMetadata
* @see MethodMetadata
*/
public interface AnnotatedTypeMetadata {
/**
* Determine whether the underlying type has an annotation or
* meta-annotation of the given type defined.
* <p>If this method returns {@code true}, then
* {@link #getAnnotationAttributes} will return a non-null Map.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* class names for exposure as values in the returned Map, instead of Class
* references which might potentially have to be loaded first
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
/**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying method, as direct annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value") and
* a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType);
/**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying method, as direct annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value") and
* a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String)
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType,
boolean classValuesAsString);
}
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package org.springframework.core.type; package org.springframework.core.type;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
...@@ -25,11 +24,13 @@ import java.util.Set; ...@@ -25,11 +24,13 @@ import java.util.Set;
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Fisher * @author Mark Fisher
* @author Phillip Webb
* @since 2.5 * @since 2.5
* @see StandardAnnotationMetadata * @see StandardAnnotationMetadata
* @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata() * @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata()
* @see AnnotatedTypeMetadata
*/ */
public interface AnnotationMetadata extends ClassMetadata { public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
/** /**
* Return the names of all annotation types defined on the underlying class. * Return the names of all annotation types defined on the underlying class.
...@@ -61,42 +62,6 @@ public interface AnnotationMetadata extends ClassMetadata { ...@@ -61,42 +62,6 @@ public interface AnnotationMetadata extends ClassMetadata {
*/ */
boolean hasMetaAnnotation(String metaAnnotationType); boolean hasMetaAnnotation(String metaAnnotationType);
/**
* Determine whether the underlying class has an annotation or
* meta-annotation of the given type defined.
* <p>This is equivalent to a "hasAnnotation || hasMetaAnnotation"
* check. If this method returns {@code true}, then
* {@link #getAnnotationAttributes} will return a non-null Map.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* class names for exposure as values in the returned Map, instead of Class
* references which might potentially have to be loaded first
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
/** /**
* Determine whether the underlying class has any methods that are * Determine whether the underlying class has any methods that are
* annotated (or meta-annotated) with the given annotation type. * annotated (or meta-annotated) with the given annotation type.
......
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.core.type; package org.springframework.core.type;
import java.util.Map;
/** /**
* Interface that defines abstract access to the annotations of a specific * Interface that defines abstract access to the annotations of a specific
* class, in a form that does not require that class to be loaded yet. * class, in a form that does not require that class to be loaded yet.
...@@ -25,11 +23,13 @@ import java.util.Map; ...@@ -25,11 +23,13 @@ import java.util.Map;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Pollack * @author Mark Pollack
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
* @see StandardMethodMetadata * @see StandardMethodMetadata
* @see AnnotationMetadata#getAnnotatedMethods * @see AnnotationMetadata#getAnnotatedMethods
* @see AnnotatedTypeMetadata
*/ */
public interface MethodMetadata { public interface MethodMetadata extends AnnotatedTypeMetadata {
/** /**
* Return the name of the method. * Return the name of the method.
...@@ -57,23 +57,4 @@ public interface MethodMetadata { ...@@ -57,23 +57,4 @@ public interface MethodMetadata {
*/ */
boolean isOverridable(); boolean isOverridable();
/**
* Determine whether the underlying method has an annotation or
* meta-annotation of the given type defined.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying method, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
} }
...@@ -23,7 +23,7 @@ import java.util.Map; ...@@ -23,7 +23,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.MultiValueMap;
/** /**
* {@link AnnotationMetadata} implementation that uses standard reflection * {@link AnnotationMetadata} implementation that uses standard reflection
...@@ -32,6 +32,7 @@ import org.springframework.core.annotation.AnnotationUtils; ...@@ -32,6 +32,7 @@ import org.springframework.core.annotation.AnnotationUtils;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Fisher * @author Mark Fisher
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 2.5 * @since 2.5
*/ */
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata { public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
...@@ -74,21 +75,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements ...@@ -74,21 +75,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
} }
public Set<String> getMetaAnnotationTypes(String annotationType) { public Set<String> getMetaAnnotationTypes(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations(); return AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationType);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
Set<String> types = new LinkedHashSet<String>();
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation metaAnn : metaAnns) {
types.add(metaAnn.annotationType().getName());
for (Annotation metaMetaAnn : metaAnn.annotationType().getAnnotations()) {
types.add(metaMetaAnn.annotationType().getName());
}
}
return types;
}
}
return null;
} }
public boolean hasAnnotation(String annotationType) { public boolean hasAnnotation(String annotationType) {
...@@ -102,77 +89,40 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements ...@@ -102,77 +89,40 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
} }
public boolean hasMetaAnnotation(String annotationType) { public boolean hasMetaAnnotation(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations(); return AnnotatedElementUtils.hasMetaAnnotationTypes(getIntrospectedClass(), annotationType);
for (Annotation ann : anns) {
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation metaAnn : metaAnns) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
for (Annotation metaMetaAnn : metaAnn.annotationType().getAnnotations()) {
if (metaMetaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
}
}
}
return false;
} }
public boolean isAnnotated(String annotationType) { public boolean isAnnotated(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations(); return AnnotatedElementUtils.isAnnotated(getIntrospectedClass(), annotationType);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return true;
}
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
}
}
return false;
} }
public Map<String, Object> getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
return this.getAnnotationAttributes(annotationType, false); return this.getAnnotationAttributes(annotationType, false);
} }
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) { public Map<String, Object> getAnnotationAttributes(String annotationType,
Annotation[] anns = getIntrospectedClass().getAnnotations(); boolean classValuesAsString) {
for (Annotation ann : anns) { return AnnotatedElementUtils.getAnnotationAttributes(getIntrospectedClass(),
if (ann.annotationType().getName().equals(annotationType)) { annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
return AnnotationUtils.getAnnotationAttributes(
ann, classValuesAsString, this.nestedAnnotationsAsMap);
}
}
for (Annotation ann : anns) {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(
metaAnn, classValuesAsString, this.nestedAnnotationsAsMap);
}
} }
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
return getAllAnnotationAttributes(annotationType, false);
} }
return null;
public MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString) {
return AnnotatedElementUtils.getAllAnnotationAttributes(getIntrospectedClass(),
annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
} }
public boolean hasAnnotatedMethods(String annotationType) { public boolean hasAnnotatedMethods(String annotationType) {
Method[] methods = getIntrospectedClass().getDeclaredMethods(); Method[] methods = getIntrospectedClass().getDeclaredMethods();
for (Method method : methods) { for (Method method : methods) {
for (Annotation ann : method.getAnnotations()) { if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
if (ann.annotationType().getName().equals(annotationType)) {
return true;
}
else {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true; return true;
} }
} }
}
}
}
return false; return false;
} }
...@@ -180,19 +130,9 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements ...@@ -180,19 +130,9 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
Method[] methods = getIntrospectedClass().getDeclaredMethods(); Method[] methods = getIntrospectedClass().getDeclaredMethods();
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>(); Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
for (Method method : methods) { for (Method method : methods) {
for (Annotation ann : method.getAnnotations()) { if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
if (ann.annotationType().getName().equals(annotationType)) { annotatedMethods.add(new StandardMethodMetadata(method,
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap)); this.nestedAnnotationsAsMap));
break;
}
else {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
break;
}
}
}
} }
} }
return annotatedMethods; return annotatedMethods;
......
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,13 +16,12 @@ ...@@ -16,13 +16,12 @@
package org.springframework.core.type; package org.springframework.core.type;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Map; import java.util.Map;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
/** /**
* {@link MethodMetadata} implementation that uses standard reflection * {@link MethodMetadata} implementation that uses standard reflection
...@@ -31,6 +30,7 @@ import org.springframework.util.Assert; ...@@ -31,6 +30,7 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Pollack * @author Mark Pollack
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
*/ */
public class StandardMethodMetadata implements MethodMetadata { public class StandardMethodMetadata implements MethodMetadata {
...@@ -89,35 +89,26 @@ public class StandardMethodMetadata implements MethodMetadata { ...@@ -89,35 +89,26 @@ public class StandardMethodMetadata implements MethodMetadata {
} }
public boolean isAnnotated(String annotationType) { public boolean isAnnotated(String annotationType) {
Annotation[] anns = this.introspectedMethod.getAnnotations(); return AnnotatedElementUtils.isAnnotated(this.introspectedMethod, annotationType);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return true;
}
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
}
}
return false;
} }
public Map<String, Object> getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
Annotation[] anns = this.introspectedMethod.getAnnotations(); return getAnnotationAttributes(annotationType, false);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(
ann, true, nestedAnnotationsAsMap);
}
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(
metaAnn, true, this.nestedAnnotationsAsMap);
} }
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
return AnnotatedElementUtils.getAnnotationAttributes(this.introspectedMethod,
annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
} }
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
return getAllAnnotationAttributes(annotationType, false);
} }
return null;
public MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString) {
return AnnotatedElementUtils.getAllAnnotationAttributes(this.introspectedMethod,
annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
} }
} }
...@@ -34,13 +34,14 @@ import org.springframework.asm.SpringAsmInfo; ...@@ -34,13 +34,14 @@ import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type; import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
/** /**
* @author Chris Beams * @author Chris Beams
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Phillip Webb
* @since 3.1.1 * @since 3.1.1
*/ */
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor { abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
...@@ -51,12 +52,14 @@ abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor { ...@@ -51,12 +52,14 @@ abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
protected final ClassLoader classLoader; protected final ClassLoader classLoader;
public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) { public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) {
super(SpringAsmInfo.ASM_VERSION); super(SpringAsmInfo.ASM_VERSION);
this.classLoader = classLoader; this.classLoader = classLoader;
this.attributes = attributes; this.attributes = attributes;
} }
public void visit(String attributeName, Object attributeValue) { public void visit(String attributeName, Object attributeValue) {
this.attributes.put(attributeName, attributeValue); this.attributes.put(attributeName, attributeValue);
} }
...@@ -108,12 +111,14 @@ final class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationV ...@@ -108,12 +111,14 @@ final class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationV
private final List<AnnotationAttributes> allNestedAttributes = new ArrayList<AnnotationAttributes>(); private final List<AnnotationAttributes> allNestedAttributes = new ArrayList<AnnotationAttributes>();
public RecursiveAnnotationArrayVisitor( public RecursiveAnnotationArrayVisitor(
String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) { String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) {
super(classLoader, attributes); super(classLoader, attributes);
this.attributeName = attributeName; this.attributeName = attributeName;
} }
@Override @Override
public void visit(String attributeName, Object attributeValue) { public void visit(String attributeName, Object attributeValue) {
Object newValue = attributeValue; Object newValue = attributeValue;
...@@ -155,12 +160,14 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi ...@@ -155,12 +160,14 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
private final String annotationType; private final String annotationType;
public RecursiveAnnotationAttributesVisitor( public RecursiveAnnotationAttributesVisitor(
String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) { String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) {
super(classLoader, attributes); super(classLoader, attributes);
this.annotationType = annotationType; this.annotationType = annotationType;
} }
public final void visitEnd() { public final void visitEnd() {
try { try {
Class<?> annotationClass = this.classLoader.loadClass(this.annotationType); Class<?> annotationClass = this.classLoader.loadClass(this.annotationType);
...@@ -214,18 +221,20 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi ...@@ -214,18 +221,20 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
*/ */
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor { final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
private final String annotationType; private final String annotationType;
private final Map<String, AnnotationAttributes> attributesMap; private final MultiValueMap<String, AnnotationAttributes> attributesMap;
private final Map<String, Set<String>> metaAnnotationMap; private final Map<String, Set<String>> metaAnnotationMap;
public AnnotationAttributesReadingVisitor( public AnnotationAttributesReadingVisitor(
String annotationType, Map<String, AnnotationAttributes> attributesMap, String annotationType, MultiValueMap<String, AnnotationAttributes> attributesMap,
Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) { Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) {
super(annotationType, new AnnotationAttributes(), classLoader); super(annotationType, new AnnotationAttributes(), classLoader);
...@@ -234,29 +243,33 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib ...@@ -234,29 +243,33 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib
this.metaAnnotationMap = metaAnnotationMap; this.metaAnnotationMap = metaAnnotationMap;
} }
@Override @Override
public void doVisitEnd(Class<?> annotationClass) { public void doVisitEnd(Class<?> annotationClass) {
super.doVisitEnd(annotationClass); super.doVisitEnd(annotationClass);
this.attributesMap.put(this.annotationType, this.attributes); List<AnnotationAttributes> attributes = this.attributesMap.get(this.annotationType);
registerMetaAnnotations(annotationClass); if (attributes == null) {
this.attributesMap.add(this.annotationType, this.attributes);
}
else {
attributes.add(0, this.attributes);
} }
private void registerMetaAnnotations(Class<?> annotationClass) {
// Register annotations that the annotation type is annotated with.
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>(); Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>();
for (Annotation metaAnnotation : annotationClass.getAnnotations()) { for (Annotation metaAnnotation : annotationClass.getAnnotations()) {
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName()); recusivelyCollectMetaAnnotations(metaAnnotationTypeNames, metaAnnotation);
if (!this.attributesMap.containsKey(metaAnnotation.annotationType().getName())) {
this.attributesMap.put(metaAnnotation.annotationType().getName(),
AnnotationUtils.getAnnotationAttributes(metaAnnotation, true, true));
}
for (Annotation metaMetaAnnotation : metaAnnotation.annotationType().getAnnotations()) {
metaAnnotationTypeNames.add(metaMetaAnnotation.annotationType().getName());
}
} }
if (this.metaAnnotationMap != null) { if (this.metaAnnotationMap != null) {
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
} }
} }
private void recusivelyCollectMetaAnnotations(Set<String> visited, Annotation annotation) {
if (visited.add(annotation.annotationType().getName())) {
this.attributesMap.add(annotation.annotationType().getName(),
AnnotationUtils.getAnnotationAttributes(annotation, true, true));
for (Annotation metaMetaAnnotation : annotation.annotationType().getAnnotations()) {
recusivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
}
}
}
} }
...@@ -41,6 +41,7 @@ import org.springframework.util.MultiValueMap; ...@@ -41,6 +41,7 @@ import org.springframework.util.MultiValueMap;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Fisher * @author Mark Fisher
* @author Costin Leau * @author Costin Leau
* @author Phillip Webb
* @since 2.5 * @since 2.5
*/ */
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata { public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
...@@ -51,7 +52,7 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito ...@@ -51,7 +52,7 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>(4); protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>(4);
protected final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<String, AnnotationAttributes>(4); protected final MultiValueMap<String, AnnotationAttributes> attributeMap = new LinkedMultiValueMap<String, AnnotationAttributes>(4);
protected final MultiValueMap<String, MethodMetadata> methodMetadataMap = new LinkedMultiValueMap<String, MethodMetadata>(); protected final MultiValueMap<String, MethodMetadata> methodMetadataMap = new LinkedMultiValueMap<String, MethodMetadata>();
...@@ -105,60 +106,30 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito ...@@ -105,60 +106,30 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
} }
public AnnotationAttributes getAnnotationAttributes(String annotationType, boolean classValuesAsString) { public AnnotationAttributes getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
AnnotationAttributes raw = this.attributeMap.get(annotationType); List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
return convertClassValues(raw, classValuesAsString); AnnotationAttributes raw = (attributes == null ? null : attributes.get(0));
return AnnotationReadingVisitorUtils.convertClassValues(this.classLoader, raw,
classValuesAsString);
} }
private AnnotationAttributes convertClassValues(AnnotationAttributes original, boolean classValuesAsString) { public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
if (original == null) { return getAllAnnotationAttributes(annotationType, false);
return null;
}
AnnotationAttributes result = new AnnotationAttributes(original.size());
for (Map.Entry<String, Object> entry : original.entrySet()) {
try {
Object value = entry.getValue();
if (value instanceof AnnotationAttributes) {
value = convertClassValues((AnnotationAttributes) value, classValuesAsString);
}
else if (value instanceof AnnotationAttributes[]) {
AnnotationAttributes[] values = (AnnotationAttributes[])value;
for (int i = 0; i < values.length; i++) {
values[i] = convertClassValues(values[i], classValuesAsString);
}
}
else if (value instanceof Type) {
value = (classValuesAsString ? ((Type) value).getClassName() :
this.classLoader.loadClass(((Type) value).getClassName()));
}
else if (value instanceof Type[]) {
Type[] array = (Type[]) value;
Object[] convArray = (classValuesAsString ? new String[array.length] : new Class[array.length]);
for (int i = 0; i < array.length; i++) {
convArray[i] = (classValuesAsString ? array[i].getClassName() :
this.classLoader.loadClass(array[i].getClassName()));
}
value = convArray;
} }
else if (classValuesAsString) {
if (value instanceof Class) { public MultiValueMap<String, Object> getAllAnnotationAttributes(
value = ((Class<?>) value).getName(); String annotationType, boolean classValuesAsString) {
} MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<String, Object>();
else if (value instanceof Class[]) { List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
Class<?>[] clazzArray = (Class[]) value; if (attributes == null) {
String[] newValue = new String[clazzArray.length]; return null;
for (int i = 0; i < clazzArray.length; i++) {
newValue[i] = clazzArray[i].getName();
}
value = newValue;
}
}
result.put(entry.getKey(), value);
} }
catch (Exception ex) { for (AnnotationAttributes raw : attributes) {
// Class not found - can't resolve class reference in annotation attribute. for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues(
this.classLoader, raw, classValuesAsString).entrySet()) {
allAttributes.add(entry.getKey(), entry.getValue());
} }
} }
return result; return allAttributes;
} }
public boolean hasAnnotatedMethods(String annotationType) { public boolean hasAnnotatedMethods(String annotationType) {
......
/*
* Copyright 2002-2013 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.type.classreading;
import java.util.Map;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
/**
* Internal utility class used when reading annotations.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Costin Leau
* @author Phillip Webb
* @since 4.0
*/
abstract class AnnotationReadingVisitorUtils {
public static AnnotationAttributes convertClassValues(ClassLoader classLoader,
AnnotationAttributes original, boolean classValuesAsString) {
if (original == null) {
return null;
}
AnnotationAttributes result = new AnnotationAttributes(original.size());
for (Map.Entry<String, Object> entry : original.entrySet()) {
try {
Object value = entry.getValue();
if (value instanceof AnnotationAttributes) {
value = convertClassValues(classLoader, (AnnotationAttributes) value,
classValuesAsString);
}
else if (value instanceof AnnotationAttributes[]) {
AnnotationAttributes[] values = (AnnotationAttributes[])value;
for (int i = 0; i < values.length; i++) {
values[i] = convertClassValues(classLoader, values[i],
classValuesAsString);
}
}
else if (value instanceof Type) {
value = (classValuesAsString ? ((Type) value).getClassName() :
classLoader.loadClass(((Type) value).getClassName()));
}
else if (value instanceof Type[]) {
Type[] array = (Type[]) value;
Object[] convArray = (classValuesAsString ? new String[array.length] : new Class[array.length]);
for (int i = 0; i < array.length; i++) {
convArray[i] = (classValuesAsString ? array[i].getClassName() :
classLoader.loadClass(array[i].getClassName()));
}
value = convArray;
}
else if (classValuesAsString) {
if (value instanceof Class) {
value = ((Class<?>) value).getName();
}
else if (value instanceof Class[]) {
Class<?>[] clazzArray = (Class[]) value;
String[] newValue = new String[clazzArray.length];
for (int i = 0; i < clazzArray.length; i++) {
newValue[i] = clazzArray[i].getName();
}
value = newValue;
}
}
result.put(entry.getKey(), value);
}
catch (Exception ex) {
// Class not found - can't resolve class reference in annotation attribute.
}
}
return result;
}
}
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
package org.springframework.core.type.classreading; package org.springframework.core.type.classreading;
import java.util.LinkedHashMap; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.asm.AnnotationVisitor; import org.springframework.asm.AnnotationVisitor;
...@@ -26,6 +26,7 @@ import org.springframework.asm.SpringAsmInfo; ...@@ -26,6 +26,7 @@ import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type; import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.MethodMetadata;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
/** /**
...@@ -37,6 +38,7 @@ import org.springframework.util.MultiValueMap; ...@@ -37,6 +38,7 @@ import org.springframework.util.MultiValueMap;
* @author Mark Pollack * @author Mark Pollack
* @author Costin Leau * @author Costin Leau
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
*/ */
public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata { public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {
...@@ -51,7 +53,7 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho ...@@ -51,7 +53,7 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
protected final MultiValueMap<String, MethodMetadata> methodMetadataMap; protected final MultiValueMap<String, MethodMetadata> methodMetadataMap;
protected final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<String, AnnotationAttributes>(2); protected final MultiValueMap<String, AnnotationAttributes> attributeMap = new LinkedMultiValueMap<String, AnnotationAttributes>(2);
public MethodMetadataReadingVisitor(String name, int access, String declaringClassName, ClassLoader classLoader, public MethodMetadataReadingVisitor(String name, int access, String declaringClassName, ClassLoader classLoader,
...@@ -93,8 +95,34 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho ...@@ -93,8 +95,34 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
return this.attributeMap.containsKey(annotationType); return this.attributeMap.containsKey(annotationType);
} }
public AnnotationAttributes getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
return this.attributeMap.get(annotationType); return getAnnotationAttributes(annotationType, false);
}
public Map<String, Object> getAnnotationAttributes(String annotationType,
boolean classValuesAsString) {
List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
return (attributes == null ? null : AnnotationReadingVisitorUtils.convertClassValues(
this.classLoader, attributes.get(0), classValuesAsString));
}
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
return getAllAnnotationAttributes(annotationType, false);
}
public MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString) {
if(!this.attributeMap.containsKey(annotationType)) {
return null;
}
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<String, Object>();
for (AnnotationAttributes annotationAttributes : this.attributeMap.get(annotationType)) {
for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues(
this.classLoader, annotationAttributes, classValuesAsString).entrySet()) {
allAttributes.add(entry.getKey(), entry.getValue());
}
}
return allAttributes;
} }
public String getDeclaringClassName() { public String getDeclaringClassName() {
......
...@@ -18,7 +18,9 @@ package org.springframework.core.type; ...@@ -18,7 +18,9 @@ package org.springframework.core.type;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
...@@ -29,10 +31,12 @@ import java.lang.annotation.ElementType; ...@@ -29,10 +31,12 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
...@@ -45,6 +49,7 @@ import org.springframework.stereotype.Component; ...@@ -45,6 +49,7 @@ import org.springframework.stereotype.Component;
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
*/ */
public class AnnotationMetadataTests { public class AnnotationMetadataTests {
...@@ -93,7 +98,7 @@ public class AnnotationMetadataTests { ...@@ -93,7 +98,7 @@ public class AnnotationMetadataTests {
assertThat(metadata.hasAnnotation(Component.class.getName()), is(true)); assertThat(metadata.hasAnnotation(Component.class.getName()), is(true));
assertThat(metadata.hasAnnotation(Scope.class.getName()), is(true)); assertThat(metadata.hasAnnotation(Scope.class.getName()), is(true));
assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(true)); assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().size(), is(3)); assertThat(metadata.getAnnotationTypes().size(), is(5));
assertThat(metadata.getAnnotationTypes().contains(Component.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(Component.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(Scope.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(Scope.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true));
...@@ -104,6 +109,15 @@ public class AnnotationMetadataTests { ...@@ -104,6 +109,15 @@ public class AnnotationMetadataTests {
AnnotationAttributes scopeAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Scope.class.getName()); AnnotationAttributes scopeAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Scope.class.getName());
assertThat(scopeAttrs.size(), is(1)); assertThat(scopeAttrs.size(), is(1));
assertThat(scopeAttrs.getString("value"), is("myScope")); assertThat(scopeAttrs.getString("value"), is("myScope"));
Set<MethodMetadata> methods = metadata.getAnnotatedMethods(DirectAnnotation.class.getName());
MethodMetadata method = methods.iterator().next();
assertEquals("direct", method.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value"));
List<Object> allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value");
assertThat(new HashSet<Object>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta")))));
assertTrue(metadata.isAnnotated(IsAnnotatedAnnotation.class.getName()));
{ // perform tests with classValuesAsString = false (the default) { // perform tests with classValuesAsString = false (the default)
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName()); AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName());
assertThat(specialAttrs.size(), is(6)); assertThat(specialAttrs.size(), is(6));
...@@ -137,6 +151,10 @@ public class AnnotationMetadataTests { ...@@ -137,6 +151,10 @@ public class AnnotationMetadataTests {
assertTrue(optionalArray[0].getEnum("anEnum").equals(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}, (Class[])optionalArray[0].get("classArray"));
assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray")); assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray"));
assertEquals("direct", metadata.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value"));
allMeta = metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value");
assertThat(new HashSet<Object>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta")))));
} }
{ // perform tests with classValuesAsString = true { // perform tests with classValuesAsString = true
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true); AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true);
...@@ -161,6 +179,10 @@ public class AnnotationMetadataTests { ...@@ -161,6 +179,10 @@ public class AnnotationMetadataTests {
AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray"); AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray");
assertArrayEquals(new String[]{Void.class.getName()}, (String[])optionalArray[0].get("classArray")); assertArrayEquals(new String[]{Void.class.getName()}, (String[])optionalArray[0].get("classArray"));
assertArrayEquals(new String[]{Void.class.getName()}, optionalArray[0].getStringArray("classArray")); assertArrayEquals(new String[]{Void.class.getName()}, optionalArray[0].getStringArray("classArray"));
assertEquals(metadata.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value"), "direct");
allMeta = metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value");
assertThat(new HashSet<Object>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta")))));
} }
} }
...@@ -201,6 +223,29 @@ public class AnnotationMetadataTests { ...@@ -201,6 +223,29 @@ public class AnnotationMetadataTests {
NestedAnno[] optionalArray() default {@NestedAnno(value="optional", anEnum=SomeEnum.DEFAULT, classArray=Void.class)}; NestedAnno[] optionalArray() default {@NestedAnno(value="optional", anEnum=SomeEnum.DEFAULT, classArray=Void.class)};
} }
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DirectAnnotation {
String value();
}
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface IsAnnotatedAnnotation {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@DirectAnnotation("meta")
@IsAnnotatedAnnotation
public @interface MetaAnnotation {
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@MetaAnnotation
public @interface MetaMetaAnnotation {
}
@Component("myName") @Component("myName")
@Scope("myScope") @Scope("myScope")
...@@ -211,6 +256,8 @@ public class AnnotationMetadataTests { ...@@ -211,6 +256,8 @@ public class AnnotationMetadataTests {
@NestedAnno(value = "na1", anEnum = SomeEnum.LABEL2, classArray = {Number.class}) @NestedAnno(value = "na1", anEnum = SomeEnum.LABEL2, classArray = {Number.class})
}) })
@SuppressWarnings({"serial", "unused"}) @SuppressWarnings({"serial", "unused"})
@DirectAnnotation("direct")
@MetaMetaAnnotation
private static class AnnotatedComponent implements Serializable { private static class AnnotatedComponent implements Serializable {
@TestAutowired @TestAutowired
...@@ -219,6 +266,11 @@ public class AnnotationMetadataTests { ...@@ -219,6 +266,11 @@ public class AnnotationMetadataTests {
public void doSleep() { public void doSleep() {
} }
@DirectAnnotation("direct")
@MetaMetaAnnotation
public void meta() {
}
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册