All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.context.annotation.BusinessClassPostProcessor Maven / Gradle / Ivy

There is a newer version: 0.2.4
Show newest version
package org.springframework.context.annotation;

import com.seven.cow.beans.spring.boot.starter.properties.BeansProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.parsing.SourceExtractor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.Conventions;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.util.*;

import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;

public class BusinessClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
        PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

    private final Logger logger = LoggerFactory.getLogger(BusinessClassPostProcessor.class);

    private static final String IMPORT_REGISTRY_BEAN_NAME =
            BusinessClassPostProcessor.class.getName() + ".importRegistry";

    private SourceExtractor sourceExtractor = new PassThroughSourceExtractor();

    private ProblemReporter problemReporter = new FailFastProblemReporter();

    @Nullable
    private Environment environment;

    private ResourceLoader resourceLoader = new DefaultResourceLoader();

    @Nullable
    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();

    private boolean setMetadataReaderFactoryCalled = false;

    private final Set registriesPostProcessed = new HashSet<>();

    private final Set factoriesPostProcessed = new HashSet<>();

    @Nullable
    private ConfigurationClassBeanDefinitionReader reader;

    private boolean localBeanNameGeneratorSet = false;

    /* Using short class names as default bean names */
    private BeanNameGenerator componentScanBeanNameGenerator = new AnnotationBeanNameGenerator();

    private BeansProperties beansProperties;

    private String basePackage;

    public BeansProperties getBeansProperties() {
        return beansProperties;
    }

    public void setBeansProperties(BeansProperties beansProperties) {
        this.beansProperties = beansProperties;
    }

    public String getBasePackage() {
        return basePackage;
    }

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    /* Using fully qualified class names as default bean names */
    private BeanNameGenerator importBeanNameGenerator = new AnnotationBeanNameGenerator() {
        @Override
        protected String buildDefaultBeanName(BeanDefinition definition) {
            String beanClassName = definition.getBeanClassName();
            Assert.state(beanClassName != null, "No bean class name set");
            return beanClassName;
        }
    };


    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;  // within PriorityOrdered
    }

    /**
     * Set the {@link SourceExtractor} to use for generated bean definitions
     * that correspond to {@link Bean} factory methods.
     */
    public void setSourceExtractor(@Nullable SourceExtractor sourceExtractor) {
        this.sourceExtractor = (sourceExtractor != null ? sourceExtractor : new PassThroughSourceExtractor());
    }

    /**
     * Set the {@link ProblemReporter} to use.
     * 

Used to register any problems detected with {@link Configuration} or {@link Bean} * declarations. For instance, an @Bean method marked as {@code final} is illegal * and would be reported as a problem. Defaults to {@link FailFastProblemReporter}. */ public void setProblemReporter(@Nullable ProblemReporter problemReporter) { this.problemReporter = (problemReporter != null ? problemReporter : new FailFastProblemReporter()); } /** * Set the {@link MetadataReaderFactory} to use. *

Default is a {@link CachingMetadataReaderFactory} for the specified * {@linkplain #setBeanClassLoader bean class loader}. */ public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) { Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null"); this.metadataReaderFactory = metadataReaderFactory; this.setMetadataReaderFactoryCalled = true; } /** * Set the {@link BeanNameGenerator} to be used when triggering component scanning * from {@link Configuration} classes and when registering {@link Import}'ed * configuration classes. The default is a standard {@link AnnotationBeanNameGenerator} * for scanned components (compatible with the default in {@link ClassPathBeanDefinitionScanner}) * and a variant thereof for imported configuration classes (using unique fully-qualified * class names instead of standard component overriding). *

Note that this strategy does not apply to {@link Bean} methods. *

This setter is typically only appropriate when configuring the post-processor as * a standalone bean definition in XML, e.g. not using the dedicated * {@code AnnotationConfig*} application contexts or the {@code * } element. Any bean name generator specified against * the application context will take precedence over any value set here. * * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator) * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR * @since 3.1.1 */ public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null"); this.localBeanNameGeneratorSet = true; this.componentScanBeanNameGenerator = beanNameGenerator; this.importBeanNameGenerator = beanNameGenerator; } @Override public void setEnvironment(Environment environment) { Assert.notNull(environment, "Environment must not be null"); this.environment = environment; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { Assert.notNull(resourceLoader, "ResourceLoader must not be null"); this.resourceLoader = resourceLoader; if (!this.setMetadataReaderFactoryCalled) { this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); } } @Override public void setBeanClassLoader(ClassLoader beanClassLoader) { this.beanClassLoader = beanClassLoader; if (!this.setMetadataReaderFactoryCalled) { this.metadataReaderFactory = new CachingMetadataReaderFactory(beanClassLoader); } } /** * Derive further bean definitions from the configuration classes in the registry. */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); processConfigBeanDefinitions(registry); } public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class BusinessConfigurationClassParser parser = new BusinessConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry, beansProperties, basePackage); Set candidates = new LinkedHashSet<>(configCandidates); Set alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null) { if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } } /** * Prepare the Configuration classes for servicing bean requests at runtime * by replacing them with CGLIB-enhanced subclasses. */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); if (this.factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); if (!this.registriesPostProcessed.contains(factoryId)) { // BeanDefinitionRegistryPostProcessor hook apparently not supported... // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(new BusinessClassPostProcessor.ImportAwareBeanPostProcessor(beanFactory)); } private static final String CONFIGURATION_CLASS_FULL = "full"; private static final String CONFIGURATION_CLASS_ATTRIBUTE = Conventions.getQualifiedAttributeName(BusinessClassPostProcessor.class, "configurationClass"); private static boolean isFullConfigurationClass(BeanDefinition beanDef) { return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE)); } /** * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}. * Candidate status is determined by BeanDefinition attribute metadata. * * @see ConfigurationClassEnhancer */ public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { Map configBeanDefs = new LinkedHashMap<>(); for (String beanName : beanFactory.getBeanDefinitionNames()) { BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); if (isFullConfigurationClass(beanDef)) { if (!(beanDef instanceof AbstractBeanDefinition)) { throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + beanName + "' since it is not stored in an AbstractBeanDefinition subclass"); } else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) { logger.warn("Cannot enhance @Configuration bean definition '" + beanName + "' since its singleton instance has been created too early. The typical cause " + "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " + "return type: Consider declaring such methods as 'static'."); } configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } if (configBeanDefs.isEmpty()) { // nothing to enhance -> return immediately return; } ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); for (Map.Entry entry : configBeanDefs.entrySet()) { AbstractBeanDefinition beanDef = entry.getValue(); // If a @Configuration class gets proxied, always proxy the target class beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); try { // Set enhanced subclass of the user-specified bean class Class configClass = beanDef.resolveBeanClass(this.beanClassLoader); if (configClass != null) { Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); if (configClass != enhancedClass) { if (logger.isDebugEnabled()) { logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " + "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName())); } beanDef.setBeanClass(enhancedClass); } } } catch (Throwable ex) { throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex); } } } private static class ImportAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter { private final BeanFactory beanFactory; public ImportAwareBeanPostProcessor(BeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { // Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's // postProcessPropertyValues method attempts to autowire other configuration beans. if (bean instanceof ConfigurationClassEnhancer.EnhancedConfiguration) { ((ConfigurationClassEnhancer.EnhancedConfiguration) bean).setBeanFactory(this.beanFactory); } return pvs; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if (bean instanceof ImportAware) { ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class); AnnotationMetadata importingClass = ir.getImportingClassFor(bean.getClass().getSuperclass().getName()); if (importingClass != null) { ((ImportAware) bean).setImportMetadata(importingClass); } } return bean; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy