• 【Spring Boot源码分析】@EnableAutoConfiguration注解(一)@AutoConfigurationImportSelector注解的处理


    一、概述

    @EnableAutoConfiguration注解是Spring Boot中配置自动装载的总开关。本文将从@EnableAutoConfiguration入手,尝试通过源码分析增强对Spring Boot的理解。
     
    所用版本:Spring Boot 2.2.0.M5 + Spring Framework 5.2.0.RC1
     

    1. 功能的实现:(Spring Boot部分)

    boot.autoconfigure.EnableAutoConfiguration注解
      -> @Import了一个AutoConfigurationImportSelector实例
      -> AutoConfigurationImportSelector类(implement ImportSelector),实现了selectImports() 方法,用来筛选被@Import的Configuration类(减去exclude等)

    2. 接口的调度:(Spring部分)

    context.annotation.ConfigurationClassParser类的parse() 方法
        -> 调用对应不同BeanDefinition类型的parse() 方法
        |   -> 调用context.annotation.ConfigurationClassParser.doProcessConfigurationClass()方法处理ConfigurationClass
        |       -> 调用processImports()方法来处理所有@Import注解
        |           -> 遍历每个@Import标签,生成被注入的ImportSelector子类的实例
        |               -> 对于普通ImportSelector,调用其selectImport()方法,筛掉exclude的,再嵌套processImports(),对需要被@Import的类的@Import注解进行处理
        |               -> 对于DefferedImportSelector,只加入deferredImportSelectors列表中
        -> 对defferedImportImportSelectors调用相应handler的process()方法进行处理
            -> 对DefferedImportImportSelector调用processImports()

    3. 接口在框架中的位置:(其中一条路径,由顶向下)

    【Spring Boot部分】
    boot.SpringApplication.main() 或 ApplicationStarter.main()
    boot.SpringApplication.run()
    boot.SpringApplication.refreshContext()
    boot.SpringApplication.refresh()
    boot.web.servlet.context.ServletWebServerApplicationContext.refresh()
    【Spring部分】
    context.support.AbstractApplicationContext.refresh()
    context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors()
    context.support.PostPreprocessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
    context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory()
    context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions()
    context.annotation.ConfigurationClassParser.parse()  (正是上一小结所述接口)

     

    二、源码细节

    (SpringBoot) boot.autoconfigure.EnableAutoConfiguration注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class) // 载入selector,识别AutoConfigutaion类并import
    public @interface EnableAutoConfiguration {
    	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    	// @interface的参数,以方法形式声明
    	Class<?>[] exclude() default {};
    	String[] excludeName() default {};
    }
    
     
    @EnableAutoConfiguration注解有两个参数和一个属性变量。
    注入了一个AutoConfigurationImportSelector类实例,看起来应该是用于筛选AutoConfiguration的Import的。
    @AutoConfigurationPackage待以后另行分析。

    (SpringBoot) boot.autoconfigure.AutoConfigurationImportSelector类

        public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
    		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
        //......
        @Override
    	public String[] selectImports(AnnotationMetadata annotationMetadata) {
    		// 如果AutoConfiguration没开,返回{}
    		if (!isEnabled(annotationMetadata)) {
    			return NO_IMPORTS;
    		}
    		// 将spring-autoconfigure-metadata.properties的键值对配置载入到PropertiesAutoConfigurationMetadata对象中并返回
    		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    				.loadMetadata(this.beanClassLoader);
            // 基于各种配置计算需要import的configuration和exclusion
    		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
    				annotationMetadata);
    		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    	}
                
        // 判断AudoConfiguration是否开启
    	protected boolean isEnabled(AnnotationMetadata metadata) {
    		if (getClass() == AutoConfigurationImportSelector.class) {
    			// 如果配置文件中有"spring.boot.enableautoconfiguration",返回该字段的值;否则返回true
    			return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
    		}
    		return true;
    	}
                
        protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
    			AnnotationMetadata annotationMetadata) {
    		if (!isEnabled(annotationMetadata)) {
    			return EMPTY_ENTRY;
    		}
    		// 获取注解的属性值
    		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    		// 从META-INF/spring.factories文件中获取EnableAutoConfiguration所对应的configurations
    		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    		// 去重,List转Set再转List
    		configurations = removeDuplicates(configurations);
    		// 从注解的exclude/excludeName属性中获取排除项
    		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    		// 对于不属于AutoConfiguration的exclude报错
    		checkExcludedClasses(configurations, exclusions);
    		// 从configurations去除exclusions
    		configurations.removeAll(exclusions);
    		// 由所有AutoConfigurationImportFilter类的实例再进行一次筛选,去
    		configurations = filter(configurations, autoConfigurationMetadata);
    		// 把AutoConfigurationImportEvent绑定在所有AutoConfigurationImportListener子类实例上
    		fireAutoConfigurationImportEvents(configurations, exclusions);
    		// 返回(configurations, exclusions)组
    		return new AutoConfigurationEntry(configurations, exclusions);
    	}
        // ......
    }
    
    可见selectImports()是AutoConfigurationImportSelector的核心函数,其核心功能就是获取spring.factories中EnableAutoConfiguration所对应的Configuration类列表,由@EnableAutoConfiguration注解中的exclude/excludeName参数筛选一遍,再由AutoConfigurationImportFilter类所有实例筛选一遍,得到最终的用于Import的configuration和exclusion。
    该函数是被谁调用的呢?在org.springframework.context.annotation.ConfigurationClassParser类中被processImports()调用,而processImports()函数被doProcessConfigurationClass()调用。下面从doProcessConfigurationClass() 看起。
     
     

    (Spring) context.annotation.ConfigurationClassParser.doProcessConfigurationClass()方法

    其中,configClass是一个ConfigurationClass实例,记录了bean name(返回的bean名)和meta data(配置数据);sourceClass是简单封装后的有注解的类,主要方便对类的注解的使用,初始值是封装过的configClass。

    doProcessConfigurationClass() 对ConfigurationClass进行了各种配置,包括process @ComponentScan, process @Bean, process @Import等等。如果该SourceClass有父类,返回父类,否则返回null。
            @Nullable
    	protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
    			throws IOException {
    		// ......
    		// Process any @Import annotations
    		processImports(configClass, sourceClass, getImports(sourceClass), true); 
    
    		// ......
    		// Process superclass, if any
    		if (sourceClass.getMetadata().hasSuperClass()) {
    			String superclass = sourceClass.getMetadata().getSuperClassName();
    			if (superclass != null && !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;
    	}   
    

    其中,processImports()方法的第三个参数中,getImports()方法嵌套的遍历了sourceClass的注解,搜集所有@Import注解的值,即被Import的类名集合。

    	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
    		Set<SourceClass> imports = new LinkedHashSet<>();
    		Set<SourceClass> visited = new LinkedHashSet<>();
    		collectImports(sourceClass, imports, visited);
    		return imports;
    	}
    
    	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
    			throws IOException {
            // 如果是头一次添加
    		if (visited.add(sourceClass)) {
    			for (SourceClass annotation : sourceClass.getAnnotations()) {
    				String annName = annotation.getMetadata().getClassName();
                    // 当前注解的类名是否是Import
    				if (!annName.equals(Import.class.getName())) { 
                        // 嵌套遍历被Import的类
    					collectImports(annotation, imports, visited);
    				}
    			}
                // 增加Import注解的值,即被Import的类名
    			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    		}
    	}
    

    (Spring) context.annotation.ConfigurationClassParser.processImports()方法

    processImports() 的第一个参数configClass,是上层函数processConfigurationClass()的唯一参数,即被处理的Configuration类。第二个参数currentSourceClass是configClass的SourceClass类封装。第三个参数是嵌套遍历出的所有需要被Import的类。第四个参数指定是否检查循环import。

    	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
    			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    
    		// ......
    				for (SourceClass candidate : importCandidates) {
                        // 如果candidate(即被@Import的类)是ImportSelector的子类
    					if (candidate.isAssignable(ImportSelector.class)) {
    						// Candidate class is an ImportSelector -> delegate to it to determine imports
    						Class<?> candidateClass = candidate.loadClass();
                            // 生成candidate class(一个ImportSelector子类)的实例
    						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                            // 将ImportSelector子类实例挂载为对应功能的Aware类(用于消息通知?)
    						ParserStrategyUtils.invokeAwareMethods(
    								selector, this.environment, this.resourceLoader, this.registry);
                            // DeferredImportSelector是一种特殊的ImportSelector,这里单独处理
    						if (selector instanceof DeferredImportSelector) {
                                // 挂到deferredImportSelectors列表上
    							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    						}
    						else {
                                // 筛选符合要求的@Import的名字
    							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                // 转换成名字对应的类的集合
    							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                                // 嵌套判断被@Import的类是不是还有@Import注解
    							processImports(configClass, currentSourceClass, importSourceClasses, false);
    						}
    					}
    					// ......
    				}
    		// ......
    	}
    

    (Spring) context.annotation.ConfigurationClassParser.DefferedImportSelectorHandler私有类

    deferredImportSelectors已被初始化为ArrayList<>(),因此全部走else分支。

    private class DeferredImportSelectorHandler {
    		@Nullable
    		private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
    
    		public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
    			DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
    					configClass, importSelector); // 一个封装后的pair
                // deferredImportSelectors被初始化为ArrayList<>(),所以if分支永远不会执行到?
    			if (this.deferredImportSelectors == null) { 
    				DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
    				handler.register(holder); // 注册到DeferredImportSelectorGroupingHandler.configurationClasses中
    				handler.processGroupImports(); // -> processImports()
    			}
    			else {
                    // 所有的DeferredImportSelector类实例都挂到deferredImportSelectors列表上
    				this.deferredImportSelectors.add(holder);
    			}
    		}
            // ......
    }
    

      

    那么deferredImportSelectors上挂载的DefferedImportSelector类是何时处理的呢?
    doProcessConfigurationClass() 方法被processConfigurationClass() 调用,而processConfigurationClass() 被parse() 调用。可以看到在处理完所有的普通ImportSelector类后,即嵌套载入需要的被Import的类的实例之后,再统一处理DefferedImportSelector类。
     

    (Spring) context.annotation.ConfigurationClassParser.processConfigurationClass() 方法

        protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
            // ...
    
            // Recursively process the configuration class and its superclass hierarchy.
            SourceClass sourceClass = asSourceClass(configClass);
            do {
                // 如果sourceClass有父类会返回父类,否则返回null
                sourceClass = doProcessConfigurationClass(configClass, sourceClass);
            }
            while (sourceClass != null);
    
            this.configurationClasses.put(configClass, configClass);
        }
    

      

    (Spring) context.annotation.ConfigurationClassParser.parse() 方法

            public void parse(Set<BeanDefinitionHolder> configCandidates) {
        	// 处理所有的ImportSelector类,其中DeferredImportSelector类只挂在deferredImportSelectorHandler列表上不处理,其他均处理,即嵌套遍历被Import的类
    		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);
    			}
    		}
    		
        	// 处理deferredImportSelectorHandler上挂着的DeferredImportSelector类
    		this.deferredImportSelectorHandler.process();
    	}
    
    	protected final void parse(@Nullable String className, String beanName) throws IOException {
    		Assert.notNull(className, "No bean class name for configuration class bean definition");
    		MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
    		processConfigurationClass(new ConfigurationClass(reader, beanName));
    	}
    
    	protected final void parse(Class<?> clazz, String beanName) throws IOException {
    		processConfigurationClass(new ConfigurationClass(clazz, beanName));
    	}
    
    	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    		processConfigurationClass(new ConfigurationClass(metadata, beanName));
    	}
    

      

    再来看看DeferredImportSelector实例是如何被处理的。

    (Spring) context.annotation.ConfigurationClassParser.DefferedImportSelectorHandler私有类

    和普通ConfigurationClass一样,DefferedImportSelector最后也是先注册到列表中,再依次嵌套处理,只不过在import前多了一个根据order排序。

    	// 对@Order(...)进行排序
    	private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =
    			(o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getImportSelector(), o2.getImportSelector());
    
    	private class DeferredImportSelectorHandler {
        	// ...
        	public void process() {
    			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    			this.deferredImportSelectors = null;
    			try {
    				if (deferredImports != null) {
    					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
    					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                        // 添加到handler的configurationClasses列表中
    					deferredImports.forEach(handler::register);
                        // 对handler中每个grouping的每个configClass,调用processImports()
    					handler.processGroupImports();
    				}
    			}
    			finally {
    				this.deferredImportSelectors = new ArrayList<>();
    			}
    		}
    	}
    

    DONE.

  • 相关阅读:
    js的基本数据类型有哪些?
    页面优化的方式有哪些?
    为什么要用rem
    sass开发过程中遇到的几个坑
    js事件监听
    js数组去重的方法
    什么是怪异盒模型
    Cookie SameSite属性介绍及其在ASP.NET项目中的应用
    HttpServlet在第一个Servlet程序中的知识点
    Myeclipse 2017 创建第一个servlet步骤
  • 原文地址:https://www.cnblogs.com/desertfish/p/11637933.html
Copyright © 2020-2023  润新知