Merge branch 'SPR-10534'
* SPR-10534: Add support for DeferredImportSelector Support for @Conditional configuration Extend AnnotationMetadata and MethodMetadata
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,11 +24,9 @@ import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.EnvironmentCapable;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -39,6 +37,7 @@ import org.springframework.util.Assert;
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
* @see AnnotationConfigApplicationContext#register
|
||||
*/
|
||||
@@ -134,12 +133,9 @@ public class AnnotatedBeanDefinitionReader {
|
||||
|
||||
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
|
||||
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
|
||||
AnnotationMetadata metadata = abd.getMetadata();
|
||||
if (metadata.isAnnotated(Profile.class.getName())) {
|
||||
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
|
||||
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
|
||||
return;
|
||||
}
|
||||
if (ConditionalAnnotationHelper.shouldSkip(abd, this.registry,
|
||||
this.environment, this.beanNameGenerator)) {
|
||||
return;
|
||||
}
|
||||
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
|
||||
abd.setScope(scopeMetadata.getScopeName());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,6 +52,7 @@ import org.springframework.util.PatternMatchUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 2.5
|
||||
* @see AnnotationConfigApplicationContext#scan
|
||||
* @see org.springframework.stereotype.Component
|
||||
@@ -298,6 +299,10 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||
* bean definition has been found for the specified name
|
||||
*/
|
||||
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
|
||||
if (ConditionalAnnotationHelper.shouldSkip(beanDefinition, getRegistry(),
|
||||
getEnvironment(), this.beanNameGenerator)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.registry.containsBeanDefinition(beanName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
* @see BeanMethod
|
||||
* @see ConfigurationClassParser
|
||||
@@ -58,7 +59,7 @@ final class ConfigurationClass {
|
||||
|
||||
private String beanName;
|
||||
|
||||
private final boolean imported;
|
||||
private final ConfigurationClass importedBy;
|
||||
|
||||
|
||||
/**
|
||||
@@ -66,28 +67,28 @@ final class ConfigurationClass {
|
||||
* @param metadataReader reader used to parse the underlying {@link Class}
|
||||
* @param beanName must not be {@code null}
|
||||
* @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) {
|
||||
Assert.hasText(beanName, "bean name must not be null");
|
||||
this.metadata = metadataReader.getAnnotationMetadata();
|
||||
this.resource = metadataReader.getResource();
|
||||
this.beanName = beanName;
|
||||
this.imported = false;
|
||||
this.importedBy = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigurationClass} representing a class that was imported
|
||||
* 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 imported whether the given configuration class is being imported
|
||||
* @param importedBy the configuration class importing this one or {@code null}
|
||||
* @since 3.1.1
|
||||
*/
|
||||
public ConfigurationClass(MetadataReader metadataReader, boolean imported) {
|
||||
public ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
|
||||
this.metadata = metadataReader.getAnnotationMetadata();
|
||||
this.resource = metadataReader.getResource();
|
||||
this.imported = imported;
|
||||
this.importedBy = importedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,14 +96,14 @@ final class ConfigurationClass {
|
||||
* @param clazz the underlying {@link Class} to represent
|
||||
* @param beanName name of the {@code @Configuration} class bean
|
||||
* @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) {
|
||||
Assert.hasText(beanName, "bean name must not be null");
|
||||
this.metadata = new StandardAnnotationMetadata(clazz, true);
|
||||
this.resource = new DescriptiveResource(clazz.toString());
|
||||
this.beanName = beanName;
|
||||
this.imported = false;
|
||||
this.importedBy = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,13 +111,13 @@ final class ConfigurationClass {
|
||||
* using the {@link Import} annotation or automatically processed as a nested
|
||||
* configuration class (if imported is {@code true}).
|
||||
* @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
|
||||
*/
|
||||
public ConfigurationClass(Class<?> clazz, boolean imported) {
|
||||
public ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
|
||||
this.metadata = new StandardAnnotationMetadata(clazz, true);
|
||||
this.resource = new DescriptiveResource(clazz.toString());
|
||||
this.imported = imported;
|
||||
this.importedBy = importedBy;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,9 +137,20 @@ final class ConfigurationClass {
|
||||
* Return whether this configuration class was registered via @{@link Import} or
|
||||
* automatically registered due to being nested within another configuration class.
|
||||
* @since 3.1.1
|
||||
* @see #getImportedBy()
|
||||
*/
|
||||
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) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -26,7 +28,6 @@ import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
@@ -51,8 +52,6 @@ import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
|
||||
/**
|
||||
* Reads a given fully-populated set of ConfigurationClass instances, registering bean
|
||||
* definitions with the given {@link BeanDefinitionRegistry} based on its contents.
|
||||
@@ -63,6 +62,7 @@ import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
* @see ConfigurationClassParser
|
||||
*/
|
||||
@@ -118,7 +118,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
* Read a particular {@link ConfigurationClass}, registering bean definitions for the
|
||||
* class itself, all its {@link Bean} methods
|
||||
*/
|
||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||
public void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||
if (configClass.isImported()) {
|
||||
registerBeanDefinitionForImportedConfigurationClass(configClass);
|
||||
}
|
||||
@@ -153,6 +153,10 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
* with the BeanDefinitionRegistry based on its contents.
|
||||
*/
|
||||
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
|
||||
if (ConditionalAnnotationHelper.shouldSkip(beanMethod, this.registry,
|
||||
this.environment, this.importBeanNameGenerator)) {
|
||||
return;
|
||||
}
|
||||
ConfigurationClass configClass = beanMethod.getConfigurationClass();
|
||||
MethodMetadata metadata = beanMethod.getMetadata();
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
@@ -32,19 +34,23 @@ import java.util.Stack;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
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.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
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.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.NestedIOException;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
@@ -76,11 +82,21 @@ import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
* @see ConfigurationClassBeanDefinitionReader
|
||||
*/
|
||||
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 ProblemReporter problemReporter;
|
||||
@@ -103,6 +119,10 @@ class ConfigurationClassParser {
|
||||
|
||||
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
|
||||
@@ -117,10 +137,28 @@ class ConfigurationClassParser {
|
||||
this.environment = environment;
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.registry = registry;
|
||||
this.beanNameGenerator = componentScanBeanNameGenerator;
|
||||
this.componentScanParser = new ComponentScanAnnotationParser(
|
||||
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.
|
||||
@@ -138,17 +176,16 @@ class ConfigurationClassParser {
|
||||
* @param clazz the Class to parse
|
||||
* @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));
|
||||
}
|
||||
|
||||
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
|
||||
AnnotationMetadata metadata = configClass.getMetadata();
|
||||
if (this.environment != null && metadata.isAnnotated(Profile.class.getName())) {
|
||||
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
|
||||
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConditionalAnnotationHelper.shouldSkip(configClass, this.registry,
|
||||
this.environment, this.beanNameGenerator)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// recursively process the configuration class and its superclass hierarchy
|
||||
@@ -173,7 +210,7 @@ class ConfigurationClassParser {
|
||||
ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
|
||||
|
||||
// recursively process any member (nested) classes first
|
||||
processMemberClasses(metadata);
|
||||
processMemberClasses(configClass, metadata);
|
||||
|
||||
// process any @PropertySource annotations
|
||||
AnnotationAttributes propertySource = attributesFor(metadata, org.springframework.context.annotation.PropertySource.class);
|
||||
@@ -256,11 +293,12 @@ class ConfigurationClassParser {
|
||||
* @param metadata the metadata representation of the containing 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) {
|
||||
for (Class<?> memberClass : ((StandardAnnotationMetadata) metadata).getIntrospectedClass().getDeclaredClasses()) {
|
||||
if (ConfigurationClassUtils.isConfigurationCandidate(new StandardAnnotationMetadata(memberClass))) {
|
||||
processConfigurationClass(new ConfigurationClass(memberClass, true));
|
||||
processConfigurationClass(new ConfigurationClass(memberClass, configClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,7 +307,7 @@ class ConfigurationClassParser {
|
||||
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName);
|
||||
AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata();
|
||||
if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) {
|
||||
processConfigurationClass(new ConfigurationClass(reader, true));
|
||||
processConfigurationClass(new ConfigurationClass(reader, configClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
if (checkForCircularImports && this.importStack.contains(configClass)) {
|
||||
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
|
||||
@@ -380,7 +433,12 @@ class ConfigurationClassParser {
|
||||
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate));
|
||||
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
|
||||
invokeAwareMethods(selector);
|
||||
processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false);
|
||||
if(selector instanceof DeferredImportSelector) {
|
||||
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(
|
||||
configClass, (DeferredImportSelector) selector));
|
||||
} else {
|
||||
processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false);
|
||||
}
|
||||
}
|
||||
else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
|
||||
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
|
||||
@@ -391,9 +449,11 @@ class ConfigurationClassParser {
|
||||
}
|
||||
else {
|
||||
// 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));
|
||||
processConfigurationClass(candidateToCheck instanceof Class ? new ConfigurationClass((Class) candidateToCheck, true) :
|
||||
new ConfigurationClass((MetadataReader) candidateToCheck, true));
|
||||
this.importStack.registerImport(importingClassMetadata.getClassName(),
|
||||
(candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
|
||||
processConfigurationClass((candidateToCheck instanceof Class ?
|
||||
new ConfigurationClass((Class) candidateToCheck, configClass) :
|
||||
new ConfigurationClass((MetadataReader) candidateToCheck, configClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -82,6 +82,7 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
|
||||
@@ -275,20 +276,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
ConfigurationClassParser parser = new ConfigurationClassParser(
|
||||
this.metadataReaderFactory, this.problemReporter, this.environment,
|
||||
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
|
||||
for (BeanDefinitionHolder holder : 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.parse(configCandidates);
|
||||
parser.validate();
|
||||
|
||||
// Handle any @PropertySource annotations
|
||||
@@ -312,7 +300,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
|
||||
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
|
||||
if (singletonRegistry != null) {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
|
||||
* </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
|
||||
* @since 3.1
|
||||
* @see DeferredImportSelector
|
||||
* @see Import
|
||||
* @see ImportBeanDefinitionRegistrar
|
||||
* @see Configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,8 +38,9 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
* <p>The {@code @Profile} 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>as a meta-annotation, for the purpose of composing custom stereotype annotations
|
||||
* {@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 @Profile}, all of the
|
||||
@@ -65,6 +66,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
* {@code spring-beans} XSD (version 3.1 or greater) for details.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
* @see ConfigurableEnvironment#setActiveProfiles
|
||||
* @see ConfigurableEnvironment#setDefaultProfiles
|
||||
@@ -72,7 +74,8 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
* @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Conditional(ProfileCondition.class)
|
||||
public @interface Profile {
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.core.type;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -25,11 +24,13 @@ import java.util.Set;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @author Phillip Webb
|
||||
* @since 2.5
|
||||
* @see StandardAnnotationMetadata
|
||||
* @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.
|
||||
@@ -61,42 +62,6 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||
*/
|
||||
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
|
||||
* annotated (or meta-annotated) with the given annotation type.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.core.type;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -25,11 +23,13 @@ import java.util.Map;
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Pollack
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
* @see StandardMethodMetadata
|
||||
* @see AnnotationMetadata#getAnnotatedMethods
|
||||
* @see AnnotatedTypeMetadata
|
||||
*/
|
||||
public interface MethodMetadata {
|
||||
public interface MethodMetadata extends AnnotatedTypeMetadata {
|
||||
|
||||
/**
|
||||
* Return the name of the method.
|
||||
@@ -57,23 +57,4 @@ public interface MethodMetadata {
|
||||
*/
|
||||
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;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link AnnotationMetadata} implementation that uses standard reflection
|
||||
@@ -32,6 +32,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 2.5
|
||||
*/
|
||||
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
|
||||
@@ -74,21 +75,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||
}
|
||||
|
||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||
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;
|
||||
return AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationType);
|
||||
}
|
||||
|
||||
public boolean hasAnnotation(String annotationType) {
|
||||
@@ -102,75 +89,38 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||
}
|
||||
|
||||
public boolean hasMetaAnnotation(String annotationType) {
|
||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||
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;
|
||||
return AnnotatedElementUtils.hasMetaAnnotationTypes(getIntrospectedClass(), annotationType);
|
||||
}
|
||||
|
||||
public boolean isAnnotated(String annotationType) {
|
||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||
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;
|
||||
return AnnotatedElementUtils.isAnnotated(getIntrospectedClass(), annotationType);
|
||||
}
|
||||
|
||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||
return this.getAnnotationAttributes(annotationType, false);
|
||||
}
|
||||
|
||||
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
|
||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||
for (Annotation ann : anns) {
|
||||
if (ann.annotationType().getName().equals(annotationType)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
public Map<String, Object> getAnnotationAttributes(String annotationType,
|
||||
boolean classValuesAsString) {
|
||||
return AnnotatedElementUtils.getAnnotationAttributes(getIntrospectedClass(),
|
||||
annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
|
||||
}
|
||||
|
||||
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
|
||||
return getAllAnnotationAttributes(annotationType, false);
|
||||
}
|
||||
|
||||
public MultiValueMap<String, Object> getAllAnnotationAttributes(
|
||||
String annotationType, boolean classValuesAsString) {
|
||||
return AnnotatedElementUtils.getAllAnnotationAttributes(getIntrospectedClass(),
|
||||
annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
|
||||
}
|
||||
|
||||
public boolean hasAnnotatedMethods(String annotationType) {
|
||||
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
for (Annotation ann : method.getAnnotations()) {
|
||||
if (ann.annotationType().getName().equals(annotationType)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
|
||||
if (metaAnn.annotationType().getName().equals(annotationType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -180,19 +130,9 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||
Method[] methods = getIntrospectedClass().getDeclaredMethods();
|
||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||
for (Method method : methods) {
|
||||
for (Annotation ann : method.getAnnotations()) {
|
||||
if (ann.annotationType().getName().equals(annotationType)) {
|
||||
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
|
||||
if (metaAnn.annotationType().getName().equals(annotationType)) {
|
||||
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
|
||||
annotatedMethods.add(new StandardMethodMetadata(method,
|
||||
this.nestedAnnotationsAsMap));
|
||||
}
|
||||
}
|
||||
return annotatedMethods;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package org.springframework.core.type;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link MethodMetadata} implementation that uses standard reflection
|
||||
@@ -31,6 +30,7 @@ import org.springframework.util.Assert;
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Pollack
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StandardMethodMetadata implements MethodMetadata {
|
||||
@@ -89,35 +89,26 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||
}
|
||||
|
||||
public boolean isAnnotated(String annotationType) {
|
||||
Annotation[] anns = this.introspectedMethod.getAnnotations();
|
||||
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;
|
||||
return AnnotatedElementUtils.isAnnotated(this.introspectedMethod, annotationType);
|
||||
}
|
||||
|
||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||
Annotation[] anns = this.introspectedMethod.getAnnotations();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return getAnnotationAttributes(annotationType, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.1
|
||||
*/
|
||||
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
|
||||
@@ -51,12 +52,14 @@ abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
|
||||
|
||||
protected final ClassLoader classLoader;
|
||||
|
||||
|
||||
public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) {
|
||||
super(SpringAsmInfo.ASM_VERSION);
|
||||
this.classLoader = classLoader;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
|
||||
public void visit(String attributeName, Object attributeValue) {
|
||||
this.attributes.put(attributeName, attributeValue);
|
||||
}
|
||||
@@ -108,12 +111,14 @@ final class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationV
|
||||
|
||||
private final List<AnnotationAttributes> allNestedAttributes = new ArrayList<AnnotationAttributes>();
|
||||
|
||||
|
||||
public RecursiveAnnotationArrayVisitor(
|
||||
String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) {
|
||||
super(classLoader, attributes);
|
||||
this.attributeName = attributeName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visit(String attributeName, Object attributeValue) {
|
||||
Object newValue = attributeValue;
|
||||
@@ -155,12 +160,14 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
|
||||
|
||||
private final String annotationType;
|
||||
|
||||
|
||||
public RecursiveAnnotationAttributesVisitor(
|
||||
String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) {
|
||||
super(classLoader, attributes);
|
||||
this.annotationType = annotationType;
|
||||
}
|
||||
|
||||
|
||||
public final void visitEnd() {
|
||||
try {
|
||||
Class<?> annotationClass = this.classLoader.loadClass(this.annotationType);
|
||||
@@ -214,18 +221,20 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
|
||||
|
||||
private final String annotationType;
|
||||
|
||||
private final Map<String, AnnotationAttributes> attributesMap;
|
||||
private final MultiValueMap<String, AnnotationAttributes> attributesMap;
|
||||
|
||||
private final Map<String, Set<String>> metaAnnotationMap;
|
||||
|
||||
|
||||
public AnnotationAttributesReadingVisitor(
|
||||
String annotationType, Map<String, AnnotationAttributes> attributesMap,
|
||||
String annotationType, MultiValueMap<String, AnnotationAttributes> attributesMap,
|
||||
Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) {
|
||||
|
||||
super(annotationType, new AnnotationAttributes(), classLoader);
|
||||
@@ -234,29 +243,33 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib
|
||||
this.metaAnnotationMap = metaAnnotationMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doVisitEnd(Class<?> annotationClass) {
|
||||
super.doVisitEnd(annotationClass);
|
||||
this.attributesMap.put(this.annotationType, this.attributes);
|
||||
registerMetaAnnotations(annotationClass);
|
||||
}
|
||||
|
||||
private void registerMetaAnnotations(Class<?> annotationClass) {
|
||||
// Register annotations that the annotation type is annotated with.
|
||||
List<AnnotationAttributes> attributes = this.attributesMap.get(this.annotationType);
|
||||
if (attributes == null) {
|
||||
this.attributesMap.add(this.annotationType, this.attributes);
|
||||
}
|
||||
else {
|
||||
attributes.add(0, this.attributes);
|
||||
}
|
||||
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>();
|
||||
for (Annotation metaAnnotation : annotationClass.getAnnotations()) {
|
||||
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
||||
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());
|
||||
}
|
||||
recusivelyCollectMetaAnnotations(metaAnnotationTypeNames, metaAnnotation);
|
||||
}
|
||||
if (this.metaAnnotationMap != null) {
|
||||
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;
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
* @since 2.5
|
||||
*/
|
||||
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
|
||||
@@ -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, 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>();
|
||||
|
||||
@@ -105,60 +106,30 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
|
||||
}
|
||||
|
||||
public AnnotationAttributes getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
|
||||
AnnotationAttributes raw = this.attributeMap.get(annotationType);
|
||||
return convertClassValues(raw, classValuesAsString);
|
||||
List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
|
||||
AnnotationAttributes raw = (attributes == null ? null : attributes.get(0));
|
||||
return AnnotationReadingVisitorUtils.convertClassValues(this.classLoader, raw,
|
||||
classValuesAsString);
|
||||
}
|
||||
|
||||
private AnnotationAttributes convertClassValues(AnnotationAttributes original, boolean classValuesAsString) {
|
||||
if (original == null) {
|
||||
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
|
||||
return getAllAnnotationAttributes(annotationType, false);
|
||||
}
|
||||
|
||||
public MultiValueMap<String, Object> getAllAnnotationAttributes(
|
||||
String annotationType, boolean classValuesAsString) {
|
||||
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<String, Object>();
|
||||
List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
|
||||
if (attributes == 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((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) {
|
||||
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.
|
||||
for (AnnotationAttributes raw : attributes) {
|
||||
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) {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 @@
|
||||
|
||||
package org.springframework.core.type.classreading;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
@@ -26,6 +26,7 @@ import org.springframework.asm.SpringAsmInfo;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
@@ -37,6 +38,7 @@ import org.springframework.util.MultiValueMap;
|
||||
* @author Mark Pollack
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {
|
||||
@@ -51,7 +53,7 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
|
||||
|
||||
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,
|
||||
@@ -93,8 +95,34 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
|
||||
return this.attributeMap.containsKey(annotationType);
|
||||
}
|
||||
|
||||
public AnnotationAttributes getAnnotationAttributes(String annotationType) {
|
||||
return this.attributeMap.get(annotationType);
|
||||
public Map<String, Object> getAnnotationAttributes(String 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() {
|
||||
|
||||
@@ -18,7 +18,9 @@ package org.springframework.core.type;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -29,10 +31,12 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
@@ -45,6 +49,7 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class AnnotationMetadataTests {
|
||||
|
||||
@@ -93,7 +98,7 @@ public class AnnotationMetadataTests {
|
||||
assertThat(metadata.hasAnnotation(Component.class.getName()), is(true));
|
||||
assertThat(metadata.hasAnnotation(Scope.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(Scope.class.getName()), is(true));
|
||||
assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true));
|
||||
@@ -104,6 +109,15 @@ public class AnnotationMetadataTests {
|
||||
AnnotationAttributes scopeAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Scope.class.getName());
|
||||
assertThat(scopeAttrs.size(), is(1));
|
||||
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)
|
||||
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName());
|
||||
assertThat(specialAttrs.size(), is(6));
|
||||
@@ -137,6 +151,10 @@ public class AnnotationMetadataTests {
|
||||
assertTrue(optionalArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
|
||||
assertArrayEquals(new Class[]{Void.class}, (Class[])optionalArray[0].get("classArray"));
|
||||
assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray"));
|
||||
|
||||
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
|
||||
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true);
|
||||
@@ -161,6 +179,10 @@ public class AnnotationMetadataTests {
|
||||
AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray");
|
||||
assertArrayEquals(new String[]{Void.class.getName()}, (String[])optionalArray[0].get("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 {
|
||||
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")
|
||||
@Scope("myScope")
|
||||
@@ -211,6 +256,8 @@ public class AnnotationMetadataTests {
|
||||
@NestedAnno(value = "na1", anEnum = SomeEnum.LABEL2, classArray = {Number.class})
|
||||
})
|
||||
@SuppressWarnings({"serial", "unused"})
|
||||
@DirectAnnotation("direct")
|
||||
@MetaMetaAnnotation
|
||||
private static class AnnotatedComponent implements Serializable {
|
||||
|
||||
@TestAutowired
|
||||
@@ -219,6 +266,11 @@ public class AnnotationMetadataTests {
|
||||
|
||||
public void doSleep() {
|
||||
}
|
||||
|
||||
@DirectAnnotation("direct")
|
||||
@MetaMetaAnnotation
|
||||
public void meta() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user