笔记——SpringBoot启动配置类

前言:

        记录学习历程,在学习笔记中有描述不正确的地方,欢迎小伙伴们评论指正。

参考:

(转载)非常详细的SpringBoot-自动装配原理 - loveCode'shy - 博客园先看看SpringBoot的主配置类: 里面有一个main方法运行了一个run()方法,在run方法中必须要传入一个被@SpringBootApplication注解的类。 @SpringBootAphttps://www.cnblogs.com/hhcode520/p/9450933.html云道小破栈本站是大道七哥的技术分享博客。内容涵盖北漂程序员的生活故事、技术提升、Java后端技术、Spring Boot、 Spring Cloud、微服务架构、大数据演进、高可用架构、中间件使用、系统监控等相关的研究与知识分享。http://www.yund.tech/zdetail.html?type=1&id=2b2a032bd98dfeb5e4113ef6624722d2springboot启动类_仇人太多不方便透露姓名的博客-CSDN博客_springboot启动类我们开发任何一个Spring Boot项目,都会用到如下的启动类@SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}从上面代...https://blog.csdn.net/weixin_41688792/article/details/100839204

既然从头梳理学习,那么就从最基础的启动配置类开始吧,看了N篇的文章后,整理一下学习笔记.

正文:

创建好的SpringBoot工程都会有一个启动配置类。如下:

package learn.basics.learnbasics;

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

@SpringBootApplication
public class LearnBasicsApplication {

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

}

从上述代码中,首先比较惹人注意的就是启动配置类中的注解(Annotation)@SpringBootApplication和main方法中的SpringApplication.run()方法。

1、SpringApplication

        SpringApplication是SpringBoot驱动Spring应用上下文的引导类,它的run()方法是启动Spring应用,实际上是为Spring应用创建并初始化Spring上下文。

2、SpringApplication#run

SpringApplication的静态run()方法中,首先创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法

点进run方法可以看到如下源码:

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

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

其中primarySource就是我们的启动类LearnBasicsApplication。也就是说run()方法需要传入的其中第一个参数就是本身用@SpringBootApplication注解的启动配置类。然后调用第二个run()方法。

在第二个run()方法中,第一是通过构造函数创建SpringApplication对象;第二是调用其run()方法。

2.1、new SpringApplication(primarySources)

    public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

2.2、new SpringApplication(primarySources).run()

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

3、@SpringBootApplication

进入@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

3.1、@Target({ElementType.TYPE})

@Target:说明了注解(Annotation)所修饰的对象范围。作用与描述注解的使用范围。

取值ElementType有:
        1)CONSTRUCTOR:(constructor)用于描述构造器。
        2)FIELD:(field)用于描述域。
        3)LOCAL_VARIABLE:(local_variable)用于描述局部变量。
        4)METHOD:(method)用于描述方法。
        5)PACKAGE:(package)用于描述包。
        6)PARAMETER:(parameter)用于描述参数
        7)TYPE:(type)用于描述类、接口(包括注解类型)或枚举enum声明。

3.2、@Retention(RetentionPolicy.RUNTIME)

@Retention:生命周期。

        1)RetentionPolicy.SOURCE:注解只保留在源文件,当JAVA文件被编译成class文件时,注解被遗弃。
        2)RetentionPolicy.CLASS:注解只保留到class文件,当JVM加载class文件时被遗弃,也是默认的生命周期。
        3)RetentionPolicy.RUNTIME:注解不仅被保存在class文件中,当JVM加载class文件之后依然存在。

3.3、@Documented

@Documented:表明被javadoc工具记录,默认情况下,javadoc是不包含注解的,但如果声明注解时用了@Documented,则会被javadoc工具处理,故注解类型的信息也会被包含在生成的文档中,是一个标记注解,没有成员。

3.4、@Inherited

@Inherited:是一个标记,用来修饰注解。作用是如果一个类用了@Inherited,那么其子类也会集成这个注解。

注:接口(父类的方法)用了@Inherited,其实现类(子类)不会集成这个注解。

当用@Inherited修改的注解的@Retention是RetentionPolicy.RUNTIME,则增强继承性,在反射中可以获取得到。

3.5、@SpringBootConfiguration

进入@SpringBootConfiguration可以看到如下源码:

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

@Indexed可以为 Spring 的模式注解添加索引,以提升应用启动性能。

在源码中可以看到@SpringBootConfiguration其本质就是一个@Configuration

即:@SpringBootConfiguration = @Configuration + @EnableAutoConfiguration + @ComponentScan

3.5.1、@Configuration

@Configuration就是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。

3.5.2、@EnableAutoConfiguration

@EnableAutoConfiguration进去查看源码:

@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 {};
}

由上述代码可以看到,@EnableAutoConfiguration中的核心只有俩个注解@AutoConfigurationPackage(自动配置包)和@Import(导入自动配置的组件)。

@AutoConfigurationPackage进去后可以看到其本质是一个@Import。源码如下

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

由上述源码可以得到@AutoConfigurationPackage中用@Import给Spring容器中导入一个组件Registrar.class,通过Registrar.class的registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)方法来获取扫描的包路径。

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    }

@Import({AutoConfigurationImportSelector.class})注解给Spring组件导入了一个组件选择器AutoConfigurationImportSelector。
AutoConfigurationImportSelector里面有一个selectImports方法,将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

3.5.3、@ComponentScan

@ComponentScan注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值