001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.annotation; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.HashSet; 022import java.util.LinkedHashMap; 023import java.util.LinkedHashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031import org.springframework.aop.framework.autoproxy.AutoProxyUtils; 032import org.springframework.beans.PropertyValues; 033import org.springframework.beans.factory.BeanClassLoaderAware; 034import org.springframework.beans.factory.BeanDefinitionStoreException; 035import org.springframework.beans.factory.BeanFactory; 036import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; 037import org.springframework.beans.factory.config.BeanDefinition; 038import org.springframework.beans.factory.config.BeanDefinitionHolder; 039import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 040import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 041import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; 042import org.springframework.beans.factory.config.SingletonBeanRegistry; 043import org.springframework.beans.factory.parsing.FailFastProblemReporter; 044import org.springframework.beans.factory.parsing.PassThroughSourceExtractor; 045import org.springframework.beans.factory.parsing.ProblemReporter; 046import org.springframework.beans.factory.parsing.SourceExtractor; 047import org.springframework.beans.factory.support.AbstractBeanDefinition; 048import org.springframework.beans.factory.support.BeanDefinitionRegistry; 049import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; 050import org.springframework.beans.factory.support.BeanNameGenerator; 051import org.springframework.context.EnvironmentAware; 052import org.springframework.context.ResourceLoaderAware; 053import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration; 054import org.springframework.core.Ordered; 055import org.springframework.core.PriorityOrdered; 056import org.springframework.core.env.Environment; 057import org.springframework.core.env.StandardEnvironment; 058import org.springframework.core.io.DefaultResourceLoader; 059import org.springframework.core.io.ResourceLoader; 060import org.springframework.core.type.AnnotationMetadata; 061import org.springframework.core.type.MethodMetadata; 062import org.springframework.core.type.classreading.CachingMetadataReaderFactory; 063import org.springframework.core.type.classreading.MetadataReaderFactory; 064import org.springframework.lang.Nullable; 065import org.springframework.util.Assert; 066import org.springframework.util.ClassUtils; 067 068/** 069 * {@link BeanFactoryPostProcessor} used for bootstrapping processing of 070 * {@link Configuration @Configuration} classes. 071 * 072 * <p>Registered by default when using {@code <context:annotation-config/>} or 073 * {@code <context:component-scan/>}. Otherwise, may be declared manually as 074 * with any other BeanFactoryPostProcessor. 075 * 076 * <p>This post processor is priority-ordered as it is important that any 077 * {@link Bean} methods declared in {@code @Configuration} classes have 078 * their corresponding bean definitions registered before any other 079 * {@link BeanFactoryPostProcessor} executes. 080 * 081 * @author Chris Beams 082 * @author Juergen Hoeller 083 * @author Phillip Webb 084 * @since 3.0 085 */ 086public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, 087 PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { 088 089 /** 090 * A {@code BeanNameGenerator} using fully qualified class names as default bean names. 091 * <p>This default for configuration-level import purposes may be overridden through 092 * {@link #setBeanNameGenerator}. Note that the default for component scanning purposes 093 * is a plain {@link AnnotationBeanNameGenerator#INSTANCE}, unless overridden through 094 * {@link #setBeanNameGenerator} with a unified user-level bean name generator. 095 * @since 5.2 096 * @see #setBeanNameGenerator 097 */ 098 public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR = 099 new FullyQualifiedAnnotationBeanNameGenerator(); 100 101 private static final String IMPORT_REGISTRY_BEAN_NAME = 102 ConfigurationClassPostProcessor.class.getName() + ".importRegistry"; 103 104 105 private final Log logger = LogFactory.getLog(getClass()); 106 107 private SourceExtractor sourceExtractor = new PassThroughSourceExtractor(); 108 109 private ProblemReporter problemReporter = new FailFastProblemReporter(); 110 111 @Nullable 112 private Environment environment; 113 114 private ResourceLoader resourceLoader = new DefaultResourceLoader(); 115 116 @Nullable 117 private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); 118 119 private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(); 120 121 private boolean setMetadataReaderFactoryCalled = false; 122 123 private final Set<Integer> registriesPostProcessed = new HashSet<>(); 124 125 private final Set<Integer> factoriesPostProcessed = new HashSet<>(); 126 127 @Nullable 128 private ConfigurationClassBeanDefinitionReader reader; 129 130 private boolean localBeanNameGeneratorSet = false; 131 132 /* Using short class names as default bean names by default. */ 133 private BeanNameGenerator componentScanBeanNameGenerator = AnnotationBeanNameGenerator.INSTANCE; 134 135 /* Using fully qualified class names as default bean names by default. */ 136 private BeanNameGenerator importBeanNameGenerator = IMPORT_BEAN_NAME_GENERATOR; 137 138 139 @Override 140 public int getOrder() { 141 return Ordered.LOWEST_PRECEDENCE; // within PriorityOrdered 142 } 143 144 /** 145 * Set the {@link SourceExtractor} to use for generated bean definitions 146 * that correspond to {@link Bean} factory methods. 147 */ 148 public void setSourceExtractor(@Nullable SourceExtractor sourceExtractor) { 149 this.sourceExtractor = (sourceExtractor != null ? sourceExtractor : new PassThroughSourceExtractor()); 150 } 151 152 /** 153 * Set the {@link ProblemReporter} to use. 154 * <p>Used to register any problems detected with {@link Configuration} or {@link Bean} 155 * declarations. For instance, an @Bean method marked as {@code final} is illegal 156 * and would be reported as a problem. Defaults to {@link FailFastProblemReporter}. 157 */ 158 public void setProblemReporter(@Nullable ProblemReporter problemReporter) { 159 this.problemReporter = (problemReporter != null ? problemReporter : new FailFastProblemReporter()); 160 } 161 162 /** 163 * Set the {@link MetadataReaderFactory} to use. 164 * <p>Default is a {@link CachingMetadataReaderFactory} for the specified 165 * {@linkplain #setBeanClassLoader bean class loader}. 166 */ 167 public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) { 168 Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null"); 169 this.metadataReaderFactory = metadataReaderFactory; 170 this.setMetadataReaderFactoryCalled = true; 171 } 172 173 /** 174 * Set the {@link BeanNameGenerator} to be used when triggering component scanning 175 * from {@link Configuration} classes and when registering {@link Import}'ed 176 * configuration classes. The default is a standard {@link AnnotationBeanNameGenerator} 177 * for scanned components (compatible with the default in {@link ClassPathBeanDefinitionScanner}) 178 * and a variant thereof for imported configuration classes (using unique fully-qualified 179 * class names instead of standard component overriding). 180 * <p>Note that this strategy does <em>not</em> apply to {@link Bean} methods. 181 * <p>This setter is typically only appropriate when configuring the post-processor as a 182 * standalone bean definition in XML, e.g. not using the dedicated {@code AnnotationConfig*} 183 * application contexts or the {@code <context:annotation-config>} element. Any bean name 184 * generator specified against the application context will take precedence over any set here. 185 * @since 3.1.1 186 * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator) 187 * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR 188 */ 189 public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { 190 Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null"); 191 this.localBeanNameGeneratorSet = true; 192 this.componentScanBeanNameGenerator = beanNameGenerator; 193 this.importBeanNameGenerator = beanNameGenerator; 194 } 195 196 @Override 197 public void setEnvironment(Environment environment) { 198 Assert.notNull(environment, "Environment must not be null"); 199 this.environment = environment; 200 } 201 202 @Override 203 public void setResourceLoader(ResourceLoader resourceLoader) { 204 Assert.notNull(resourceLoader, "ResourceLoader must not be null"); 205 this.resourceLoader = resourceLoader; 206 if (!this.setMetadataReaderFactoryCalled) { 207 this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); 208 } 209 } 210 211 @Override 212 public void setBeanClassLoader(ClassLoader beanClassLoader) { 213 this.beanClassLoader = beanClassLoader; 214 if (!this.setMetadataReaderFactoryCalled) { 215 this.metadataReaderFactory = new CachingMetadataReaderFactory(beanClassLoader); 216 } 217 } 218 219 220 /** 221 * Derive further bean definitions from the configuration classes in the registry. 222 */ 223 @Override 224 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { 225 int registryId = System.identityHashCode(registry); 226 if (this.registriesPostProcessed.contains(registryId)) { 227 throw new IllegalStateException( 228 "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); 229 } 230 if (this.factoriesPostProcessed.contains(registryId)) { 231 throw new IllegalStateException( 232 "postProcessBeanFactory already called on this post-processor against " + registry); 233 } 234 this.registriesPostProcessed.add(registryId); 235 236 processConfigBeanDefinitions(registry); 237 } 238 239 /** 240 * Prepare the Configuration classes for servicing bean requests at runtime 241 * by replacing them with CGLIB-enhanced subclasses. 242 */ 243 @Override 244 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { 245 int factoryId = System.identityHashCode(beanFactory); 246 if (this.factoriesPostProcessed.contains(factoryId)) { 247 throw new IllegalStateException( 248 "postProcessBeanFactory already called on this post-processor against " + beanFactory); 249 } 250 this.factoriesPostProcessed.add(factoryId); 251 if (!this.registriesPostProcessed.contains(factoryId)) { 252 // BeanDefinitionRegistryPostProcessor hook apparently not supported... 253 // Simply call processConfigurationClasses lazily at this point then. 254 processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); 255 } 256 257 enhanceConfigurationClasses(beanFactory); 258 beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); 259 } 260 261 /** 262 * Build and validate a configuration model based on the registry of 263 * {@link Configuration} classes. 264 */ 265 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { 266 List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); 267 String[] candidateNames = registry.getBeanDefinitionNames(); 268 269 for (String beanName : candidateNames) { 270 BeanDefinition beanDef = registry.getBeanDefinition(beanName); 271 if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { 272 if (logger.isDebugEnabled()) { 273 logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); 274 } 275 } 276 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { 277 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); 278 } 279 } 280 281 // Return immediately if no @Configuration classes were found 282 if (configCandidates.isEmpty()) { 283 return; 284 } 285 286 // Sort by previously determined @Order value, if applicable 287 configCandidates.sort((bd1, bd2) -> { 288 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); 289 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); 290 return Integer.compare(i1, i2); 291 }); 292 293 // Detect any custom bean name generation strategy supplied through the enclosing application context 294 SingletonBeanRegistry sbr = null; 295 if (registry instanceof SingletonBeanRegistry) { 296 sbr = (SingletonBeanRegistry) registry; 297 if (!this.localBeanNameGeneratorSet) { 298 BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( 299 AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); 300 if (generator != null) { 301 this.componentScanBeanNameGenerator = generator; 302 this.importBeanNameGenerator = generator; 303 } 304 } 305 } 306 307 if (this.environment == null) { 308 this.environment = new StandardEnvironment(); 309 } 310 311 // Parse each @Configuration class 312 ConfigurationClassParser parser = new ConfigurationClassParser( 313 this.metadataReaderFactory, this.problemReporter, this.environment, 314 this.resourceLoader, this.componentScanBeanNameGenerator, registry); 315 316 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); 317 Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); 318 do { 319 parser.parse(candidates); 320 parser.validate(); 321 322 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 323 configClasses.removeAll(alreadyParsed); 324 325 // Read the model and create bean definitions based on its content 326 if (this.reader == null) { 327 this.reader = new ConfigurationClassBeanDefinitionReader( 328 registry, this.sourceExtractor, this.resourceLoader, this.environment, 329 this.importBeanNameGenerator, parser.getImportRegistry()); 330 } 331 this.reader.loadBeanDefinitions(configClasses); 332 alreadyParsed.addAll(configClasses); 333 334 candidates.clear(); 335 if (registry.getBeanDefinitionCount() > candidateNames.length) { 336 String[] newCandidateNames = registry.getBeanDefinitionNames(); 337 Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); 338 Set<String> alreadyParsedClasses = new HashSet<>(); 339 for (ConfigurationClass configurationClass : alreadyParsed) { 340 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); 341 } 342 for (String candidateName : newCandidateNames) { 343 if (!oldCandidateNames.contains(candidateName)) { 344 BeanDefinition bd = registry.getBeanDefinition(candidateName); 345 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && 346 !alreadyParsedClasses.contains(bd.getBeanClassName())) { 347 candidates.add(new BeanDefinitionHolder(bd, candidateName)); 348 } 349 } 350 } 351 candidateNames = newCandidateNames; 352 } 353 } 354 while (!candidates.isEmpty()); 355 356 // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes 357 if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { 358 sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); 359 } 360 361 if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { 362 // Clear cache in externally provided MetadataReaderFactory; this is a no-op 363 // for a shared cache since it'll be cleared by the ApplicationContext. 364 ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); 365 } 366 } 367 368 /** 369 * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; 370 * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}. 371 * Candidate status is determined by BeanDefinition attribute metadata. 372 * @see ConfigurationClassEnhancer 373 */ 374 public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { 375 Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>(); 376 for (String beanName : beanFactory.getBeanDefinitionNames()) { 377 BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); 378 Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE); 379 MethodMetadata methodMetadata = null; 380 if (beanDef instanceof AnnotatedBeanDefinition) { 381 methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata(); 382 } 383 if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) { 384 // Configuration class (full or lite) or a configuration-derived @Bean method 385 // -> resolve bean class at this point... 386 AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef; 387 if (!abd.hasBeanClass()) { 388 try { 389 abd.resolveBeanClass(this.beanClassLoader); 390 } 391 catch (Throwable ex) { 392 throw new IllegalStateException( 393 "Cannot load configuration class: " + beanDef.getBeanClassName(), ex); 394 } 395 } 396 } 397 if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) { 398 if (!(beanDef instanceof AbstractBeanDefinition)) { 399 throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + 400 beanName + "' since it is not stored in an AbstractBeanDefinition subclass"); 401 } 402 else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) { 403 logger.info("Cannot enhance @Configuration bean definition '" + beanName + 404 "' since its singleton instance has been created too early. The typical cause " + 405 "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " + 406 "return type: Consider declaring such methods as 'static'."); 407 } 408 configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); 409 } 410 } 411 if (configBeanDefs.isEmpty()) { 412 // nothing to enhance -> return immediately 413 return; 414 } 415 416 ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); 417 for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) { 418 AbstractBeanDefinition beanDef = entry.getValue(); 419 // If a @Configuration class gets proxied, always proxy the target class 420 beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); 421 // Set enhanced subclass of the user-specified bean class 422 Class<?> configClass = beanDef.getBeanClass(); 423 Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); 424 if (configClass != enhancedClass) { 425 if (logger.isTraceEnabled()) { 426 logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " + 427 "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName())); 428 } 429 beanDef.setBeanClass(enhancedClass); 430 } 431 } 432 } 433 434 435 private static class ImportAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter { 436 437 private final BeanFactory beanFactory; 438 439 public ImportAwareBeanPostProcessor(BeanFactory beanFactory) { 440 this.beanFactory = beanFactory; 441 } 442 443 @Override 444 public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) { 445 // Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's 446 // postProcessProperties method attempts to autowire other configuration beans. 447 if (bean instanceof EnhancedConfiguration) { 448 ((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory); 449 } 450 return pvs; 451 } 452 453 @Override 454 public Object postProcessBeforeInitialization(Object bean, String beanName) { 455 if (bean instanceof ImportAware) { 456 ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class); 457 AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName()); 458 if (importingClass != null) { 459 ((ImportAware) bean).setImportMetadata(importingClass); 460 } 461 } 462 return bean; 463 } 464 } 465 466}