zoukankan      html  css  js  c++  java
  • SpringBoot自动装配原理

      网上很多文章对于自动状态都是说的很含糊,而且是千篇一律。很多都是这个注解@EnableAutoConfiguration就自动完成了。怎么可能呢?注解永远都是死的,是需要某个代码去解析处理的

      我今天就跟了下代码看看怎么回事

    一  Configuration

      @Configuration这个注解从Spring3就有了,可不是一个新东西。他的作用就是解放配置文件。

      我们可以看到 

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {
    }

      就是一个@Configuration。

      再看看 EnableAutoConfiguration,里面包含了一个 @Import

    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration 

    二  源码跟踪

      我们要分析的代码入口在  ConfigurationClassPostProcessor 。确切的说是  ConfigurationClassParser.parse 

      这里说一下 ConfigurationClassPostProcessor 

      public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor

    public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
        void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
    }

      也就是说作为 BeanDefinitionRegistryPostProcessor 的实现类,ConfigurationClassPostProcessor需要实现接口方法,而该接口方法的意义就是完成BeanDefinitioin的注册

      下面再来看ConfigurationClassPostProcessor被调用的地方

      大名鼎鼎的AbstractApplicationContext中的refresh方法

    public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//这个方法里会把springboot的启动类也就是有@SpringBootApplication这个注解的类解析成BeanDefinitionHolder
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
            PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    
            // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
            // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
            if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
                beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
                beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
            }
        }
    class PostProcessorRegistrationDelegate {
    
        public static void invokeBeanFactoryPostProcessors(
                ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    
            // Invoke BeanDefinitionRegistryPostProcessors first, if any.
            Set<String> processedBeans = new HashSet<>();
    
            if (beanFactory instanceof BeanDefinitionRegistry) {
                BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
                List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
                List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
    
                for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                    if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {//就在这里
                        BeanDefinitionRegistryPostProcessor registryProcessor =
                                (BeanDefinitionRegistryPostProcessor) postProcessor;
                        registryProcessor.postProcessBeanDefinitionRegistry(registry);
                        registryProcessors.add(registryProcessor);
                    }
                    else {
                        regularPostProcessors.add(postProcessor);
                    }
                }
    public void parse(Set<BeanDefinitionHolder> configCandidates) {
            this.deferredImportSelectors = new LinkedList<>();
    
            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();//对AutoConfigurationImportSelector的解析在这里
        }

      上面的import只是提了一嘴,现在还没有走到那里,还是继续看怎么处理Configration这个注解的

      继续往里面看 还是在  ConfigurationClassParser # processConfigurationClass

    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) {
                        BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                        if (bdCand == null) {
                            bdCand = holder.getBeanDefinition();
                        }
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                            parse(bdCand.getBeanClassName(), holder.getBeanName());
                        }
                    }
                }
            }
    
            // Process any @Import annotations
            processImports(configClass, sourceClass, getImports(sourceClass), true);

      注意绿色的注解,processImports专门负责解析 @Import,其内部逻辑是把每一个@Import 注解的类都加载到  deferredImportSelectors

      ConfigurationClassParser成员变量 

    @Nullable
    
      private List<DeferredImportSelectorHolder> deferredImportSelectors;

      收集Import有必要说下,因为我一开始走了弯路,我一直在找 @EnableAutoConfiguration 这个注解的处理类,但是我想错了,其实这个类不需要解析。而是解析他里面的Import注解就行了。

    比如说我们公司的启动类是这样的

    @EnableLionConfig
    @SpringBootApplication(
        excludeName = {"${exclude.auto-configuration.name}"},
        scanBasePackages = {"${scan.basePkg.path}", "com.ymm.trade", "com.ymm.architecture.*", "com.ymm.unify.authorization.*", "com.ymm.framework.healthcheck.client.*"}
    )
    @ServletComponentScan
    @PropertySource(
        value = {"classpath:default.properties", "classpath:application.properties"},
        factory = TradePropertySourceFactory.class
    )
    @ImportResource(
        locations = {"${custom.resource.import:classpath*:spring/spring-*.xml}", "classpath*:authorization/pigeon-service.xml"},
        reader = TradeImportResourceReader.class
    )
    public class TradeBootStrap {

       那么跟代码的结果就是 ConfigurationClassParser # collectImports

    private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
                throws IOException {
    
            if (visited.add(sourceClass)) {
                for (SourceClass annotation : sourceClass.getAnnotations()) {//第一次是@EnableLionConfig
                    String annName = annotation.getMetadata().getClassName();
                    if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
                        collectImports(annotation, imports, visited);//递归解析
                    }
                }
                imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
            }
        }

      这里解释下 @EnableLionConfig 张这个样子

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Import(LionConfigRegister.class)
    public @interface EnableLionConfig {
    
    }

      直接跳到  ConfigurationClassParser # processDeferredImportSelectors

    private void processDeferredImportSelectors() {
            List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;//见截图
            this.deferredImportSelectors = null;
            if (deferredImports == null) {
                return;
            }
    
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
            Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
            for (DeferredImportSelectorHolder deferredImport : deferredImports) {
                Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
                DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
                        (group == null ? deferredImport : group),
                        (key) -> new DeferredImportSelectorGrouping(createGroup(group)));
                grouping.add(deferredImport);
                configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
                        deferredImport.getConfigurationClass());
            }
            for (DeferredImportSelectorGrouping grouping : groupings.values()) {
                grouping.getImports().forEach((entry) -> { grouping.getImports()方法就会调用AutoConfigurationImportSelector.selectImports
                    ConfigurationClass configurationClass = configurationClasses.get(
                            entry.getMetadata());
                    try {
                        processImports(configurationClass, asSourceClass(configurationClass),
                                asSourceClasses(entry.getImportClassName()), false);
                    }

          图1

      继续看processImports

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    
            省略....
                try {
                    for (SourceClass candidate : importCandidates) {
                        if (candidate.isAssignable(ImportSelector.class)) {//import注解内参数的一种情况,importSelector
                            // Candidate class is an ImportSelector -> delegate to it to determine imports
                            Class<?> candidateClass = candidate.loadClass();
                            ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                            ParserStrategyUtils.invokeAwareMethods(
                                    selector, this.environment, this.resourceLoader, this.registry);
                            if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                                this.deferredImportSelectors.add(
                                        new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                            }
                            else {
                                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                                processImports(configClass, currentSourceClass, importSourceClasses, false);
                            }
                        }
                        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {//另一种情况ImportBeanDefinitionRegistrar
                            // Candidate class is an ImportBeanDefinitionRegistrar ->
                            // delegate to it to register additional bean definitions
                            Class<?> candidateClass = candidate.loadClass();
                            ImportBeanDefinitionRegistrar registrar =
                                    BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                            ParserStrategyUtils.invokeAwareMethods(
                                    registrar, this.environment, this.resourceLoader, this.registry);
                            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                        }
                        else {
                            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                            // process it as an @Configuration class
                            this.importStack.registerImport(
                                    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                            processConfigurationClass(candidate.asConfigClass(configClass));
                        }
                    }
                }
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
            List<String> configurations = getCandidateConfigurations(annotationMetadata,
                    attributes);//getCandidateConfigurations方法就是去扫描spring.factories下面的KV键值对,把每个要加载的Configuration都加载进来
            configurations = removeDuplicates(configurations);
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = filter(configurations, autoConfigurationMetadata);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
        }

                     图2

     

      在这里有两个对象一个是我们写main方法所在的bean就是用@SpringBootApplication注解的bean,另一个就是AutoConfigurationImportSelector

      所以自动装配发生作用的地点是在 ConfigurationClassPostProcessor 这个BeanFactoryPostProcessor对于注解@Configuration的处理中完成的

      

  • 相关阅读:
    Android应用四大组件和应用程序的生命周期
    DOCTYPE html PUBLIC 指定了 HTML 文档遵循的文档类型定义
    Web前端灰框技术
    用JS实现在页面关闭或刷新时触发特定的事件
    style.display的全部属性值
    Android SDK更新的问题
    页面灰框模式精彩实现
    解决sql 语句中truncate语句不支持变量的问题
    获取网页中图片链接的路径的正则表达式
    web页面自动保存本页面的内容到本地
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14132352.html
Copyright © 2011-2022 走看看