0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SpringBootApplication是什么

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-10-13 11:03 ? 次閱讀

概述

我們先來了解一下 @SpringBootApplication 是什么,以及如何在一個(gè)簡單的 Spring Boot 應(yīng)用程序中使用它。我們先看看 Spring Team 在源碼中對它的定義是什么?

Indicates a configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning. This is a convenience annotation that is equivalent to declaring @Configuration, @EnableAutoConfiguration and @ComponentScan.

表示一個(gè)配置類,它聲明了一個(gè)或多個(gè)@Bean方法,也觸發(fā)了自動(dòng)配置和組件掃描。這是一個(gè)方便的注解,相當(dāng)于聲明了@Configuration、@EnableAutoConfiguration和@ComponentScan。

從上面的定義我們可以看出,@SpringBootApplication 注解其實(shí)是一個(gè)組合注解。使用 @SpringBootApplication 相當(dāng)于同時(shí)使用了 @Configuration、@EnableAutoConfiguration@ComponentScan 。@SpringBootApplication 是在 Spring Boot 1.2.0 之后才開始有的,如何你的項(xiàng)目使用的 Spring Boot 1.2.0 之前的版本,那需要抱歉了,你不能使用這個(gè)注解,你只能完整的使用那 3 個(gè)注解來代替它。

那我們接下來看看,通過一個(gè)簡單的示例來看看怎么使用的。

@SpringBootApplication 示例

下面是一個(gè)簡單的例子,說明如何使用 @SpringBootApplication 注解來編寫 Spring Boot 應(yīng)用程序。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

Spring Boot 項(xiàng)目的啟動(dòng)類非常的簡潔,沒有一行多余的代碼。@SpringBootApplication 放在項(xiàng)目啟動(dòng)類上主要起到了自動(dòng)化配置的作用。下面我們看看 @SpringBootApplication 具體的代碼。

@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) })
public @interface SpringBootApplication {
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class< ? >[] exclude() default {};
    
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class< ? >[] scanBasePackageClasses() default {};
    @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
    Class< ? extends BeanNameGenerator > nameGenerator() default BeanNameGenerator.class;
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;
}

@SpringBootApplication 源碼可以看出 @SpringBootApplication = @SpringBootConfiguration + @ComponentScan+ @EnableAutoConfiguration 。

前面已經(jīng)提過了,@SpringBootApplication 是3個(gè)注解的組合,下面分別介紹一下每個(gè)注解都有什么作用吧。

@SpringBootConfiguration

這個(gè)注解將一個(gè)類標(biāo)記為基于 Java Config 的配置類。如果你喜歡基于 Java 的配置而不是基于 XML 的配置,這一點(diǎn)就特別重要。

@ComponentScan

該注解使組件掃描成為可能,這樣你創(chuàng)建的 Web 控制器類和其他組件將被自動(dòng)發(fā)現(xiàn),并在 Spring 應(yīng)用上下文中注冊為 Bean。你編寫的所有 @Controller 類將被該注解發(fā)現(xiàn)。

@EnableAutoConfiguration

這個(gè)注解可以啟用 Spring Boot 自動(dòng)配置功能。

如果你仔細(xì)的話會(huì)發(fā)現(xiàn)和前面講的不一樣, @SpringBootConfiguration 是從那里冒出來的,不是應(yīng)該是 @Configuration 嗎?下面就告訴你答案。

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;
}

從源碼可以看出,@SpringBootConfiguration 繼承自 @Configuration,二者功能也一致,標(biāo)注當(dāng)前類是配置類,不過 @SpringBootConfiguration 是一個(gè)特殊的標(biāo)記類,在項(xiàng)目中只能使用一次。

@ComponentScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
 @AliasFor("basePackages")
 String[] value() default {};
 @AliasFor("value")
 String[] basePackages() default {};
 Class< ? >[] basePackageClasses() default {};
 Class< ? extends BeanNameGenerator > nameGenerator() default BeanNameGenerator.class;
 Class< ? extends ScopeMetadataResolver > scopeResolver() default AnnotationScopeMetadataResolver.class;
 ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    ...
}

@ComponentScan 不做過多的解釋了,使用過 Spring 的朋友都懂的。其他的朋友我就啰嗦一句吧, 可以通過該注解指定掃描某些包下包含如下注解的均自動(dòng)注冊為 Spring beans@Component、@Service、 @Repository、 @Controller 等等注釋的類。Spring Boot 除了可以使用 @ComponentScan 注解來加載我們的bean,還可以使用 @Import 指定該類。

@EnableAutoConfiguration

@EnableAutoConfiguration 的作用啟動(dòng)自動(dòng)的配置,意思就是 Spring Boot 根據(jù)你添加的 jar 包來配置你項(xiàng)目的默認(rèn)配置,比如根據(jù) spring-boot-starter-web ,來判斷你的項(xiàng)目是否需要添加了 web mvctomcat,就會(huì)自動(dòng)的幫你配置 web 項(xiàng)目中所需要的默認(rèn)配置。簡單點(diǎn)說就是它會(huì)根據(jù)定義在 classpath 下的類,自動(dòng)的給你生成一些 Bean,并加載到 Spring 的上下文中。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class< ? >[] exclude() default {};
    String[] excludeName() default {};
}

從上述源碼中可以看到 @Import 引入了 AutoConfigurationImportSelector 類。AutoConfigurationImportSelector 使用了 Spring Core 包的 SpringFactoriesLoader#loadFactoryNames() 方法。AutoConfigurationImportSelector 類實(shí)現(xiàn)了 DeferredImportSelector 接口,并實(shí)現(xiàn)了 selectImports 方法,用來導(dǎo)出 Configuration 類。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List< String > configurations = getCandidateConfigurations(annotationMetadata, attributes);
        ...
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    protected boolean isEnabled(AnnotationMetadata metadata) {
        if (getClass() == AutoConfigurationImportSelector.class) {
            return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
        }
        return true;
    }
    protected List< String > getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        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;
    }
}

導(dǎo)出的類是通過 SpringFactoriesLoader#loadFactoryNames() 讀取了 classpath 下面的 META-INF/spring.factories 文件。

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    public static List< String > loadFactoryNames(Class< ? > factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    private static Map< String, List< String >> loadSpringFactories(ClassLoader classLoader) {
        Map< String, List< String >> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        result = new HashMap<  >();
        try {
            Enumeration< URL > urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry< ?, ? > entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    String[] factoryImplementationNames =
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                    for (String factoryImplementationName : factoryImplementationNames) {
                        result.computeIfAbsent(factoryTypeName, key - > new ArrayList<  >())
                                .add(factoryImplementationName.trim());
                    }
                }
            }
            // Replace all lists with unmodifiable lists containing unique elements
            result.replaceAll((factoryType, implementations) - > implementations.stream().distinct()
                    .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
            cache.put(classLoader, result);
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
        return result;
    }
}

META-INF/spring.factories 文件中一部分自動(dòng)配置的內(nèi)容:

# 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,
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,

如果你發(fā)現(xiàn)自動(dòng)裝配的 Bean 不是你想要的,你也可以 disable 它。比如說,我不想要自動(dòng)裝配 Database 的那些Bean:

@EnableAutoConfiguration(exclude  = {DataSourceAutoConfiguration.class})

這也是一種為 Spring Boot 項(xiàng)目瘦身的方法。你可以看到網(wǎng)上一些為項(xiàng)目瘦身的方法都是通過這個(gè)注解來操作的。

@AutoConfigurationPackage

@EnableAutoConfiguration 又繼承了 @AutoConfigurationPackage ,@AutoConfigurationPackage 會(huì)引導(dǎo)類(@SpringBootApplication 標(biāo)注的類)所在的包及下面所有子包里面的所有組件掃描到Spring容器。具體怎么實(shí)現(xiàn)的呢,我們來看代碼,原來它 import 了 AutoConfigurationPackages.Registrar.class, 我們來看看它做了什么?

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
    Class< ? >[] basePackageClasses() default {};
}
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }
    @Override
    public Set< Object > determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }
}

看代碼就很容易理解,把注解掃描進(jìn)來的 package 全部給注冊到 spring bean中。這樣 Spring Boot 的自動(dòng)配置也就完成了。

總結(jié)

經(jīng)過這樣的一番折騰,相信大家已經(jīng)對 @SpringBootApplication 注解,有了一定的了解。也知道了 @SpringBootApplication 怎么實(shí)現(xiàn) Spring Boot 的自動(dòng)配置功能。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    626

    瀏覽量

    28966
  • 應(yīng)用程序
    +關(guān)注

    關(guān)注

    37

    文章

    3198

    瀏覽量

    57360
  • 組件
    +關(guān)注

    關(guān)注

    1

    文章

    495

    瀏覽量

    17731
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    172

    瀏覽量

    145
收藏 人收藏

    評論

    相關(guān)推薦

    Spring Boot如何實(shí)現(xiàn)異步任務(wù)

    @EnableAsync 注解啟用異步功能; 需要在異步方法上添加 @Async 注解。 示例代碼如下: @SpringBootApplication @EnableAsync public class Applicat
    的頭像 發(fā)表于 09-30 10:32 ?1135次閱讀

    結(jié)合場景談一談微服務(wù)配置

    @SpringBootApplication@NacosPropertySource(dataId = "mysql.properties")public class SpringBootMySQLApplication {public
    發(fā)表于 12-12 15:53

    結(jié)合實(shí)際場景談一談微服務(wù)配置

    的目的。添加 @NacosPropertySource 注解@SpringBootApplication@NacosPropertySource(dataId?=?"mysql.properties
    發(fā)表于 12-12 15:56 ?130次閱讀

    Spring Boot的注解原理是什么

    @SpringBootApplication來看,發(fā)現(xiàn)@SpringBootApplication是一個(gè)組合注解。 @Target(ElementType.TYPE) @Retention
    的頭像 發(fā)表于 08-27 09:24 ?2118次閱讀

    Java注解及其底層原理解析 1

    什么是注解? 當(dāng)我們開發(fā)SpringBoot項(xiàng)目,我們只需對啟動(dòng)類加上`@SpringBootApplication`,就能自動(dòng)裝配,不需要編寫冗余的xml配置。當(dāng)我們?yōu)轫?xiàng)目添加lombok
    的頭像 發(fā)表于 02-09 14:18 ?655次閱讀
    Java注解及其底層原理解析 1

    Java注解及其底層原理解析2

    什么是注解? 當(dāng)我們開發(fā)SpringBoot項(xiàng)目,我們只需對啟動(dòng)類加上`@SpringBootApplication`,就能自動(dòng)裝配,不需要編寫冗余的xml配置。當(dāng)我們?yōu)轫?xiàng)目添加lombok
    的頭像 發(fā)表于 02-09 14:18 ?432次閱讀
    Java注解及其底層原理解析2

    SpringBoot的核心注解1

    今天跟大家來探討下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot為什么不需要XML,達(dá)到零配置
    的頭像 發(fā)表于 04-07 14:34 ?607次閱讀
    SpringBoot的核心注解1

    SpringBoot的核心注解2

    今天跟大家來探討下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot為什么不需要XML,達(dá)到零配置
    的頭像 發(fā)表于 04-07 14:34 ?1870次閱讀
    SpringBoot的核心注解2

    Spring Boot啟動(dòng) Eureka流程

    流程 @SpringBootApplication @EnableEurekaServer public class EurekaServer { public static void main
    的頭像 發(fā)表于 10-10 11:40 ?707次閱讀
    Spring Boot啟動(dòng) Eureka流程

    springboot核心注解

    幫助開發(fā)者簡化開發(fā)過程,提高開發(fā)效率。本文將深入介紹 Spring Boot 的核心注解及其使用方法。 @SpringBootApplication @SpringBootApplication
    的頭像 發(fā)表于 11-23 09:23 ?425次閱讀

    SpringBoot核心注解由幾個(gè)注解組成

    簡化應(yīng)用程序開發(fā)的注解,其中核心注解包括 @SpringBootApplication、@RestController、@RequestMapping、@Autowired、@ComponentScan
    的頭像 發(fā)表于 12-03 15:09 ?564次閱讀