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.beans.PropertyDescriptor;
20  import java.util.Arrays;
21  import java.util.HashSet;
22  import java.util.LinkedHashMap;
23  import java.util.LinkedHashSet;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
31  import org.springframework.beans.PropertyValues;
32  import org.springframework.beans.factory.BeanClassLoaderAware;
33  import org.springframework.beans.factory.BeanDefinitionStoreException;
34  import org.springframework.beans.factory.BeanFactory;
35  import org.springframework.beans.factory.BeanFactoryAware;
36  import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
37  import org.springframework.beans.factory.config.BeanDefinition;
38  import org.springframework.beans.factory.config.BeanDefinitionHolder;
39  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
40  import org.springframework.beans.factory.config.BeanPostProcessor;
41  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
42  import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
43  import org.springframework.beans.factory.config.SingletonBeanRegistry;
44  import org.springframework.beans.factory.parsing.FailFastProblemReporter;
45  import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
46  import org.springframework.beans.factory.parsing.ProblemReporter;
47  import org.springframework.beans.factory.parsing.SourceExtractor;
48  import org.springframework.beans.factory.support.AbstractBeanDefinition;
49  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
50  import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
51  import org.springframework.beans.factory.support.BeanNameGenerator;
52  import org.springframework.beans.factory.support.RootBeanDefinition;
53  import org.springframework.context.EnvironmentAware;
54  import org.springframework.context.ResourceLoaderAware;
55  import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
56  import org.springframework.core.Ordered;
57  import org.springframework.core.PriorityOrdered;
58  import org.springframework.core.env.Environment;
59  import org.springframework.core.io.DefaultResourceLoader;
60  import org.springframework.core.io.ResourceLoader;
61  import org.springframework.core.type.AnnotationMetadata;
62  import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
63  import org.springframework.core.type.classreading.MetadataReaderFactory;
64  import org.springframework.util.Assert;
65  import org.springframework.util.ClassUtils;
66  
67  import static org.springframework.context.annotation.AnnotationConfigUtils.*;
68  
69  /**
70   * {@link BeanFactoryPostProcessor} used for bootstrapping processing of
71   * {@link Configuration @Configuration} classes.
72   *
73   * <p>Registered by default when using {@code <context:annotation-config/>} or
74   * {@code <context:component-scan/>}. Otherwise, may be declared manually as
75   * with any other BeanFactoryPostProcessor.
76   *
77   * <p>This post processor is {@link Ordered#HIGHEST_PRECEDENCE} as it is important
78   * that any {@link Bean} methods declared in Configuration classes have their
79   * respective bean definitions registered before any other BeanFactoryPostProcessor
80   * executes.
81   *
82   * @author Chris Beams
83   * @author Juergen Hoeller
84   * @author Phillip Webb
85   * @since 3.0
86   */
87  public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
88  		PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
89  
90  	private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME =
91  			ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor";
92  
93  	private static final String IMPORT_REGISTRY_BEAN_NAME =
94  			ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
95  
96  	private static final String ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME =
97  			ConfigurationClassPostProcessor.class.getName() + ".enhancedConfigurationProcessor";
98  
99  
100 	private final Log logger = LogFactory.getLog(getClass());
101 
102 	private SourceExtractor sourceExtractor = new PassThroughSourceExtractor();
103 
104 	private ProblemReporter problemReporter = new FailFastProblemReporter();
105 
106 	private Environment environment;
107 
108 	private ResourceLoader resourceLoader = new DefaultResourceLoader();
109 
110 	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
111 
112 	private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
113 
114 	private boolean setMetadataReaderFactoryCalled = false;
115 
116 	private final Set<Integer> registriesPostProcessed = new HashSet<Integer>();
117 
118 	private final Set<Integer> factoriesPostProcessed = new HashSet<Integer>();
119 
120 	private ConfigurationClassBeanDefinitionReader reader;
121 
122 	private boolean localBeanNameGeneratorSet = false;
123 
124 	/* using short class names as default bean names */
125 	private BeanNameGenerator componentScanBeanNameGenerator = new AnnotationBeanNameGenerator();
126 
127 	/* using fully qualified class names as default bean names */
128 	private BeanNameGenerator importBeanNameGenerator = new AnnotationBeanNameGenerator() {
129 		@Override
130 		protected String buildDefaultBeanName(BeanDefinition definition) {
131 			return definition.getBeanClassName();
132 		}
133 	};
134 
135 
136 	@Override
137 	public int getOrder() {
138 		return Ordered.LOWEST_PRECEDENCE;  // within PriorityOrdered
139 	}
140 
141 	/**
142 	 * Set the {@link SourceExtractor} to use for generated bean definitions
143 	 * that correspond to {@link Bean} factory methods.
144 	 */
145 	public void setSourceExtractor(SourceExtractor sourceExtractor) {
146 		this.sourceExtractor = (sourceExtractor != null ? sourceExtractor : new PassThroughSourceExtractor());
147 	}
148 
149 	/**
150 	 * Set the {@link ProblemReporter} to use.
151 	 * <p>Used to register any problems detected with {@link Configuration} or {@link Bean}
152 	 * declarations. For instance, an @Bean method marked as {@code final} is illegal
153 	 * and would be reported as a problem. Defaults to {@link FailFastProblemReporter}.
154 	 */
155 	public void setProblemReporter(ProblemReporter problemReporter) {
156 		this.problemReporter = (problemReporter != null ? problemReporter : new FailFastProblemReporter());
157 	}
158 
159 	/**
160 	 * Set the {@link MetadataReaderFactory} to use.
161 	 * <p>Default is a {@link CachingMetadataReaderFactory} for the specified
162 	 * {@linkplain #setBeanClassLoader bean class loader}.
163 	 */
164 	public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) {
165 		Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
166 		this.metadataReaderFactory = metadataReaderFactory;
167 		this.setMetadataReaderFactoryCalled = true;
168 	}
169 
170 	/**
171 	 * Set the {@link BeanNameGenerator} to be used when triggering component scanning
172 	 * from {@link Configuration} classes and when registering {@link Import}'ed
173 	 * configuration classes. The default is a standard {@link AnnotationBeanNameGenerator}
174 	 * for scanned components (compatible with the default in {@link ClassPathBeanDefinitionScanner})
175 	 * and a variant thereof for imported configuration classes (using unique fully-qualified
176 	 * class names instead of standard component overriding).
177 	 * <p>Note that this strategy does <em>not</em> apply to {@link Bean} methods.
178 	 * <p>This setter is typically only appropriate when configuring the post-processor as
179 	 * a standalone bean definition in XML, e.g. not using the dedicated
180 	 * {@code AnnotationConfig*} application contexts or the {@code
181 	 * <context:annotation-config>} element. Any bean name generator specified against
182 	 * the application context will take precedence over any value set here.
183 	 * @since 3.1.1
184 	 * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
185 	 * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
186 	 */
187 	public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
188 		Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null");
189 		this.localBeanNameGeneratorSet = true;
190 		this.componentScanBeanNameGenerator = beanNameGenerator;
191 		this.importBeanNameGenerator = beanNameGenerator;
192 	}
193 
194 	@Override
195 	public void setEnvironment(Environment environment) {
196 		Assert.notNull(environment, "Environment must not be null");
197 		this.environment = environment;
198 	}
199 
200 	@Override
201 	public void setResourceLoader(ResourceLoader resourceLoader) {
202 		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
203 		this.resourceLoader = resourceLoader;
204 	}
205 
206 	@Override
207 	public void setBeanClassLoader(ClassLoader beanClassLoader) {
208 		this.beanClassLoader = beanClassLoader;
209 		if (!this.setMetadataReaderFactoryCalled) {
210 			this.metadataReaderFactory = new CachingMetadataReaderFactory(beanClassLoader);
211 		}
212 	}
213 
214 
215 	/**
216 	 * Derive further bean definitions from the configuration classes in the registry.
217 	 */
218 	@Override
219 	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
220 		RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
221 		iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
222 		registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
223 
224 		RootBeanDefinition ecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);
225 		ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
226 		registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME, ecbpp);
227 
228 		int registryId = System.identityHashCode(registry);
229 		if (this.registriesPostProcessed.contains(registryId)) {
230 			throw new IllegalStateException(
231 					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
232 		}
233 		if (this.factoriesPostProcessed.contains(registryId)) {
234 			throw new IllegalStateException(
235 					"postProcessBeanFactory already called on this post-processor against " + registry);
236 		}
237 		this.registriesPostProcessed.add(registryId);
238 
239 		processConfigBeanDefinitions(registry);
240 	}
241 
242 	/**
243 	 * Prepare the Configuration classes for servicing bean requests at runtime
244 	 * by replacing them with CGLIB-enhanced subclasses.
245 	 */
246 	@Override
247 	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
248 		int factoryId = System.identityHashCode(beanFactory);
249 		if (this.factoriesPostProcessed.contains(factoryId)) {
250 			throw new IllegalStateException(
251 					"postProcessBeanFactory already called on this post-processor against " + beanFactory);
252 		}
253 		this.factoriesPostProcessed.add(factoryId);
254 		if (!this.registriesPostProcessed.contains(factoryId)) {
255 			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
256 			// Simply call processConfigurationClasses lazily at this point then.
257 			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
258 		}
259 		enhanceConfigurationClasses(beanFactory);
260 	}
261 
262 	/**
263 	 * Build and validate a configuration model based on the registry of
264 	 * {@link Configuration} classes.
265 	 */
266 	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
267 		Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
268 		String[] candidateNames = registry.getBeanDefinitionNames();
269 
270 		for (String beanName : candidateNames) {
271 			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
272 			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
273 					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
274 				if (logger.isDebugEnabled()) {
275 					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
276 				}
277 			}
278 			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
279 				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
280 			}
281 		}
282 
283 		// Return immediately if no @Configuration classes were found
284 		if (configCandidates.isEmpty()) {
285 			return;
286 		}
287 
288 		// Detect any custom bean name generation strategy supplied through the enclosing application context
289 		SingletonBeanRegistry singletonRegistry = null;
290 		if (registry instanceof SingletonBeanRegistry) {
291 			singletonRegistry = (SingletonBeanRegistry) registry;
292 			if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
293 				BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
294 				this.componentScanBeanNameGenerator = generator;
295 				this.importBeanNameGenerator = generator;
296 			}
297 		}
298 
299 		// Parse each @Configuration class
300 		ConfigurationClassParser parser = new ConfigurationClassParser(
301 				this.metadataReaderFactory, this.problemReporter, this.environment,
302 				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
303 
304 		Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
305 		do {
306 			parser.parse(configCandidates);
307 			parser.validate();
308 
309 			Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
310 			configClasses.removeAll(alreadyParsed);
311 
312 			// Read the model and create bean definitions based on its content
313 			if (this.reader == null) {
314 				this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor,
315 						this.problemReporter, this.metadataReaderFactory, this.resourceLoader, this.environment,
316 						this.importBeanNameGenerator, parser.getImportRegistry());
317 			}
318 			this.reader.loadBeanDefinitions(configClasses);
319 			alreadyParsed.addAll(configClasses);
320 
321 			configCandidates.clear();
322 			if (registry.getBeanDefinitionCount() > candidateNames.length) {
323 				String[] newCandidateNames = registry.getBeanDefinitionNames();
324 				Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
325 				Set<String> alreadyParsedClasses = new HashSet<String>();
326 				for (ConfigurationClass configurationClass : alreadyParsed) {
327 					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
328 				}
329 				for (String candidateName : newCandidateNames) {
330 					if (!oldCandidateNames.contains(candidateName)) {
331 						BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
332 						if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
333 								!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
334 							configCandidates.add(new BeanDefinitionHolder(beanDef, candidateName));
335 						}
336 					}
337 				}
338 				candidateNames = newCandidateNames;
339 			}
340 		}
341 		while (!configCandidates.isEmpty());
342 
343 		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
344 		if (singletonRegistry != null) {
345 			if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
346 				singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
347 			}
348 		}
349 
350 		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
351 			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
352 		}
353 	}
354 
355 	/**
356 	 * Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
357 	 * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
358 	 * Candidate status is determined by BeanDefinition attribute metadata.
359 	 * @see ConfigurationClassEnhancer
360 	 */
361 	public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
362 		Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();
363 		for (String beanName : beanFactory.getBeanDefinitionNames()) {
364 			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
365 			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
366 				if (!(beanDef instanceof AbstractBeanDefinition)) {
367 					throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
368 							beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
369 				}
370 				configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
371 			}
372 		}
373 		if (configBeanDefs.isEmpty()) {
374 			// nothing to enhance -> return immediately
375 			return;
376 		}
377 		ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
378 		for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
379 			AbstractBeanDefinition beanDef = entry.getValue();
380 			// If a @Configuration class gets proxied, always proxy the target class
381 			beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
382 			try {
383 				// Set enhanced subclass of the user-specified bean class
384 				Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
385 				Class<?> enhancedClass = enhancer.enhance(configClass);
386 				if (configClass != enhancedClass) {
387 					if (logger.isDebugEnabled()) {
388 						logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' " +
389 								"with enhanced class name '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
390 					}
391 					beanDef.setBeanClass(enhancedClass);
392 				}
393 			}
394 			catch (Throwable ex) {
395 				throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
396 			}
397 		}
398 	}
399 
400 
401 	private static class ImportAwareBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, PriorityOrdered {
402 
403 		private BeanFactory beanFactory;
404 
405 		@Override
406 		public void setBeanFactory(BeanFactory beanFactory) {
407 			this.beanFactory = beanFactory;
408 		}
409 
410 		@Override
411 		public int getOrder() {
412 			return Ordered.HIGHEST_PRECEDENCE;
413 		}
414 
415 		@Override
416 		public Object postProcessBeforeInitialization(Object bean, String beanName)  {
417 			if (bean instanceof ImportAware) {
418 				ImportRegistry importRegistry = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
419 				AnnotationMetadata importingClass = importRegistry.getImportingClassFor(bean.getClass().getSuperclass().getName());
420 				if (importingClass != null) {
421 					((ImportAware) bean).setImportMetadata(importingClass);
422 				}
423 			}
424 			return bean;
425 		}
426 
427 		@Override
428 		public Object postProcessAfterInitialization(Object bean, String beanName) {
429 			return bean;
430 		}
431 	}
432 
433 
434 	/**
435 	 * {@link InstantiationAwareBeanPostProcessorAdapter} that ensures
436 	 * {@link EnhancedConfiguration} beans are injected with the {@link BeanFactory}
437 	 * before the {@link AutowiredAnnotationBeanPostProcessor} runs (SPR-10668).
438 	 */
439 	private static class EnhancedConfigurationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
440 			implements PriorityOrdered, BeanFactoryAware {
441 
442 		private BeanFactory beanFactory;
443 
444 		@Override
445 		public int getOrder() {
446 			return Ordered.HIGHEST_PRECEDENCE;
447 		}
448 
449 		@Override
450 		public void setBeanFactory(BeanFactory beanFactory) {
451 			this.beanFactory = beanFactory;
452 		}
453 
454 		@Override
455 		public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
456 			// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
457 			// postProcessPropertyValues method attempts to auto-wire other configuration beans.
458 			if (bean instanceof EnhancedConfiguration) {
459 				((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
460 			}
461 			return pvs;
462 		}
463 	}
464 
465 }