Diretório de artigos
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.factories
e META-INF/spring-autoconfigure-metadata.properties
dois 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):
-
@Component/@Configuration
-
@Component/@Configuration + @Bean
-
@Component/@Configuration + @CompnentScan
-
@Component/@Configuration + @Import
-
@Component/@Configuration + @ImportResource
Então você provavelmente pode adivinhar a maneira de introduzir outras classes externas. Você pode usar @ComponentScan
o 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.ServletContainerInitializer
lendo a classe de implementação da aplicação, e o spring é o mesmo, META-INFO/spring.factories
carrega 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 automaticamente
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>
@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
@EnableAutoConfiguration
Existe 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:
@Configuration
Classes de configuração anotadas- classe que implementa
ImportSelector
a interface ImportBeanDefinitionRegistrar
classe implementadacomponent
classe comum
Quando o Spring iniciar, ele criará um contêiner ioc e adicionará nossa classe de configuração ( @SpringBootApplication
classe de inicialização marcada) como uma classe de configuração, que é equivalente a uma Configuration
e 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á imports
analisada 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 @Imports
o 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 import
analisar as anotações e, em seguida, analisar as classes anotadas, incluindo a análise recursiva.
Existem três casos de importação:
- 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
ImportBeanDefinitionRegistrar
A 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á retiradaconfigurationClasses
e mesclada- Em outros casos, classes de configuração comuns, como
Configuration component
etc., também são colocadas comoconfigurationClasses
classes de configuração
Então o próximo passo ( this.deferredImportSelectorHandler.process();
) é executar a classe @Import
obtida 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.process
como fazer isso, mas aqui está deferredImportSelectorHandler.handle
uma 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 deferredImportSelectors
e depois executa, e deferredImportSelectors
nã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:
- group -> AutoConfigurationGroup.class configura automaticamente o grupo, e o grupo é direcionado para dois cenários:
- Pacote jar atual - "DefaultDeferredImportSelectorGroup.class Este artigo não analisa
- Pacote jar externo - "AutoConfigurationGroup.class ( o foco desta análise )
- 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> configurations
a 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.loadFactoryNames
de 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
**Resumo:** Este passo é DeferredImportSelector
a classe a ser executada. @Import
Se houver subclasses introduzidas nela, ela será executada aqui. @EnableAutoConfiguration
O que está na anotação é AutoConfigurationImportSelector
, então getAutoConfigurationEntry
o método será executado para ler
META-INF/spring.factories
A 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 autoConfigurationEntries
obtida de spring.factories para analisar o início da importação.autoConfigurationEntries
processImports
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 SpringBootCondition
implementando sua interface.getMatchOutcome
Por exemplo @ConditionalOnBean
, se também estiver marcado @Conditional(OnBeanCondition.class)
, então sua classe de processamento éOnBeanCondition
Aqui também quero mencionar um ponto, porque não elaborei acima;
A anotação acima @Import
lerá META-INF/spring.factories
a 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á ConditionalOnClass
um 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.
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:
- 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
- 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 OnBeanCondition
como exemplo, precisa REGISTER_BEAN
ser 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.
OnBeanCondition
Mé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.MISSING
está 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
@Import
suporte de importação
@Configuration
Classes de configuração anotadas- classe que implementa
ImportSelector
a interface ImportBeanDefinitionRegistrar
classe implementadacomponent
classe 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.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
META-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
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 ConfigurationClassBeanDefinitionReader
instanciada posteriormente.
Também podemos definir o processador de condição de classe de configuração por nós mesmos, apenas herdá-lo SpringBootCondition
e, 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.