SpringBoot入门(三)——入口类解析

SpringBoot 2.x入门书籍下载: https://t00y.com/dir/22083442-36337099-378043

 

上一篇介绍了起步依赖,这篇我们先来看下SpringBoot项目是如何启动的。

 

入口类

 

再次观察工程的Maven配置文件,可以看到工程的默认打包方式是jar格式的。

 

<packaging>jar</packaging>

 

SpringBoot默认的打包方式为jar,并且内嵌web容器。因此我们可以用运行jar包的方式启动一个web程序:

 

java -jar xxx.jar

 

linux服务器上可以用下面命令让服务常驻:

 

nohup java -jar xxx.jar &

 

我们知道jar包方式运行需要main方法,SpringBoot已为我们自动生成,这个类便是项目启动入口。

 

我的项目名是blog-demo,对应生成的main方法在BlogDemoApplication.java,其代码如下:

 

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

 

main方法中执行SpringApplication的静态方法run,并将当前类和启动参数传入。

 

静态方法中实例化一个SpringApplication,并调用实例的run方法:

 

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

 

先来看下调用的SpringApplication的构造方法:

 

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 这里的primarySources就是我们传入的入口类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推断应用类型
    this.webApplicationType = deduceWebApplicationType();
    // 设置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    // 设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 很有意思的方法,通过异常栈获取应用入口类
    this.mainApplicationClass = deduceMainApplicationClass();
}

 

注意我们传入的启动类被保存到了primarySources变量中,将作为后续context加载beans时的资源,其他细节不再展开。

接着看实例的run方法:

 

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
    // 计时工具
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 应用上下文
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();    // 设置系统参数-无图形化界面
    // 获取监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                                                 applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        // 创建上下文
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
        // 上下文前置处理,这里会解析我们传入的入口类
        prepareContext(context, environment, listeners, applicationArguments,
                       printedBanner);
        // 刷新上下文
        refreshContext(context);
        // 后置处理
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, exceptionReporters, ex);
        throw new IllegalStateException(ex);
    }
    listeners.running(context);
    return context;
}

 

通过方法注释也可以看出来该run方法引发了一系列复杂的内部调用和加载过程,从而创建了一个SpringContext。

 

在prepareContext方法中会解析我们传入的入口类,解析其上的注解。下面来看下入口类上的注解。

@SpringBootApplication

 

入口类上的注解@SpringBootApplication是SpringBoot自动配置的关键。其定义如下:

 

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

 

说明它是@ComponentScan、@SpringBootConfiguration和@EnableAutoConfiguration三个注解的组合。

 

@ComponentScan

 

@ComponentScan是Spring框架原有的注解,在spring-context组件下,用来开启自动扫描Bean并解析注解注入。

 

可以用basePackages指定扫描的包,缺省情况下默认扫描被注解类所在的包。SpringBoot项目中一般会将入口类放在顶层目录,这样默认就会扫描整个项目。

 

@SpringBootConfiguration

 

@SpringBootConfiguration是SpringBoot新增的注解,在spring-boot组件下,定义如下:

 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

 

相当于注解@Configuration,配备了该注解的类就能够以JavaConfig的方式完成一些配置,可以不再使用XML配置。

 

所以在入口类内也可以以JavaConfig的方式定义Bean。

 

@EnableAutoConfiguration

 

@EnableAutoConfiguration是SpringBoot新增的注解,在spring-boot-autoconfigurate组件下,它是SpringBoot开启自动配置的关键。放到下一节再讲。

小结

 

这一节简单解析了SpringBoot的入口类,一个由SpringBoot自动生成的java类,虽然只有短短几行代码,却引发了Spring上下文的创建的一系列事件。

 

首先SpringBoot将入口类传入作为资源的起点,当解析到入口类的时候发现其上的注解又开启了自动配置和包扫描,这样我们自定义的Bean就会被加载进去完成创建和依赖。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值