/* * Copyright 2002-2016 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.ArrayList; import java.util.Collections; import java.util.List; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase; import org.springframework.core.annotation.AnnotationAwareOrderComparator; 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.core.type.AnnotationMetadata; import org.springframework.util.ClassUtils; import org.springframework.util.MultiValueMap; /** * Internal class used to evaluate {@link Conditional} annotations. * * @author Phillip Webb * @since 4.0 */ class ConditionEvaluator { private final ConditionContextImpl context; /** * Create a new {@link ConditionEvaluator} instance. */ public ConditionEvaluator(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) { this.context = new ConditionContextImpl(registry, environment, resourceLoader); } /** * Determine if an item should be skipped based on {@code @Conditional} annotations. * The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a * {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION}) * @param metadata the meta data * @return if the item should be skipped */ public boolean shouldSkip(AnnotatedTypeMetadata metadata) { return shouldSkip(metadata, null); } /** * Determine if an item should be skipped based on {@code @Conditional} annotations. * @param metadata the meta data * @param phase the phase of the call * @return if the item should be skipped */ public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if (requiredPhase == null || requiredPhase == phase) { if (!condition.matches(this.context, metadata)) { return true; } } } return false; } @SuppressWarnings("unchecked") private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) { MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true); Object values = (attributes != null ? attributes.get("value") : null); return (List<String[]>) (values != null ? values : Collections.emptyList()); } private 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 final BeanDefinitionRegistry registry; private final ConfigurableListableBeanFactory beanFactory; private final Environment environment; private final ResourceLoader resourceLoader; public ConditionContextImpl(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) { this.registry = registry; this.beanFactory = deduceBeanFactory(registry); this.environment = (environment != null ? environment : deduceEnvironment(registry)); this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry)); } private ConfigurableListableBeanFactory deduceBeanFactory(BeanDefinitionRegistry source) { if (source instanceof ConfigurableListableBeanFactory) { return (ConfigurableListableBeanFactory) source; } if (source instanceof ConfigurableApplicationContext) { return (((ConfigurableApplicationContext) source).getBeanFactory()); } return null; } private Environment deduceEnvironment(BeanDefinitionRegistry source) { if (source instanceof EnvironmentCapable) { return ((EnvironmentCapable) source).getEnvironment(); } return null; } private ResourceLoader deduceResourceLoader(BeanDefinitionRegistry source) { if (source instanceof ResourceLoader) { return (ResourceLoader) source; } return null; } @Override public BeanDefinitionRegistry getRegistry() { return this.registry; } @Override public ConfigurableListableBeanFactory getBeanFactory() { return this.beanFactory; } @Override public Environment getEnvironment() { return this.environment; } @Override public ResourceLoader getResourceLoader() { return this.resourceLoader; } @Override public ClassLoader getClassLoader() { if (this.resourceLoader != null) { return this.resourceLoader.getClassLoader(); } if (this.beanFactory != null) { return this.beanFactory.getBeanClassLoader(); } return null; } } }