美文网首页
Spring Boot 基于注解驱动源码分析--自动配置

Spring Boot 基于注解驱动源码分析--自动配置

作者: 怪咖_OOP | 来源:发表于2018-08-28 18:11 被阅读0次

    [TOC]

    Spring作为Java开发最常用的容器管理框架,使用注解为我们提供很多便捷,下面通过源码分析Spring基于注解驱动自动配置的原理

    首先介绍两个关键类:

    ConfigurationClassPostProcessor

    类源码注释:
    {@link BeanFactoryPostProcessor} used for bootstrapping processing of{@link Configuration @Configuration} classes. 大致说的是用于引导处理 @Configuration类

    类结构图

    image.png

    从结构图中发现它实现了BeanDefinitionRegistryPostProcessor而BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor接口

    BeanFactoryPostProcessor接口是spring底层的一个接口可以在组件初始化之前,修改BeanDefinition并重新注册bean

    BeanDefinitionRegistryPostProcessor它是标准SPI扩展接口,允许在已有的BeanDefinition中进一步注册bean定义

    BeanDefinitionRegistryPostProcessor接口实现方法

    从配置类进一步获取bean定义并注册

    /**
         * Derive further bean definitions from the configuration classes in the registry.
         */
    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);
            //处理配置bean定义
            processConfigBeanDefinitions(registry);
        }
    
    processConfigBeanDefinitions(registry);
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
            
            ......省略检查部分代码
            
            // 逐个解析@Configuration 配置类
            ConfigurationClassParser parser = new ConfigurationClassParser(
                    this.metadataReaderFactory, this.problemReporter, this.environment,
                    this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
            Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
            do {
              //类配置解析器进行解析
                parser.parse(candidates);
                parser.validate();
    
                Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(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());
                }
                //加载bean定义
                this.reader.loadBeanDefinitions(configClasses);
                
                ......
        }
    

    在该方法中实例化ConfigurationClassParser(类配置解析器)

    解析完之后通过ConfigurationClassBeanDefinitionReader的loadBeanDefinitions方法加载bean定义

    关于bean加载完成后怎样进行实例化将在后面作为单独一篇文章分享给大家

    ConfigurationClassParser

    解析@Configuration类定义,解析单个配置类时可以有任意数量的配置类对象,因为当前配置类可能使用@Import注解包含其他配置类

    在上面方法代码中parser.parse(candidates);

    parse(Set<BeanDefinitionHolder> configCandidates)

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
            this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
    
            for (BeanDefinitionHolder holder : configCandidates) {
                BeanDefinition bd = holder.getBeanDefinition();
                try {
                    if (bd instanceof AnnotatedBeanDefinition) {
                        parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                    }
                    else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                        parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
                    }
                    else {
                        parse(bd.getBeanClassName(), holder.getBeanName());
                    }
                }
                catch (BeanDefinitionStoreException ex) {
                    throw ex;
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
                }
            }
    
            processDeferredImportSelectors();
        }
    

    在parse方法中调用了processConfigurationClass(ConfigurationClass configClass)方法

    processConfigurationClass(ConfigurationClass configClass)
    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    
            ......
            
            // 递归处理配置类和超类(父类)
            SourceClass sourceClass = asSourceClass(configClass);
            do {
                sourceClass = doProcessConfigurationClass(configClass, sourceClass);
            }
            while (sourceClass != null);
    
            this.configurationClasses.put(configClass, configClass);
        }
    

    doProcessConfigurationClass(configClass, sourceClass);

    该方法处理了配置类上的@PropertySources、@PropertySource、@ComponentScans、@ComponentScan、@ImportResource注解

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
                throws IOException {
    
            // Recursively process any member (nested) classes first
            processMemberClasses(configClass, sourceClass);
    
            // Process any @PropertySource annotations
            for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                    sourceClass.getMetadata(), PropertySources.class,
                    org.springframework.context.annotation.PropertySource.class)) {
                if (this.environment instanceof ConfigurableEnvironment) {
                    processPropertySource(propertySource);
                }
                else {
                    logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                            "]. Reason: Environment must implement ConfigurableEnvironment");
                }
            }
    
            // Process any @ComponentScan annotations
            Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
            if (!componentScans.isEmpty() &&
                    !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
                for (AnnotationAttributes componentScan : componentScans) {
                    // The config class is annotated with @ComponentScan -> perform the scan immediately
                    Set<BeanDefinitionHolder> scannedBeanDefinitions =
                            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                    // Check the set of scanned definitions for any further config classes and parse recursively if needed
                    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(
                                holder.getBeanDefinition(), this.metadataReaderFactory)) {
                            parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                        }
                    }
                }
            }
    
            // Process any @Import annotations
            processImports(configClass, sourceClass, getImports(sourceClass), true);
    
            // Process any @ImportResource annotations
            if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
                AnnotationAttributes importResource =
                        AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
                String[] resources = importResource.getStringArray("locations");
                Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
                for (String resource : resources) {
                    String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                    configClass.addImportedResource(resolvedResource, readerClass);
                }
            }
    
            // Process individual @Bean methods
            Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
            for (MethodMetadata methodMetadata : beanMethods) {
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
    
            // Process default methods on interfaces
            processInterfaces(configClass, sourceClass);
    
            // Process superclass, if any
            if (sourceClass.getMetadata().hasSuperClass()) {
                String superclass = sourceClass.getMetadata().getSuperClassName();
                if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
                    this.knownSuperclasses.put(superclass, configClass);
                    // Superclass found, return its annotation metadata and recurse
                    return sourceClass.getSuperClass();
                }
            }
    
            // No superclass -> processing is complete
            return null;
        }
    

    关于方法中如何处理各个注解并进行bean定义,感兴趣的朋友可以点击对应方法,自行阅读!

    以上属于原创文章,转载请注明作者@怪咖
    QQ:208275451

    相关文章

      网友评论

          本文标题:Spring Boot 基于注解驱动源码分析--自动配置

          本文链接:https://www.haomeiwen.com/subject/zottiftx.html