View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.context.annotation;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.springframework.beans.BeanUtils;
24  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
25  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
26  import org.springframework.context.ConfigurableApplicationContext;
27  import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
28  import org.springframework.core.annotation.AnnotationAwareOrderComparator;
29  import org.springframework.core.env.Environment;
30  import org.springframework.core.env.EnvironmentCapable;
31  import org.springframework.core.io.ResourceLoader;
32  import org.springframework.core.type.AnnotatedTypeMetadata;
33  import org.springframework.core.type.AnnotationMetadata;
34  import org.springframework.util.ClassUtils;
35  import org.springframework.util.MultiValueMap;
36  
37  /**
38   * Internal class used to evaluate {@link Conditional} annotations.
39   *
40   * @author Phillip Webb
41   * @since 4.0
42   */
43  class ConditionEvaluator {
44  
45  	private final ConditionContextImpl context;
46  
47  
48  	/**
49  	 * Create a new {@link ConditionEvaluator} instance.
50  	 */
51  	public ConditionEvaluator(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
52  		this.context = new ConditionContextImpl(registry, environment, resourceLoader);
53  	}
54  
55  
56  	/**
57  	 * Determine if an item should be skipped based on {@code @Conditional} annotations.
58  	 * The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a
59  	 * {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION})
60  	 * @param metadata the meta data
61  	 * @return if the item should be skipped
62  	 */
63  	public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
64  		return shouldSkip(metadata, null);
65  	}
66  
67  	/**
68  	 * Determine if an item should be skipped based on {@code @Conditional} annotations.
69  	 * @param metadata the meta data
70  	 * @param phase the phase of the call
71  	 * @return if the item should be skipped
72  	 */
73  	public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
74  		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
75  			return false;
76  		}
77  
78  		if (phase == null) {
79  			if (metadata instanceof AnnotationMetadata &&
80  					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
81  				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
82  			}
83  			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
84  		}
85  
86  		List<Condition> conditions = new ArrayList<Condition>();
87  		for (String[] conditionClasses : getConditionClasses(metadata)) {
88  			for (String conditionClass : conditionClasses) {
89  				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
90  				conditions.add(condition);
91  			}
92  		}
93  
94  		AnnotationAwareOrderComparator.sort(conditions);
95  
96  		for (Condition condition : conditions) {
97  			ConfigurationPhase requiredPhase = null;
98  			if (condition instanceof ConfigurationCondition) {
99  				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
100 			}
101 			if (requiredPhase == null || requiredPhase == phase) {
102 				if (!condition.matches(this.context, metadata)) {
103 					return true;
104 				}
105 			}
106 		}
107 
108 		return false;
109 	}
110 
111 	@SuppressWarnings("unchecked")
112 	private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
113 		MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true);
114 		Object values = (attributes != null ? attributes.get("value") : null);
115 		return (List<String[]>) (values != null ? values : Collections.emptyList());
116 	}
117 
118 	private Condition getCondition(String conditionClassName, ClassLoader classloader) {
119 		Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader);
120 		return (Condition) BeanUtils.instantiateClass(conditionClass);
121 	}
122 
123 
124 	/**
125 	 * Implementation of a {@link ConditionContext}.
126 	 */
127 	private static class ConditionContextImpl implements ConditionContext {
128 
129 		private final BeanDefinitionRegistry registry;
130 
131 		private final ConfigurableListableBeanFactory beanFactory;
132 
133 		private final Environment environment;
134 
135 		private final ResourceLoader resourceLoader;
136 
137 		public ConditionContextImpl(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
138 			this.registry = registry;
139 			this.beanFactory = deduceBeanFactory(registry);
140 			this.environment = (environment != null ? environment : deduceEnvironment(registry));
141 			this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
142 		}
143 
144 		private ConfigurableListableBeanFactory deduceBeanFactory(BeanDefinitionRegistry source) {
145 			if (source instanceof ConfigurableListableBeanFactory) {
146 				return (ConfigurableListableBeanFactory) source;
147 			}
148 			if (source instanceof ConfigurableApplicationContext) {
149 				return (((ConfigurableApplicationContext) source).getBeanFactory());
150 			}
151 			return null;
152 		}
153 
154 		private Environment deduceEnvironment(BeanDefinitionRegistry source) {
155 			if (source instanceof EnvironmentCapable) {
156 				return ((EnvironmentCapable) source).getEnvironment();
157 			}
158 			return null;
159 		}
160 
161 		private ResourceLoader deduceResourceLoader(BeanDefinitionRegistry source) {
162 			if (source instanceof ResourceLoader) {
163 				return (ResourceLoader) source;
164 			}
165 			return null;
166 		}
167 
168 		@Override
169 		public BeanDefinitionRegistry getRegistry() {
170 			return this.registry;
171 		}
172 
173 		@Override
174 		public ConfigurableListableBeanFactory getBeanFactory() {
175 			return this.beanFactory;
176 		}
177 
178 		@Override
179 		public Environment getEnvironment() {
180 			return this.environment;
181 		}
182 
183 		@Override
184 		public ResourceLoader getResourceLoader() {
185 			return this.resourceLoader;
186 		}
187 
188 		@Override
189 		public ClassLoader getClassLoader() {
190 			if (this.resourceLoader != null) {
191 				return this.resourceLoader.getClassLoader();
192 			}
193 			if (this.beanFactory != null) {
194 				return this.beanFactory.getBeanClassLoader();
195 			}
196 			return null;
197 		}
198 	}
199 
200 }