Código fuente de SpringBoot (1) configuración automática

prefacio

Utilizo la configuración automática como el primer capítulo de SpringBoot, porque de SpringMvc a SpringBoot, logra una configuración cero y hay muchas configuraciones predeterminadas. Al comprender el código fuente más adelante, puede haber algunos lugares que no entiendo, y el la idea no es clara Entonces, primero entienda claramente la parte de la configuración automática y sepa cómo se carga una de sus configuraciones, y el aprendizaje posterior debería ser más fluido.

La configuración automática de SpringBoot @EnableAutoConfigurationestá habilitada por anotaciones, así que comencemos con esta anotación para ver cómo implementa la configuración automática y el filtrado condicional.

principio

Los paquetes jar externos o los paquetes de terceros se cargan de la misma manera.Para lograr la configuración automática, debe seguir la especificación Spring, generalmente la configuración META-INFO/spring.factoriesy META-INF/spring-autoconfigure-metadata.propertiesdos archivos, para realizar la configuración automática.

En el proceso de análisis de la clase de configuración del código fuente de Spring del capítulo Spring (6) , aprendí cómo Spring inyecta beans a través de las clases de configuración, de la siguiente manera.

Spring lo juzga como una clase de configuración, y hay varias anotaciones @Configuration、@Component、@ComponentScan、@Import、@ImportResource:@Bean

Todas las formas convencionales de Spring de inyectar beans son (Spring juzga de beanDefinition, toda premisa es una beanDefinition):

  1. @Component/@Configuration

  2. @Component/@Configuration + @Bean

  3. @Component/@Configuration + @CompnentScan

  4. @Component/@Configuration + @Import

  5. @Component/@Configuration + @ImportResource

Entonces probablemente pueda adivinar la forma de introducir otras clases externas. Puede usar @ComponentScanel método de escaneo para escanear en el paquete externo, y este método es aplicar el escaneo activo. Este método activo no es flexible para el marco de integración de componentes. , Por ejemplo, si abre una interfaz a un tercero y luego no puede llamar a la interfaz, tiene que agregar esta interfaz en su configuración, se siente muy inconsistente, como, desarrollo una idea Complemento, lo he hecho de acuerdo con la especificación de la idea, lo liberé, y también lo uso para notificar al funcionario de la idea, permitirles habilitar el permiso de uso de mi complemento, es indignante, nadie se preocupa por ti.

Entonces, debemos prestar atención a la aceptación pasiva de componentes y luego escanear, al igual que ensamblar un automóvil, con un resorte que proporciona el marco, el chip de control principal y el motor, ensamblaremos el resto.

En Tomcat, Tomcat inicializa la aplicación leyendo META-INF/services/javax.servlet.ServletContainerInitializerla clase de implementación de la aplicación, y Spring es lo mismo, META-INFO/spring.factoriescarga la configuración leyendo este archivo.

Por ejemplo, el paquete de dependencia de mybatis

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

Cuando se importa, se basa en un

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
    </dependency>

Se puede ver que hay dos clases de configuración configuradas en él, y estos dos son los núcleos que mybatis puede configurar automáticamenteimagen-20221022195751583

Luego, veamos la configuración automática de SpringBoot. Spring introducirá la siguiente dependencia de configuración automática. Hay muchas clases de configuración automática en él. Básicamente, todas las configuraciones automáticas de Spring-Boot están aquí. Si observa detenidamente, verá que aop , jpa, MongoDB, kafka, servlet, etc.

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

imagen-20221022231936137

@EnableAutoConfiguration

Importación de análisis

Entonces, ¿cómo carga SpringBoot paquetes externos o paquetes de terceros?

Al iniciar la aplicación SpringBoot, se agregarán anotaciones @SpringBootApplication, y esta anotación es la clave

@SpringBootApplication
public class App {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(App.class);
    }
}

SpringApplication.run(App.class);Esto es para iniciar el contenedor web, el contenedor Spring y luego el conjunto Spring.

La definición que vimos @SpringBootApplication, hay una en ella @EnableAutoConfiguration, esta es la anotación para la carga automática de configuración

imagen-20221022233013456

imagen-20221022233052400

@EnableAutoConfigurationHay tal nota en la nota.@Import({AutoConfigurationImportSelector.class})

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    
    

	/**
	 * {@link Configuration @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
	Class<?>[] value();

}

Podemos averiguar a través de anotaciones que su valor se puede establecer en cuatro tipos de clases:

  1. @ConfigurationClases de configuración anotadas
  2. clase que implementa ImportSelectorla interfaz
  3. ImportBeanDefinitionRegistrarclase implementada
  4. componentclase común

Cuando se inicie Spring, creará un contenedor ioc y agregará nuestra clase de configuración ( @SpringBootApplicationclase de inicio marcada) como una clase de configuración, que es equivalente a una Configurationy la primera clase de configuración, a través de la cual se escanean otras clases de configuración o beans; al analizar esto clase de configuración, se importsanalizará independientemente de si está presente o no

La ubicación de la clase de configuración de análisis: org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

Ubicación del importador de análisis: org.springframework.context.annotation.ConfigurationClassParser#processImports

La función de este paso es encontrar la clase ImportSelector en las importaciones, y también encontrar el registro beanDefinition en la anotación Importar, solo averiguarlo y no hacer nada.

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {
    
    

    // 当配置类没有impots时不再进行
		if (importCandidates.isEmpty()) {
    
    
			return;
		}
    // 判断是否循环引入
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
    
    
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
    
    
            // 标记正在import的配置类
			this.importStack.push(configClass);
			try {
    
    
				for (SourceClass candidate : importCandidates) {
    
    
                    
					if (candidate.isAssignable(ImportSelector.class)) {
    
    
						// 判断import的类是否是ImportSelector子类
                        // @SpringBootApplication里的import注解走的就是这里
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
								this.environment, this.resourceLoader, this.registry);
                        // 排除选项
						Predicate<String> selectorFilter = selector.getExclusionFilter();
						if (selectorFilter != null) {
    
    
							exclusionFilter = exclusionFilter.or(selectorFilter);
						}
						if (selector instanceof DeferredImportSelector) {
    
    
                            // ImportSelector还有另一个子类接口DeferredImportSelector,
                            // 这里只做了一个动作:添加importHandler
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
    
    
                            // 普通的imports
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
							processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    
    
                        // 判断import的类是否是ImportBeanDefinitionRegistrar子类
                        // 这个子类从命名上看就是一个beanDefinition注册器,spring中的所有bean都是先通过beanDefinition注册后,再根据beanDefinition实例化的,所有这里的注册器后面也要添加到已经载入的注册器中
                        // 可以查看:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
                        // 它在parse(配置类)后有这么一句:this.reader.loadBeanDefinitions(configClasses);
                        // 这句就是将这里找出的注册器都添加的到已经加入的spring容器里的BeanDefinitionReader
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
    
    
						// import的类不是ImportBeanDefinitionRegistrar,ImportSelector,那么就作为配置类进行载入
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
    
    
				throw ex;
			}
			catch (Throwable ex) {
    
    
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
    
    
				this.importStack.pop();
			}
		}
	}

Eche un vistazo a: ParserStrategyUtils.instantiateClass(...), su creación de instancias no es una simple creación de instancias, sino que también inicializa el bean instanciado.

org.springframework.context.annotation.ParserStrategyUtils#instantiateClass

	static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment,
			ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
    
    

        // 二次校验
		Assert.notNull(clazz, "Class must not be null");
		Assert.isAssignable(assignableTo, clazz);
		if (clazz.isInterface()) {
    
    
            // import 的类不能是接口
			throw new BeanInstantiationException(clazz, "Specified class is an interface");
		}
		ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
				((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
        // 反射实例化,这里AutoConfigurationImportSelector是无参构造
		T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
        // 这一步就是为这个实例化的AutoConfigurationImportSelector进行遍历初始化,
		ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader);
		return instance;
	}

Se puede ver en este paso que @ImportsSpring le brinda el mayor soporte para las clases importadas:BeanClassLoaderAware、BeanFactoryAware、EnvironmentAware、ResourceLoaderAware

	private static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
			ResourceLoader resourceLoader, BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {
    
    

		if (parserStrategyBean instanceof Aware) {
    
    
			if (parserStrategyBean instanceof BeanClassLoaderAware && classLoader != null) {
    
    
				((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader);
			}
			if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) {
    
    
				((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry);
			}
			if (parserStrategyBean instanceof EnvironmentAware) {
    
    
				((EnvironmentAware) parserStrategyBean).setEnvironment(environment);
			}
			if (parserStrategyBean instanceof ResourceLoaderAware) {
    
    
				((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader);
			}
		}
	}

Después de encontrar la clase que debe importarse arriba, ejecute ImportSelectorHandle

Ejemplo: org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
    
    
        // 解析配置类
		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);
			}
		}

        // importSelectorHandler执行深度import
		this.deferredImportSelectorHandler.process();
	}

**Resumen:** Los pasos anteriores son para importanalizar las anotaciones y luego analizar las clases anotadas, incluido el análisis recursivo.

Hay tres casos de importación:

  1. Si se implementa la interfaz ImportSelector, entonces este tipo de interfaz debe importarse ejecutando el método de interfaz, por lo que se agregará a ladeferredImportSelectorHandler
  2. ImportBeanDefinitionRegistrarLa interfaz está implementada. Esta interfaz pertenece a la interfaz de registro de beanDefinition. La creación de beans en primavera se basa en beanDefinition, por lo que debe basarse en pasos unificados, y en el proceso de análisis de clases de configuración, aún no se ha creado. Los pasos de los beans aún se encuentran en la etapa de preparación, por lo que se crearán instancias junto con otras definiciones de beans más adelante, por lo que todos se colocan aquí, y después de que la configurationClassesclase de configuración se analice más tarde, se eliminará y fusionará .
  3. En otros casos, las clases de configuración ordinarias, como Configuration componentetc., también se incluyen como configurationClassesclases de configuración.

Luego, el siguiente paso ( this.deferredImportSelectorHandler.process();) es ejecutar la clase @Importobtenida a través de la anotación , lo que se puede decir que es una importación profunda. Esta es nuestra descripción de su función de método, que puede no ser precisa.ImportSelector

Ejecutar controlador de clase de importación

He aquí deferredImportSelectorHandler.processcómo hacerlo, pero aquí hay deferredImportSelectorHandler.handleuna manera de mencionarlo.

Ejemplo: org.springframework.context.annotation.ConfigurationClassParser#processImports

this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);

Tiene un juicio nulo, pero la clase es nueva cuando se inicializa y no hay una situación nula.

Lo que quiero decir es que toda la situación nula es después de que el selector escanea, y ejecutará el método de proceso ( this.deferredImportSelectorHandler.process();), en el método de proceso, se repite deferredImportSelectorsy luego se ejecuta, y deferredImportSelectorsno es seguro para subprocesos, y analiza la configuración La clase también es un ciclo, por lo que este proceso no puede garantizar que se agregará durante la ejecución, por lo que el proceso estará vacío primero, de modo que cuando lo maneje, sepa que el conjunto de identificadores actual ya se está procesando y no se puede agregar, luego ejecute directamente Bar;

El significado de renovar una lista en el proceso es el mismo, para asegurar que el ciclo actual no se destruya.

// deferredImportSelectorHandler.handle		
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
    
    
			DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
            // 当要添加时,发现为null,表明这个集合已经在循环处理了,这里可以直接执行
			if (this.deferredImportSelectors == null) {
    
    
				DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
				handler.register(holder);
				handler.processGroupImports();
			}
			else {
    
    
				this.deferredImportSelectors.add(holder);
			}
		}

// deferredImportSelectorHandler.process
	public void process() {
    
    
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
    
    
                // 这里执行必然不会是null,这里new List()是为了避免在循环过程中,handle方法又进行添加对象
				if (deferredImports != null) {
    
    
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					deferredImports.forEach(handler::register);
					handler.processGroupImports();
				}
			}
			finally {
    
    
				this.deferredImportSelectors = new ArrayList<>();
			}
		}
	}

Luego mira el método de proceso.

Ejemplo: org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process

deferredImports.forEach(handler::register);Agregar grupo al proceso AutoConfigurationImportSelectores devuelto directamente por el métodoAutoConfigurationGroup.class

Así que dos cosas se inicializan aquí:

  1. grupo -> AutoConfigurationGroup.class configura automáticamente el grupo, y el grupo está dirigido a dos escenarios:
    1. Paquete jar actual: "DefaultDeferredImportSelectorGroup.class Este artículo no analiza
    2. Paquete jar externo - "AutoConfigurationGroup.class ( el foco de este análisis )
  2. deferredImports -> DeferredImportSelector, una subclase de DeferredImportSelector en @Import.value, necesita
		public void register(DeferredImportSelectorHolder deferredImport) {
    
    
            // 这里其实是有值的,AutoConfigurationImportSelector它重写了这个方法,并返回了AutoConfigurationGroup.class
			Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
            // 把handle添加到grouping
            // 实际执行是:this.deferredImports.add(deferredImport);
			grouping.add(deferredImport);
			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}
		public void processGroupImports() {
    
    
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
    
    
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                // 这个getImports实际上是通过上一步添加的group.processs执行的
				grouping.getImports().forEach(entry -> {
    
    
                    // 遍历得到的configurationEntry
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
    
    
                        // 递归
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
					catch (BeanDefinitionStoreException ex) {
    
    
						throw ex;
					}
					catch (Throwable ex) {
    
    
						throw new BeanDefinitionStoreException(
								"Failed to process import candidates for configuration class [" +
										configurationClass.getMetadata().getClassName() + "]", ex);
					}
				});
			}
		}

grouping.getImports()El método realmente ejecutado:

Aquí grupo se refiere a AutoConfigurationImportSelector.AutoConfigurationGroup, que es fijo, mientras que deferredImport se obtiene de las anotaciones y no es fijo.Group.process puede considerarse como un ejecutor, y deferredImport se implementa como una interfaz de terceros

// grouping.getImports()
public Iterable<Group.Entry> getImports() {
    
    
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
    
    
                // 这里通过AutoConfigurationImportSelector.AutoConfigurationGroup#process执行
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
    // 这里将导入的类返回
			return this.group.selectImports();
		}

// group.process
/**
 * @param annotationMetadata 配置类的注解信息
 * @param deferredImportSelector 配置类上@Import.value里的类
 */
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    
    
   Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
         () -> String.format("Only %s implementations are supported, got %s",
               AutoConfigurationImportSelector.class.getSimpleName(),
               deferredImportSelector.getClass().getName()));
    // 这里它会去获取自动配置的entry
   AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
         .getAutoConfigurationEntry(annotationMetadata);
   this.autoConfigurationEntries.add(autoConfigurationEntry);
   for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    
    
      this.entries.putIfAbsent(importClassName, annotationMetadata);
   }
}

Aquí está la construcción de la clase (Lista) leída del archivo spring.fatories AutoConfigurationEntry, que puede considerarse como un objeto de clase de configuración, seguido de un análisis

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    
    
		if (!isEnabled(annotationMetadata)) {
    
    
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 获取符合条件的配置类,这里是字符串
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
        // 过滤排除的类
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
        // 加载配置过滤
        // 读取spring.factories中key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的3个条件处理类,作为导入配置类的过滤处理器(import filter)
        // 然后读取META-INF/spring-autoconfigure-metadata.properties下的键值(配置类的条件,格式className.condition)
        // 这里这样处理,是因为在这一步并没有加载class,还知识className,通过spring-autoconfigure-metadata.properties配置,将条件写进到里面,然后进行过滤,下面还会提到,这里只是说明一下
        
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}



Aquí está List<String> configurationsla lógica que obtiene, excepto por la parte que lee el archivo. Desde este párrafo, se puede ver que carga el valor SpringFactoriesLoader.loadFactoryNamesde acuerdo con el nombre completo de la clase. La capa inferior de este método es leer directamenteEnableAutoConfiguration META-INF/spring.factories

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
        // getSpringFactoriesLoaderFactoryClass() = () -> return EnableAutoConfiguration.class;
        // 所以这里它是通过 EnableAutoConfiguration 全类名获取对应 META-INF/spring.factories 下的value
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

// 上面一步的:SpringFactoriesLoader.loadFactoryNames方法实现

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    
    
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
    
    
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

Entonces la configuración automática de SpringBoot es leer el valor de esta clave

imagen-20221023202016235

**Resumen:** Este paso es DeferredImportSelectorla clase a ejecutar. @ImportSi hay subclases introducidas en él, se ejecutará aquí. @EnableAutoConfigurationLo que está en la anotación es AutoConfigurationImportSelector, por lo que getAutoConfigurationEntryel método se ejecutará para leer

META-INF/spring.factoriesLa clave a continuación org.springframework.boot.autoconfigure.EnableAutoConfigurationes el valor de la clave, que se carga en este momento List<String>, y luego lee el procesador de filtro condicional de la clase de configuración para filtrar, luego lo construye en una entrada y lo coloca , y luego atraviesa la recursividad autoConfigurationEntriesobtenida de spring.factories para analizar el comienzo de la importación.autoConfigurationEntriesprocessImports

SpringBootCondición

En la configuración automática, existe otro tipo importante de anotación Conditional, que también es función de la configuración automática, que puede inyectar beans según las condiciones ambientales.

Por ejemplo:

@CondicionalOnBean

@ConditionalOnMissingBean

@CondicionalEnClase

En SpringBoot, es la base de todas las clases de procesamiento condicional, y el resto se puede evaluar SpringBootConditionimplementando su interfaz.getMatchOutcome

Por ejemplo @ConditionalOnBean, si también está marcado en él @Conditional(OnBeanCondition.class), entonces su clase de procesamiento esOnBeanCondition

imagen-20221026231312435

Aquí también quiero mencionar un punto, porque no expliqué más arriba;

La anotación anterior @Importleerá META-INF/spring.factoriesla clase de configuración a continuación y leerá los valores clave de dos claves en total:

EnableAutoConfiguration -> Esta es la clase de importación

AutoConfigurationImportFilter -> Esta es la clase de implementación de la clase de procesamiento de condición de condición, que se utiliza para procesar la clase de importación

En este momento, las importaciones son todas de tipo String y la clase aún no se ha leído. Debido a que hay anotaciones como esta, habrá ConditionalOnClassun filtro antes de cargar la clase, y la condición del filtro se configurará y guardará en META-INF/spring-autoconfigure-metadata.properties; su key=nombre completo de la clase.El nombre de la anotación de condición indica qué anotaciones de condición están marcadas en la clase de configuración.Puedes encontrarlas en los paquetes de spring y mybatis.

imagen-20221026230342257

imagen-20221026225808585

juicio

org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)

Esta clase es una clase de verificación de condición de verificación que se llamará antes de analizar la clase de configuración. Se llama para juzgar si la clase está cargada.shouldSkip

Ejemplo: org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)

ConfigurationPhase tiene dos valores:

  1. PARSE_CONFIGURATION -> indica que la configuración de análisis está en la etapa de configuración de análisis en este momento, el juicio shouldSkip falla y la clase no se cargará
  2. REGISTER_BEAN -> indica que el tiempo está en la etapa de registro del bean, shouldSkipt falla al juzgar y el bean no se registrará
/**
 * @param metadata 注解元数据信息
 * @param phase 执行方法的阶段,时机
*/
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    
    
    // 是否有条件注解
		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
    
    
			return false;
		}

		if (phase == null) {
    
    
			if (metadata instanceof AnnotationMetadata &&
					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
    
    
				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
			}
			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
		}

		List<Condition> conditions = new ArrayList<>();
    // 从条件注解中获取到条件处理类,如@ConditionalOnMissingBean,拿到的就是这个注解上面的@Conditional(OnBeanCondition.class)的OnBeanCondition
		for (String[] conditionClasses : getConditionClasses(metadata)) {
    
    
			for (String conditionClass : conditionClasses) {
    
    
				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
				conditions.add(condition);
			}
		}

		AnnotationAwareOrderComparator.sort(conditions);

		for (Condition condition : conditions) {
    
    
			ConfigurationPhase requiredPhase = null;
			if (condition instanceof ConfigurationCondition) {
    
    
				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
			}
            // 这里就是条件判断,看matches
            // 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
    
    
				return true;
			}
		}

		return false;
	}

Para el siguiente juicio, la verificación condicional de la clase de configuración está implementada ConfigurationCondition, pero para diferentes configuraciones, el tiempo de análisis y carga es diferente y habrá diferencias.

// 这里就是条件判断,看matches
            // 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
    
    
				return true;
			}

Tómelo OnBeanConditioncomo un ejemplo, debe REGISTER_BEANjuzgarse por etapas, porque la condición de onBeanConditional es para objetos de bean, luego necesita preparar la clase, y cuando se escanea una clase de configuración, se puede sacar directamente cuando está en faltaBean o onBean.

OnBeanConditionMétodo de coincidencia real:

@Override
	protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata) {
    
    
        // 定义结果数组
		ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
		for (int i = 0; i < outcomes.length; i++) {
    
    
			String autoConfigurationClass = autoConfigurationClasses[i];
			if (autoConfigurationClass != null) {
    
    
                // 这里是获取当前配置类,在条件ConditionalOnBean下所需要的处理类
                // 这里get的是spring-autoconfigure-metadata.properties读取到的key value
				Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
                // 查找当前需要的bean类型,返回返回结果信息
				outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
				if (outcomes[i] == null) {
    
    
                    // 这里也是一样,查找需要的类型,返回结果信息
					Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,
							"ConditionalOnSingleCandidate");
					outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
				}
			}
		}
		return outcomes;
	}
	
/**
 * @param requiredBeanTypes 需要查找的bean的类全名
 * @param annotation 条件注解,这里因为是ConditionalOnBean代码,所以这里是ConditionalOnBean
 */
private ConditionOutcome getOutcome(Set<String> requiredBeanTypes, Class<? extends Annotation> annotation) {
    
    
        // 这里是通过需要的类型,去Class.forName反射加载,能加载就将当前的类名添加到missing里
		List<String> missing = filter(requiredBeanTypes, ClassNameFilter.MISSING, getBeanClassLoader());
		if (!missing.isEmpty()) {
    
    
            // 构建条件检索结果
			ConditionMessage message = ConditionMessage.forCondition(annotation)
					.didNotFind("required type", "required types").items(Style.QUOTE, missing);
			return ConditionOutcome.noMatch(message);
		}
		return null;
	}

Aquí ClassNameFilter.MISSINGhay un método

MISSING {
    
    

			@Override
			public boolean matches(String className, ClassLoader classLoader) {
    
    
                // 里面其实是通过反射`Class.forName`加载,加载失败那就是没有,返回false
				return !isPresent(className, classLoader);
			}

		};

luego regresaorg.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match

	@Override
	public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
    
    
		ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
        // 这里就是上面的执行方法,查找需要的类,返回返回结果
		ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
		boolean[] match = new boolean[outcomes.length];
		for (int i = 0; i < outcomes.length; i++) {
    
    
			match[i] = (outcomes[i] == null || outcomes[i].isMatch());
			if (!match[i] && outcomes[i] != null) {
    
    
                // 日志输出
				logOutcome(autoConfigurationClasses[i], outcomes[i]);
				if (report != null) {
    
    
                    // 日志记录
					report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
				}
			}
		}
		return match;
	}

主方法是:org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)

Tiene muchas subclases y sus implementaciones son diferentes, pero la lógica de coincidencia es similar.

Resumir

@Importsoporte de importación

  1. @ConfigurationClases de configuración anotadas
  2. clase que implementa ImportSelectorla interfaz
  3. ImportBeanDefinitionRegistrarclase implementada
  4. componentclase común

Utilizado en la configuración automática de SpringBoot , lee el valor clave de la clave @Import(AutoConfigurationImportSelector.class)a través de AutoConfigurationImportSelector , lo carga en la lista de clases de configuración y luego lee el procesador de filtro condicional de la clave, y la configuración condicional de la clase de configuración del archivo, filtra y obtiene el configuración La entrada de la clase, y luego atravesar la entrada obtenida, y luego pasar por el proceso de análisis de importación (recursivo).META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfigurationorg.springframework.boot.autoconfigure.AutoConfigurationImportFilterMETA-INF/spring-autoconfigure-metadata.properties

Entonces, si queremos implementar nuestro arranque de resorte personalizado, debemos escribir uno META-INF/spring.factories, al igual que mybatis

imagen-20221022195751583

Este proceso se lleva a cabo cuando se analiza la clase de configuración, y después de analizar la clase de configuración, la clase de configuración analizada se registrará y se creará una ConfigurationClassBeanDefinitionReaderinstancia más tarde.

También podemos definir el procesador de condición de la clase de configuración por nosotros mismos, simplemente heredarlo SpringBootConditiony luego personalizar una anotación. Soy perezoso aquí y no escribí una demostración. Tengo tiempo para escribir una en el futuro.

Supongo que te gusta

Origin blog.csdn.net/qq_28911061/article/details/127544104
Recomendado
Clasificación