Código-fonte do SpringBoot (1) configuração automática

prefácio

Eu uso a configuração automática como o primeiro capítulo do SpringBoot, porque do SpringMvc ao SpringBoot, ele atinge a configuração zero e há muitas configurações padrão. Ao entender o código-fonte posteriormente, pode haver alguns lugares que eu não entendo e o A ideia não é clara. Portanto, primeiro entenda claramente a parte da configuração automática e saiba como uma de suas configurações é carregada, e o aprendizado subsequente deve ser mais suave.

A configuração automática do SpringBoot @EnableAutoConfigurationé habilitada por anotações, então vamos começar com essa anotação para ver como ela implementa configuração automática e filtragem condicional.

princípio

Pacotes jar externos ou pacotes de terceiros são carregados da mesma forma.Para obter a configuração automática, você precisa seguir a especificação do spring, geralmente configuração META-INFO/spring.factoriese META-INF/spring-autoconfigure-metadata.propertiesdois arquivos, para realizar a configuração automática.

No capítulo Spring, código-fonte spring (6), processo de análise da classe de configuração , aprendi como o Spring injeta beans por meio de classes de configuração, como segue.

O Spring o julga como uma classe de configuração e há várias anotações @Configuration、@Component、@ComponentScan、@Import、@ImportResource:@Bean

Todas as formas convencionais do Spring de injetar beans são (juízes do Spring de beanDefinition, todas as premissas são beanDefinition):

  1. @Component/@Configuration

  2. @Component/@Configuration + @Bean

  3. @Component/@Configuration + @CompnentScan

  4. @Component/@Configuration + @Import

  5. @Component/@Configuration + @ImportResource

Então você provavelmente pode adivinhar a maneira de introduzir outras classes externas. Você pode usar @ComponentScano método de varredura para escanear no pacote externo, e este método é aplicar a varredura ativa. Este método ativo não é flexível para a estrutura de integração de componentes. , Por exemplo, se você abrir uma interface para um terceiro e não puder chamar a interface, precisará adicionar essa interface à sua configuração. Parece muito inconsistente, tipo, desenvolvo uma ideia Plug-in, fiz de acordo com a especificação da ideia, liberei e também uso para notificar o oficial da ideia, deixe-os habilitar a permissão de uso do meu plug-in, é ultrajante, ninguém se importa com você.

Portanto, o que devemos prestar atenção deve ser a aceitação passiva dos componentes e, em seguida, a digitalização, assim como a montagem de um carro, com a mola fornecendo a estrutura, o chip de controle principal e o motor, montamos o resto.

No Tomcat, o Tomcat inicializa a aplicação META-INF/services/javax.servlet.ServletContainerInitializerlendo a classe de implementação da aplicação, e o spring é o mesmo, META-INFO/spring.factoriescarrega a configuração lendo este arquivo.

Por exemplo, o pacote de dependência de mybatis

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

Quando importado, conta com um

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

Pode-se ver que existem duas classes de configuração configuradas nele, e esses dois são os núcleos que o mybatis pode configurar automaticamenteimagem-20221022195751583

Então vamos ver a configuração automática do SpringBoot. O Spring introduzirá a seguinte dependência de configuração automática. Existem muitas classes de configuração automática nele. Basicamente, todas as configurações automáticas do spring-boot estão aqui. Se você olhar com atenção, verá aop , jpa, MongoDB, kafka, servlet, etc.

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

imagem-20221022231936137

@EnableAutoConfiguration

Importação de análise

Então, como o SpringBoot carrega pacotes externos ou pacotes de terceiros?

Ao iniciar o aplicativo SpringBoot, serão adicionadas anotações @SpringBootApplication, e essa anotação é a chave

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

SpringApplication.run(App.class);Isso é para iniciar o contêiner da Web, o contêiner Spring e, em seguida, o conjunto Spring.

A definição que vimos @SpringBootApplication, existe uma nela @EnableAutoConfiguration, esta é a anotação para carregamento automático de configuração

imagem-20221022233013456

imagem-20221022233052400

@EnableAutoConfigurationExiste tal nota na 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 descobrir por meio de anotações que seu valor pode ser definido para quatro tipos de classes:

  1. @ConfigurationClasses de configuração anotadas
  2. classe que implementa ImportSelectora interface
  3. ImportBeanDefinitionRegistrarclasse implementada
  4. componentclasse comum

Quando o Spring iniciar, ele criará um contêiner ioc e adicionará nossa classe de configuração ( @SpringBootApplicationclasse de inicialização marcada) como uma classe de configuração, que é equivalente a uma Configuratione a primeira classe de configuração, por meio da qual outras classes ou beans de configuração são verificados; Ao analisar isso classe de configuração, ela será importsanalisada independentemente de estar presente ou não

O local da classe de configuração de análise: org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

Localização do importador de análise: org.springframework.context.annotation.ConfigurationClassParser#processImports

A função desta etapa é descobrir a classe ImportSelector nas importações e também descobrir o registro beanDefinition na anotação Import, apenas descobrir e não fazer 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();
			}
		}
	}

Veja: ParserStrategyUtils.instantiateClass(...), sua instanciação não é uma instanciação simples, mas também inicializa o 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;
	}

Pode-se ver a partir desta etapa que @Importso Spring oferece o maior suporte para as classes 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);
			}
		}
	}

Depois de encontrar a classe que precisa ser importada acima, execute ImportSelectorHandle

Exemplo: 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();
	}

**Resumo:** as etapas acima são para importanalisar as anotações e, em seguida, analisar as classes anotadas, incluindo a análise recursiva.

Existem três casos de importação:

  1. Se a interface for implementada ImportSelector, então esse tipo de interface precisa ser importado executando o método interface, então ela será adicionada aodeferredImportSelectorHandler
  2. ImportBeanDefinitionRegistrarA interface está implementada. Essa interface pertence à interface de registro do beanDefinition. A criação de beans na primavera é baseada no beanDefinition, portanto, deve ser baseada em etapas unificadas e, no processo de análise das classes de configuração, ainda não foi criado . As etapas dos beans ainda estão em fase de preparação, então eles serão instanciados junto com outras definições de bean posteriormente, então eles são todos colocados aqui, e depois que a classe de configuração for analisada posteriormente, ela será retirada configurationClassese mesclada
  3. Em outros casos, classes de configuração comuns, como Configuration componentetc., também são colocadas como configurationClassesclasses de configuração

Então o próximo passo ( this.deferredImportSelectorHandler.process();) é executar a classe @Importobtida através da anotação , que pode ser chamada de deep import, esta é a nossa descrição da função do método dela, que pode não ser precisa.ImportSelector

Execute o manipulador de classes de importação

Veja deferredImportSelectorHandler.processcomo fazer isso, mas aqui está deferredImportSelectorHandler.handleuma maneira de mencioná-lo.

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

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

Possui julgamento nulo, mas a classe é nova quando inicializada, não havendo situação nula.

O que eu quero dizer é que toda a situação nula é depois que o seletor escaneia, e ele vai executar o método process ( this.deferredImportSelectorHandler.process();), no método process, ele faz um loop deferredImportSelectorse depois executa, e deferredImportSelectorsnão é thread-safe, e analisando a configuração class também é um ciclo, portanto, este processo não pode garantir que o add ocorrerá durante a execução, portanto, o processo ficará vazio primeiro, para que, ao manusear, você saiba que o conjunto de identificadores atual já está sendo processado e não pode ser adicionado, execute diretamente Bar;

O significado de renovar uma lista no processo é o mesmo, para garantir que o ciclo atual não seja destruído.

// 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<>();
			}
		}
	}

Em seguida, observe o método de processo

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

deferredImports.forEach(handler::register);A adição de grupo ao processo AutoConfigurationImportSelectoré retornada diretamente pelo métodoAutoConfigurationGroup.class

Então, duas coisas são inicializadas aqui:

  1. group -> AutoConfigurationGroup.class configura automaticamente o grupo, e o grupo é direcionado para dois cenários:
    1. Pacote jar atual - "DefaultDeferredImportSelectorGroup.class Este artigo não analisa
    2. Pacote jar externo - "AutoConfigurationGroup.class ( o foco desta análise )
  2. deferredImports -> DeferredImportSelector, uma subclasse de DeferredImportSelector em @Import.value, ele precisa
		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()O método realmente executado:

Aqui, group refere-se a AutoConfigurationImportSelector.AutoConfigurationGroup, que é fixo, enquanto deferredImport é obtido de anotações e não é fixo. Group.process pode ser considerado um executor e deferredImport é implementado como uma interface de terceiros

// 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);
   }
}

Aqui está a construção da classe (Lista) lida do arquivo spring.fatories AutoConfigurationEntry, que pode ser considerado como um objeto de classe de configuração, seguido de análise

	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);
	}



Aqui está List<String> configurationsa lógica que ele obtém, exceto a parte que lê o arquivo. A partir deste parágrafo, pode-se ver que ele carrega o valor SpringFactoriesLoader.loadFactoryNamesde acordo com o nome completo da classe. A camada inferior deste método é para ler diretamenteEnableAutoConfiguration 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());
    }

Então a configuração automática do SpringBoot é ler o valor desta chave

imagem-20221023202016235

**Resumo:** Este passo é DeferredImportSelectora classe a ser executada. @ImportSe houver subclasses introduzidas nela, ela será executada aqui. @EnableAutoConfigurationO que está na anotação é AutoConfigurationImportSelector, então getAutoConfigurationEntryo método será executado para ler

META-INF/spring.factoriesA chave abaixo org.springframework.boot.autoconfigure.EnableAutoConfigurationé o valor da chave, que é carregado neste momento List<String>e, em seguida, leia o processador de filtro condicional da classe de configuração para filtrar, em seguida, construa-o em uma entrada e coloque-o em e , em seguida, percorra a recursão autoConfigurationEntriesobtida de spring.factories para analisar o início da importação.autoConfigurationEntriesprocessImports

SpringBootCondition

Na configuração automática, existe outro tipo importante de anotação Conditional, que também é função da configuração automática, que pode injetar beans de acordo com as condições ambientais.

por exemplo:

@ConditionalOnBean

@ConditionalOnMissingBean

@ConditionalOnClass

No SpringBoot, é a base de todas as classes de processamento condicional, e o restante pode ser julgado SpringBootConditionimplementando sua interface.getMatchOutcome

Por exemplo @ConditionalOnBean, se também estiver marcado @Conditional(OnBeanCondition.class), então sua classe de processamento éOnBeanCondition

imagem-20221026231312435

Aqui também quero mencionar um ponto, porque não elaborei acima;

A anotação acima @Importlerá META-INF/spring.factoriesa classe de configuração abaixo e lerá os valores de chave de duas chaves no total:

EnableAutoConfiguration -> Esta é a classe de importação

AutoConfigurationImportFilter -> Esta é a classe de implementação da classe de processamento de condição Condition, que é usada para processar a classe de importação

No momento os imports são todos do tipo String, e a classe ainda não foi lida. Por existirem anotações como esta, haverá ConditionalOnClassum filtro antes de carregar a classe, e a condição do filtro será configurada e salva em META-INF/spring-autoconfigure-metadata.properties; key=nome completo da classe. O nome da anotação Condition indica quais anotações de condição estão marcadas na classe de configuração. Você pode encontrá-las nos pacotes spring e mybatis.

imagem-20221026230342257

imagem-20221026225808585

julgamento

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

Esta classe é uma classe de verificação de condição de verificação que será chamada antes de analisar a classe de configuração.É chamada para julgar se a classe está carregada.shouldSkip

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

ConfigurationPhase tem dois valores:

  1. PARSE_CONFIGURATION -> indica que a configuração de análise está no estágio de configuração de análise neste momento, o julgamento shouldSkip falha e a classe não será carregada
  2. REGISTER_BEAN -> indica que o tempo está no estágio de registro do bean, caso Skip não julgue e o bean não será registrado
/**
 * @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 o julgamento a seguir, a verificação condicional da classe de configuração é toda implementada ConfigurationCondition, mas para configurações diferentes, o tempo de análise e carregamento é diferente e haverá diferenças.

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

Tome OnBeanConditioncomo exemplo, precisa REGISTER_BEANser julgado em etapas, pois a condição do onBeanConditional é para objetos bean, então ele precisa preparar a classe, e quando uma classe de configuração for escaneada, pode ser retirada diretamente quando estiver em missingBean ou onBean.

OnBeanConditionMétodo de correspondência 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;
	}

Aqui ClassNameFilter.MISSINGestá um método

MISSING {
    
    

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

		};

então volteorg.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)

Ela tem muitas subclasses e suas implementações são diferentes, mas a lógica correspondente é semelhante.

Resumir

@Importsuporte de importação

  1. @ConfigurationClasses de configuração anotadas
  2. classe que implementa ImportSelectora interface
  3. ImportBeanDefinitionRegistrarclasse implementada
  4. componentclasse comum

Usado na configuração automática do SpringBoot , ele lê o valor da chave @Import(AutoConfigurationImportSelector.class)por meio de AutoConfigurationImportSelector , carrega-o na lista de classes de configuração e, em seguida, lê o processador de filtro condicional da chave e a configuração condicional da classe de configuração do arquivo, filtra e obtém o configuração A entrada da classe e, em seguida, percorrer a entrada obtida e, em seguida, passar pelo processo de análise de importação (recursiva).META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfigurationorg.springframework.boot.autoconfigure.AutoConfigurationImportFilterMETA-INF/spring-autoconfigure-metadata.properties

Portanto, se quisermos implementar nosso iniciador de inicialização personalizado, precisamos escrever um META-INF/spring.factories, assim como mybatis

imagem-20221022195751583

Este processo é realizado quando a classe de configuração é analisada e, após a análise da classe de configuração, a classe de configuração analisada será registrada e ConfigurationClassBeanDefinitionReaderinstanciada posteriormente.

Também podemos definir o processador de condição de classe de configuração por nós mesmos, apenas herdá-lo SpringBootConditione, em seguida, personalizar uma anotação. Estou com preguiça aqui e não escrevi uma demonstração. Tenho tempo para escrever uma no futuro.

Acho que você gosta

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