Directorio de artículos
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 @EnableAutoConfiguration
está 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.factories
y META-INF/spring-autoconfigure-metadata.properties
dos 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):
-
@Component/@Configuration
-
@Component/@Configuration + @Bean
-
@Component/@Configuration + @CompnentScan
-
@Component/@Configuration + @Import
-
@Component/@Configuration + @ImportResource
Entonces probablemente pueda adivinar la forma de introducir otras clases externas. Puede usar @ComponentScan
el 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.ServletContainerInitializer
la clase de implementación de la aplicación, y Spring es lo mismo, META-INFO/spring.factories
carga 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áticamente
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>
@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
@EnableAutoConfiguration
Hay 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:
@Configuration
Clases de configuración anotadas- clase que implementa
ImportSelector
la interfaz ImportBeanDefinitionRegistrar
clase implementadacomponent
clase común
Cuando se inicie Spring, creará un contenedor ioc y agregará nuestra clase de configuración ( @SpringBootApplication
clase de inicio marcada) como una clase de configuración, que es equivalente a una Configuration
y 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 imports
analizará 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 @Imports
Spring 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 import
analizar las anotaciones y luego analizar las clases anotadas, incluido el análisis recursivo.
Hay tres casos de importación:
- 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
ImportBeanDefinitionRegistrar
La 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 laconfigurationClasses
clase de configuración se analice más tarde, se eliminará y fusionará .- En otros casos, las clases de configuración ordinarias, como
Configuration component
etc., también se incluyen comoconfigurationClasses
clases de configuración.
Luego, el siguiente paso ( this.deferredImportSelectorHandler.process();
) es ejecutar la clase @Import
obtenida 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.process
cómo hacerlo, pero aquí hay deferredImportSelectorHandler.handle
una 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 deferredImportSelectors
y luego se ejecuta, y deferredImportSelectors
no 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 AutoConfigurationImportSelector
es devuelto directamente por el métodoAutoConfigurationGroup.class
Así que dos cosas se inicializan aquí:
- grupo -> AutoConfigurationGroup.class configura automáticamente el grupo, y el grupo está dirigido a dos escenarios:
- Paquete jar actual: "DefaultDeferredImportSelectorGroup.class Este artículo no analiza
- Paquete jar externo - "AutoConfigurationGroup.class ( el foco de este análisis )
- 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> configurations
la lógica que obtiene, excepto por la parte que lee el archivo. Desde este párrafo, se puede ver que carga el valor SpringFactoriesLoader.loadFactoryNames
de 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
**Resumen:** Este paso es DeferredImportSelector
la clase a ejecutar. @Import
Si hay subclases introducidas en él, se ejecutará aquí. @EnableAutoConfiguration
Lo que está en la anotación es AutoConfigurationImportSelector
, por lo que getAutoConfigurationEntry
el método se ejecutará para leer
META-INF/spring.factories
La clave a continuación org.springframework.boot.autoconfigure.EnableAutoConfiguration
es 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 autoConfigurationEntries
obtenida de spring.factories para analizar el comienzo de la importación.autoConfigurationEntries
processImports
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 SpringBootCondition
implementando su interfaz.getMatchOutcome
Por ejemplo @ConditionalOnBean
, si también está marcado en él @Conditional(OnBeanCondition.class)
, entonces su clase de procesamiento esOnBeanCondition
Aquí también quiero mencionar un punto, porque no expliqué más arriba;
La anotación anterior @Import
leerá META-INF/spring.factories
la 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á ConditionalOnClass
un 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.
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:
- 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á
- 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 OnBeanCondition
como un ejemplo, debe REGISTER_BEAN
juzgarse 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.
OnBeanCondition
Mé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.MISSING
hay 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
@Import
soporte de importación
@Configuration
Clases de configuración anotadas- clase que implementa
ImportSelector
la interfaz ImportBeanDefinitionRegistrar
clase implementadacomponent
clase 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.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
META-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
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 ConfigurationClassBeanDefinitionReader
instancia más tarde.
También podemos definir el procesador de condición de la clase de configuración por nosotros mismos, simplemente heredarlo SpringBootCondition
y luego personalizar una anotación. Soy perezoso aquí y no escribí una demostración. Tengo tiempo para escribir una en el futuro.