Rumah  >  Artikel  >  Java  >  Apakah kod permulaan SpringBoot dan kod sumber pemasangan automatik?

Apakah kod permulaan SpringBoot dan kod sumber pemasangan automatik?

WBOY
WBOYke hadapan
2023-05-11 19:25:04716semak imbas

Dengan perkembangan pesat Internet, pelbagai komponen muncul tanpa henti, dan semakin banyak komponen memerlukan penyepaduan rangka kerja. Setiap komponen perlu melaksanakan kod yang berkaitan untuk disepadukan dengan bekas Spring. Konfigurasi rangka kerja SpringMVC terlalu rumit dan bergantung pada fail XML untuk memudahkan penyepaduan pantas komponen pihak ketiga dan mengurangkan pergantungan pada fail konfigurasi, SpringBoot wujud, yang mengguna pakai teori konvensyen berbanding konfigurasi supaya pembangun tidak memerlukannya; untuk mengkonfigurasi terlalu banyak untuk pembangunan. Spring, yang digunakan di bahagian bawah SpringBoot, menyepadukan pemasangan automatik N berbilang komponen secara lalai. Menggunakan SpringBoot adalah mudah, tambahkan @SpringBootApplication dalam kelas utama dan panggil SpringApplication.run() dan lulus dalam kelas utama. Kodnya adalah seperti berikut

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

Daripada kod sumber di atas, kita dapat melihat bahawa SpringApplication.run() ialah pintu masuk program SpringBoot. Artikel ini akan menganalisisnya dari dua aspek: SpringApplication.run() dan @SpringBootApplication anotasi.

1. Analisis baris utama kod permulaan SpringBoot

Kod utama dalam SpringApplication.run (StartApp.class) adalah untuk membuat kelas SpringApplication dahulu dan kemudian melaksanakan kaedah larian. Kodnya adalah seperti berikut,

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

1 Kod kaedah pembinaan SpringApplication adalah seperti berikut

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 设置资源加载器
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 设置应用主配置类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 获取web服务器类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 从spring.factories 文件中获取 ApplicationContextInitializer 的实现类
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 从spring.factories 文件中获取 ApplicationListener 监听器的实现类
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 设置main启动类
    this.mainApplicationClass = deduceMainApplicationClass();
}

Logik utama dalam kaedah pembinaan:

1 kelas konfigurasi aplikasi, yang dalam kaedah larian kemudiannya Ia akan dirangkumkan ke dalam BeanDefinitionHolder dan dimuatkan ke dalam pendaftaran konteks.

2. Dapatkan jenis pelayan web, yang akan digunakan dalam kaedah jalankan kemudian untuk mencipta jenis perkhidmatan web tertentu.

3. Dapatkan kelas pelaksanaan ApplicationContextInitializer daripada fail spring.factories dan tetapkan kepada instance SpringApplication

4. Dapatkan kelas pelaksanaan pendengar ApplicationListener daripada fail spring.factories dan tetapkannya kepada instance SpringApplication

5. Tetapkan kelas permulaan utama

Logik utama kaedah getSpringFactoriesInstances ialah: dapatkan rentetan kelas pelaksanaan tertentu daripada fail META-INF/spring.factories mengikut ke antara muka, dan letakkan rentetan Jadikan instantiated sebagai objek. Kodnya adalah seperti berikut,

// 获取类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 根据type 从META-INF/spring.factories获取 具体的实现类字符串列表
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化具体的实现类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;

berada dalam fail META-INF/spring.factories Rentetan kelas pelaksanaan yang sepadan bagi ApplicationContextInitializer.class ialah,

org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

berada dalam META-INF/. fail spring.factories ApplicationListener. Rentetan kelas pelaksanaan yang sepadan dengan kelas ialah,

org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

2. Kod kaedah larian adalah seperti berikut, logik utama dalam kaedah

StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置了一个名为 java.awt.headless 的系统属性
// 其实是想设计应用程序,即使没有检测到显示器,也允许其启动
// 对于服务器来说,是不需要显示器的 ,所以要这样设置
configureHeadlessProperty();
// 获取 SpringApplicationRunListener 加载的是 EventPublishingRunListener
// 获取启动时的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 触发启动事件
listeners.starting();
try {
    // 构造一个应用程序的参数持有类
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // 创建并配置环境
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    // 配置需要忽略的BeanInfo信息
    configureIgnoreBeanInfo(environment);
    Banner printedBanner = printBanner(environment);
    // 创建上下文对象
    context = createApplicationContext();
    // 加载配置的启动异常处理器
    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                     new Class[] { ConfigurableApplicationContext.class }, context);
    // 刷新前操作
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    // 刷新应用上下文 完成 Spring 容器的初始化
    refreshContext(context);
    // 刷新后操作
    afterRefresh(context, applicationArguments);
    stopWatch.stop();
    if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    }
    // 启动完成事件
    listeners.started(context);
    // 执行 ApplicationRunner 和 CommandLineRunner 实现类
    callRunners(context, applicationArguments);
}
catch (Throwable ex) {
    // 事件广播启动出错了
    handleRunFailure(context, ex, exceptionReporters, listeners);
    throw new IllegalStateException(ex);
}

try {
    // 运行事件
    listeners.running(context);
}
catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, null);
    throw new IllegalStateException(ex);
}
return context;

:

1. Daripada fail spring.factories Dapatkan kelas pelaksanaan SpringApplicationRunListener (penerbit acara mendengar) dan laksanakan peristiwa yang berkaitan dalam kitaran hayat konteks, seperti mencetuskan acara permulaan, acara penyiapan permulaan, dsb.

2. Cipta objek konteks aplikasi Web dan cipta jenis perkhidmatan web tertentu berdasarkan webApplicationType.

3. Sebelum memuat semula, masukkan sumber kelas konfigurasi utama ke dalam BeanDefinitionHolder dan muatkannya ke dalam pendaftaran konteks.

4. Muat semula konteks aplikasi untuk melengkapkan pemulaan bekas Spring.

5. Kelas Pelaksanaan yang melaksanakan antara muka ApplicationRunner dan CommandLineRunner.

2. Analisis prinsip pemasangan automatik SpringBoot

1 Pra-pengetahuan pemasangan automatik @Import

@SpringBootApplication anotasi, yang menggunakan anotasi @Import. Import kod sumber adalah seperti berikut :

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

Anotasi @Import biasanya digunakan bersama-sama dengan @Configuration Kelas anotasi @Configuration akan dihuraikan semasa proses pemulaan bekas Spring (kod sumber berada dalam org. springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions). Lokasi kod sumber: org.springframework.context.annotation.ConfigurationClassParser#processImports; Import:

1. Senario Dilaksanakan antara muka ImportSelector.class; panggil terus kaedah selectImports bagi pemilih contoh untuk mengembalikan senarai nama kelas penuh objek Bean untuk dijadikan instantiated, dan mencipta objek contoh berdasarkan pada senarai rentetan nama kelas penuh, dan kemudian secara rekursif memanggil kaedah processImports semasa, yang akhirnya akan ditambahkan Pada koleksi configurationClasses, objek dalam koleksi configurationClasses akan didaftarkan ke objek pendaftaran jenis BeanDefinitionRegistry. Dalam kes ini, antara muka ImportSelector dilaksanakan dan antara muka DeferredImportSelector dilanjutkan Antara muka ini digunakan untuk melaksanakan fungsi suntikan tertunda BeanDefinition. Antara muka DeferredImportSelector memanjangkan antara muka ImportSelector dan mempunyai antara muka dalaman Kumpulan Jika kelas nilai beranotasi dengan @Import melaksanakan antara muka DeferredImportSelector dan juga melaksanakan antara muka Kumpulan kelas dalaman bagi antara muka, nampaknya kelas pelaksanaan ini perlu ditangguhkan. Jika pemprosesan tertunda diperlukan, pemilih contoh ImportSelector akan dipasang ke dalam objek DeferredImportSelectorHolder dan ditambahkan pada koleksi tertundaImportSelectors Lokasi kod sumber logik: org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler; berikut,

try {
    for (SourceClass candidate : importCandidates) {
        if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            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) {
                this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
            }
            else {
                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
            }
        }
        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // Candidate class is an ImportBeanDefinitionRegistrar ->
            // delegate to it to register additional bean definitions
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                                     this.environment, this.resourceLoader, this.registry);
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
            // process it as an @Configuration class
            this.importStack.registerImport(
                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
}

Logik pelaksanaan antara muka DeferredImportSelector akan dipanggil dalam kaedah org.springframework.context.annotation.ConfigurationClassParser#parse adalah seperti berikut,

public void process() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            deferredImports.forEach(handler::register);
            // 具体的执行逻辑
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

在processGroupImports()方法中,先通过grouping.getImports()拿到需要自动装配的Group.Entry(封装了全类名)对象集合,然后通过processImports()方法根据Entry类名字符串进行创建SourceClass类(该类可以通过asConfigClass()方法转成ConfigurationClass对象),最终添加到configurationClasses集合中。代码如下,

public void processGroupImports() {
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        grouping.getImports().forEach(entry -> {
            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()方法中主要执行具体的实现类的process方法和selectImports()方法(如果是AutoConfigurationImportSelector类,则调用org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process和org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports,两个方法的具体类容请看2.2.2章节的说明),selectImports返回需要自动装配的Group.Entry对象集合,Entry对象中保存了全类名。代码如下:

public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                           deferredImport.getImportSelector());
    }
    return this.group.selectImports();
}

ImportSelector接口代码代码如下:

public interface ImportSelector {
	String[] selectImports(AnnotationMetadata importingClassMetadata);
	@Nullable
	default Predicate<String> getExclusionFilter() {
		return null;
	}
}

DeferredImportSelector接口的代码如下:

public interface DeferredImportSelector extends ImportSelector {

    @Nullable
    default Class<? extends Group> getImportGroup() {
        return null;
    }
    interface Group {
        void process(AnnotationMetadata metadata, DeferredImportSelector selector);
        Iterable<Entry> selectImports();
        class Entry {
			// 省略
        }
    }
}

2.实现了 ImportBeanDefinitionRegistrar.class接口的场景;会先创建ImportBeanDefinitionRegistrar 实例类 registrar,再把 registrar 添加到 configClass 的 importBeanDefinitionRegistrars中,接口的registerBeanDefinitions方法的调用是在 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法里的this.reader.loadBeanDefinitions(configClasses)代码中。具体执行语句loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());关键代码如下,

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    registrars.forEach((registrar, metadata) ->
                       registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

ImportBeanDefinitionRegistrar接口代码如下:

public interface ImportBeanDefinitionRegistrar {

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
                                         BeanNameGenerator importBeanNameGenerator) {
        registerBeanDefinitions(importingClassMetadata, registry);
    }

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}

3.没有实现以上两接口的普通类,会直接调用org.springframework.context.annotation.ConfigurationClassParser#processImports里面的processConfigurationClass方法,把当前configClass添加至 configurationClasses 集合中。configurationClasses集合中的对象最终会被注册到BeanDefinitionRegistry类型的 registry 对象中。

2.@SpringApplication注解分析

@SpringApplication注解主要包括了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。代码如下,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
2.1@SpringBootConfiguration

配置注解,包含了@Configuration注解,表明是配置类。

2.2@EnableAutoConfiguration

自动装配注解,主要逻辑是:根据 EnableAutoConfiguration 类型从META-INF/spring.factories 文件加载需要自动装配的类,并注入到Spring容器中。它包括了@AutoConfigurationPackage注解和一个@Import(AutoConfigurationImportSelector.class)注解。代码如下,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
2.2.1@AutoConfigurationPackage

注册名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages ,BeanClass为BasePackages.class 的GenericBeanDefinition 到 BeanDefinitionRegistry 中,通过@Import 注解实现注入功能,代码如下,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

AutoConfigurationPackages.Registrar.class实现了ImportBeanDefinitionRegistrar接口 ,所以在Spring容器初始化的过程中会调用它的registerBeanDefinitions方法把PackageImport类注入到Spring容器中去。代码如下,

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImport(metadata).getPackageName());
    }
    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImport(metadata));
    }
}
2.2.2@Import(AutoConfigurationImportSelector.class)

自动装配关键逻辑,先从META-INF/spring.factories 文件加载类型值为 EnableAutoConfiguration的字符串集合,再通过过滤,生成需要自动装配的类,最后注入到Spring容器中。AutoConfigurationImportSelector实现了DeferredImportSelector接口并且内部也实现了DeferredImportSelector.Group接口,所以在Spring容器初始化的过程中会调用

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process方法和#selectImports方法,

process()用来生成需要自动装配的类型,方法的代码如下,

Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
             () -> String.format("Only %s implementations are supported, got %s",
                                 AutoConfigurationImportSelector.class.getSimpleName(),
                                 deferredImportSelector.getClass().getName()));
// 1. getAutoConfigurationMetadata()
// 从META-INF/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据,
// 里面保存了加载类是否自动装配的条件 ,
// org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration.ConditionalOnBean
// =javax.jms.ConnectionFactory
// 2. getAutoConfigurationEntry()
// 从 META-INF/spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串类表 并封装成 自动装配类对象
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
    .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 循环遍历 自动装配类对象 的自动装配类字符串 ,添加到 this.entries
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    this.entries.putIfAbsent(importClassName, annotationMetadata);
}

getAutoConfigurationMetadata() 方法主要逻辑是:从META-INF/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据,里面保存了自动加载类是否符合自动装配的前置条件,比较熟悉的有ConditionalOnClass和ConditionalOnBean,文件相关内容如下:

org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.security.oauth3.client.servlet.OAuth3ClientAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration=
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration.ConditionalOnWebApplication=REACTIVE
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=REACTIVE
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.security.oauth3.resource.servlet.OAuth3ResourceServerAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration.ConditionalOnWebApplication=SERVLET
//省略

getAutoConfigurationEntry()方法 主要逻辑是:从spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串列表并封装成自动装配类AutoConfigurationEntry对象,代码如下,

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                                                           AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 获取注解元数据的属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 从spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串列表
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 去掉重复的 自动装配类字符串
    configurations = removeDuplicates(configurations);
    // 根据注解元数据获取 需要排除的类名
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // 检查排除的类名
    checkExcludedClasses(configurations, exclusions);
    // 根据排除的类名进行排除
    configurations.removeAll(exclusions);
    // 从spring.factories 文件中获取key为 AutoConfigurationImportFilter 的配置对象进行过滤
    // 过滤规则从 getAutoConfigurationMetadata() 返回类的数据中获取
    configurations = filter(configurations, autoConfigurationMetadata);
    // 执行导入配置类的监听事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 返回 AutoConfigurationEntry 对象
    return new AutoConfigurationEntry(configurations, exclusions);
}

getCandidateConfigurations()方法从spring.factories 文件中获取类型为 EnableAutoConfiguration 的配置类字符串列表,代码如下,

// getSpringFactoriesLoaderFactoryClass()方法返回 EnableAutoConfiguration
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;

在META-INF/spring.factories文件中EnableAutoConfiguration .class 对应的实现类字符串为

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
// 省略

2.selectImports()方法返回排序后的 Entry(需要自动装配的包装实体类) 对象集合,代码如下,

public Iterable<Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
        return Collections.emptyList();
    }
    Set<String> allExclusions = this.autoConfigurationEntries.stream()
        .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
    Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
        .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
        .collect(Collectors.toCollection(LinkedHashSet::new));
    processedConfigurations.removeAll(allExclusions);
    // 返回排序后的 Entry 集合
    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
        .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
        .collect(Collectors.toList());
}

注意:@EnableAutoConfiguration 注解的分析过程需要结合@Import注解的过程来看。

2.2.3@ComponentScan

Anotasi pengimbasan komponen digunakan untuk mengkonfigurasi laluan pakej pengimbasan automatik. Jika tiada laluan konfigurasi, semua pakej dan kelas di bawah ruang nama kelas konfigurasi utama diimbas.

Atas ialah kandungan terperinci Apakah kod permulaan SpringBoot dan kod sumber pemasangan automatik?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam