本文来自网易云社区

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

入口类

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

  1. <packaging>jar</packaging>

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

  1. java -jar xxx.jar

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

  1. nohup java -jar xxx.jar &

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

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

  1. @SpringBootApplicationpublic class BlogDemoApplication {    public static void main(String[] args) {
  2.         SpringApplication.run(BlogDemoApplication.class, args);
  3.     }
  4. }

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

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

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

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

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

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

接着看实例的run方法:

  1. /**
  2.  * Run the Spring application, creating and refreshing a new
  3.  * {@link ApplicationContext}.
  4.  * @param args the application arguments (usually passed from a Java main method)
  5.  * @return a running {@link ApplicationContext}
  6.  */public ConfigurableApplicationContext run(String... args) {    // 计时工具
  7.     StopWatch stopWatch = new StopWatch();
  8.     stopWatch.start();    // 应用上下文
  9.     ConfigurableApplicationContext context = null;
  10.     Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  11.     configureHeadlessProperty();    // 设置系统参数-无图形化界面
  12.     // 获取监听器
  13.     SpringApplicationRunListeners listeners = getRunListeners(args);
  14.     listeners.starting();    try {
  15.         ApplicationArguments applicationArguments = new DefaultApplicationArguments(
  16.             args);
  17.         ConfigurableEnvironment environment = prepareEnvironment(listeners,
  18.                                                                  applicationArguments);
  19.         configureIgnoreBeanInfo(environment);
  20.         Banner printedBanner = printBanner(environment);        // 创建上下文
  21.         context = createApplicationContext();
  22.         exceptionReporters = getSpringFactoriesInstances(
  23.             SpringBootExceptionReporter.class,            new Class[] { ConfigurableApplicationContext.class }, context);        // 上下文前置处理,这里会解析我们传入的入口类
  24.         prepareContext(context, environment, listeners, applicationArguments,
  25.                        printedBanner);        // 刷新上下文
  26.         refreshContext(context);        // 后置处理
  27.         afterRefresh(context, applicationArguments);
  28.         stopWatch.stop();        if (this.logStartupInfo) {            new StartupInfoLogger(this.mainApplicationClass)
  29.                 .logStarted(getApplicationLog(), stopWatch);
  30.         }
  31.         listeners.started(context);
  32.         callRunners(context, applicationArguments);
  33.     }    catch (Throwable ex) {
  34.         handleRunFailure(context, listeners, exceptionReporters, ex);        throw new IllegalStateException(ex);
  35.     }
  36.     listeners.running(context);    return context;
  37. }

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

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

@SpringBootApplication

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

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

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

@ComponentScan

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

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

@SpringBootConfiguration

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

  1. @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {
  2.  
  3. }

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

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

@EnableAutoConfiguration

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

小结

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

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

相关阅读:SpringBoot入门(一)——开箱即用

SpringBoot入门(二)——起步依赖

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

SpringBoot入门(四)——自动配置

SpringBoot入门(五)——自定义配置

网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者金港生授权发布。

SpringBoot入门(三)——入口类解析的更多相关文章

  1. 尚硅谷springboot学习5-主入口类说明

    package com.atguigu; import org.springframework.boot.SpringApplication; import org.springframework.b ...

  2. SpringBoot入门02-配置类

    引入 Spring Boot的底层已经有了Spring MVC Spring Boot习惯优先的思想,很多配置都是可省的 不需要配置web.xml文件 不需要服务层的xml配置 不需要dao层的xml ...

  3. springboot 入门三- 读取配置信息二(读取属性文件方式)

    在上篇文章中简单介绍自带读取方式.springboot提供多种方式来读取 一.@ConfigurationProperties(value="my") 支持更灵活的绑定及元数据的支 ...

  4. SpringBoot入门 (三) 日志配置

    上一篇博文记录了再springboot项目中读取属性文件中配置的属性,本文学习在springboot项目中记录日志. 日志记录在项目中是很常见的一个功能了,对排查问题有很大帮助,也可以做分类分析及统计 ...

  5. SpringBoot入门(五)——自定义配置

    本文来自网易云社区 大部分比萨店也提供某种形式的自动配置.你可以点荤比萨.素比萨.香辣意大利比萨,或者是自动配置比萨中的极品--至尊比萨.在下单时,你并没有指定具体的辅料,你所点的比萨种类决定了所用的 ...

  6. SpringBoot入门(四)——自动配置

    本文来自网易云社区 SpringBoot之所以能够快速构建项目,得益于它的2个新特性,一个是起步依赖前面已经介绍过,另外一个则是自动配置.起步依赖用于降低项目依赖的复杂度,自动配置负责减少人工配置的工 ...

  7. SpringBoot入门(二)——起步依赖

    本文来自网易云社区 在前一篇我们通过简单几步操作就生成了一个可以直接运行的Web程序,这是因为SpringBoot代替我们做了许多工作,概括来讲可以分为起步依赖和自动配置.这一篇先来看看起步依赖. 项 ...

  8. SpringBoot入门(一)——开箱即用

    本文来自网易云社区 Spring Boot是什么 从根本上来讲Spring Boot就是一些库的集合,是一个基于"约定优于配置"的原则,快速搭建应用的框架.本质上依然Spring, ...

  9. SpringBoot入门学习看这一篇就够了

    1.SpringBoot是什么? SpringBoot是一套基于Spring框架的微服务框架. 2.为什么需要SpringBoot 由于Spring是一个轻量级的企业开发框架,主要的功能就是用于整合和 ...

随机推荐

  1. python:一个轻松的递归逻辑

    #递归 age = 10 def dig(n): global age#函数dig引用全局变量age age += 2 n -= 1 if n != 1:#如果满足条件,则调用本身 dig(n) di ...

  2. Python nonlocal 与 global 关键字解析

    nonlocal 首先,要明确 nonlocal 关键字是定义在闭包里面的.请看以下代码: x = 0 def outer(): x = 1 def inner(): x = 2 print(&quo ...

  3. delphi三层架构

    我们的delphi程序很多是以前开发的,采用典型的CS架构,由程序直接连接数据库.现在需要改成在外网可以直接操作软件.先把数据库搬到了阿里云上,测试发现直接连数据库和VPN连接测试速度很慢,直连还容易 ...

  4. C# Attribute的用法

         {                   {                            Attribute[] atts = Attribute.GetCustomAttribut ...

  5. SQL select 执行顺序

    一.sql语句的执行步骤:1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义.2)语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限.3)视图转换,将涉及视图的查询语 ...

  6. Perl 变量:数组变量

    Perl 数组Perl 数组一个是存储标量值的列表变量,变量可以是不同类型.数组变量以 @ 开头.访问数组元素使用 $ + 变量名称 + [索引值] 格式来读取. 1.创建列表.数组1.数组变量以 @ ...

  7. poj2796 Feel good

    题目给出N个数,找出一段区间使得区间最小值乘区间和的值最大 其中N<=100000 分析: 单调队列(单调栈) 求出每个值作为最小值时最长的影响区间,然后枚举判断 这找出最长影响区间应该算是单调 ...

  8. TCP与UDP与HTTP协议

    http:是用于www浏览的一个协议.tcp:是机器之间建立连接用的到的一个协议. 1.TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层.在网络层有IP协议.ICMP协议.ARP协议.R ...

  9. 764. Largest Plus Sign最大的dfs十字架

    [抄题]: 求挖掉一些区域后,能允许出现的最大十字架 In a 2D grid from (0, 0) to (N-1, N-1), every cell contains a 1, except t ...

  10. tftp-hpa客户端使用说明

    1.板子 sudo apt-get install tftp-hpa 2.主机chmod 777 tftp—dir 3.tftp -4 192.168.1.122 -c put lib2.tar.gz ...