【2期】SpringBoot的启动流程

SpringBoot的启动流程分析

一、分析启动类
1、@SpringBootApplication注解

这是一个复合注解,包含了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

(1)@SpringBootConfiguration
@SpringBootConfiguration:继承自@Configuration,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。@SpringBootConfiguration注解类相当于spring配置bean的xml文件。
关于@Configuration,请查看附录
(2)@EnableAutoConfiguration
@EnableAutoConfiguration:是复合注解.
使用@Import将所有符合自动配置条件的bean定义加载到Spring ioc中,并且将所有符合条件的@configuration配置都加载到当前spring ioc。
敢于@Import,请查看附录
(3)@ComponentScan
@ComponentScan:扫描当前包及其子包下被@Component,@Controller,@Service,@Repository注解标记的类并纳入到spring容器中进行管理。
所以SpringBoot的启动类最好放在项目的根目录下。
相当于在xml配置:<context:component-scan>
可以使用@ComponentScan 的basepackage等属性来指定扫描范围。例如:(@SpringBootApplication(scanBasePackages = "com.text.orderservice")
2、SpringApplication.run(XXX.class,args);方法

在创建的main方法中调用SpringApplication.run方法,第一个参数是当前启动类的class,第二个参数是main方法的args。

二、分析SpringApplication类

SpringApplication类的构造函数中调用

//实例化META-INF/spring.factories文件中的ApplicationContextInitializer类 
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//实例化META-INF/spring.factories文件中的ApplicationListener类 
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

SpringBoot实例化的5个ApplicationContextInitializer
在这里插入图片描述

SpringBoot实例化的9个ApplicationListener

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FqvfdIG7-1589252868141)(C:\Users\ky\Pictures\9个ApplicationListener.png)]

1、public ConfigurableApplicationContext run(String… args)

​ 从启动类的run方法点击进入SpringApplication,一直找到这个run方法

public ConfigurableApplicationContext run(String... args) {
	//1、创建并启动计时监控类
        StopWatch stopWatch = new StopWatch();
        //开始记录时间
        stopWatch.start();
    //2、初始化上下文、异常报告
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    //3、设置系统参数 'java.awt.headless'的值,默认值是true
        this.configureHeadlessProperty();
    //4、创建Spring运行监听器,内部只有一个EventPublishingRunListener
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //EventPublishingRunListener会向SpringApplication的所有ApplicationListener广播事件。 
        //事件有:ApplicationStartedEvent;ApplicationEnvironmentPreparedEvent;					//ApplicationPreparedEvent;ApplicationFailedEvent;ApplicationReadyEvent 
        // 上面分析过,会封装成SpringApplicationEvent事件然后广播出去给SpringApplication中的			//	listeners所监听
      // 这里接受ApplicationStartedEvent事件的listener会执行相应的操作
        listeners.starting();

        try {
    //5、初始化应用参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
   //6、根据运行监听器和应用参数来准备Spring环境
   //有StandardServletEnvironment() 和 StandardEnvironment()
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
   //配置要排除的bean
            this.configureIgnoreBeanInfo(environment);
   //7、创建Banner打印类
            Banner printedBanner = this.printBanner(environment);
   //8、创建应用的上下文,
   // 有SERVLET 和 REACTIVE 2种,默认是SERVLET
            context = this.createApplicationContext();
   //9、准备异常报告器,实例化SpringBootExceptionReporter,支持关于启动的错误
            this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
  //10、准备应用上下文
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  //11、刷新应用上下文
            this.refreshContext(context);
  //12、上下文创建完成之后执行额外一些操作
            this.afterRefresh(context, applicationArguments);
  //13、停止计时监控器
            stopWatch.stop();
  //14、输出日记记录执行主类名、时间信息
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
   //15、发布应用上下文启动完成事件
            listeners.started(context);
   //16、执行所有Runner运行期
            this.callRunners(context, applicationArguments);
        } catch (Throwable var9) {
            this.handleRunFailure(context, listeners, exceptionReporters, var9);
            throw new IllegalStateException(var9);
        }
   //17、发布应用上下文就绪事件
        listeners.running(context);
   //18、返回应用上下文
        return context;
    }

接下来我们分析run里面的启动内容

2、创建并启动计时监控类

StopWatch stopWatch = new StopWatch();
stopWatch.start();

​ 相关源码:

public void start() throws IllegalStateException {
//当前任务名称,默认为空字符串
       this.start("");
   }
   public void start(String taskName) throws IllegalStateException {
   //如果当前已经存在一个任务,抛出异常
       if (this.currentTaskName != null) {
           throw new IllegalStateException("Can't start StopWatch: it's already running");
       } else {
       //设置当前任务名称,并开始记录当前SpringBoot应用启动的开始时间
           this.currentTaskName = taskName;
           this.startTimeMillis = System.currentTimeMillis();
       }
   }

3、初始化应用上下文、异常报告集合

ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();

4、设置java.awt.headless的值

this.configureHeadlessProperty();
java.awt.headless的介绍请查看附录

5、创建Spring运行监听器并发布应用启动时间

SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
其中getRunListeners方法是去获取所有的监听器,代码如下:
private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }
   SpringApplicationRunListeners的代码如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

其中Set names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

是在调用Spring core 包中的SpringFactoriesLoader.loadFactoryNames方法, 在loadFactoryNames方法中,会找到SpringBoot的jar包下的【META-INF/spring.factories】文件,并读取其中的内容,里面配置了8个监听。

SpringApplicationRunListeners类和SpringApplicationRunListener类的介绍:

SpringApplicationRunListeners内部持有SpringApplicationRunListener集合和1个Log日志类。用于SpringApplicationRunListener监听器的批量执行。
SpringApplicationRunListener用于监听SpringApplication的run方法的执行。
它定义了5个步骤:
1. started(run方法执行的时候立马执行;对应事件的类型是ApplicationStartedEvent)
2. environmentPrepared(ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent)
3. contextPrepared(ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件)
4. contextLoaded(ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent)
5. finished(run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent)

SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener,它把监听的过程封装成了SpringApplicationEvent事件并让内部属性(属性名为multicaster)ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。

所以说SpringApplicationRunListener和ApplicationListener之间的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的
如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f6tLyA6h-1589252868142)(C:\Users\ky\Pictures\SpringApplicationEvent.png)]

附录

一、java.awt.headless模式
1.什么是java.awt.headless模式

java.awt.headless是J2SE的一种模式,用于在缺失显示屏、鼠标或者键盘时的系统配置。对于后端服务来讲,很多都是需要将这个属性设置为true的。

2.什么时候使用java.awt.headless模式

对于开发者来讲常常需要在该模式下工作。因为服务器(如提供Web服务的)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的、键盘和的主机)。

3.如何使用java.awt.headless模式

文章开头的springboot的源码已经展示了使用方式,就是使用 System.setProperty("java.awt.headless", Boolean headlessFlag);
二、@Configuration

Spring是给予IOC的,在4.0之前的版本,通常都是程序依赖上下文xml文件来管理bean,尽管有了扫描配置后相对简单,然而java配置的方式不同于xml,通过注解能够更简单。

@Configuration,该注解配置在类上,告知Spring这个类是一个拥有bean定义和依赖项的配置类。@Bean注释用于定义Bean,该注解位于实例化bean并设置依赖项的方法上。方法名默认通beanId活默认名称相同,该方法返回类型是Spring注册的bean。总体来说就是告诉Spring容器加载这个配置,相对于xml,这个注解就是将*.xml配置进web.xml

下面我们通过这两种方式比较

  • 1、xml中bean的定义
<beans>
    <bean id="userController" class="com.test.controller.UserController">
        <property name="userService" ref="userService"/>
    </bean>
    <bean id="userService" class="com.test.service.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="userDao" class="com.test.dao.UserDao" />
</beans>
  • 2、注解配置类

    
    @Configuration
    public class AppContext {
        @Bean
        public UserController userController() {
            UserController userController = new UserController();
            userController.setUserService(userService());
            return userController;
        }
        @Bean
        public UserService userService() {
            UserService userService = new UserService();
            userService.userDao(userDao());
            return userService;
        }
        @Bean
        public UserDao userDao() {
            return new UserDao();
        }
    }
    
三、@Import
  • @Import 与xml配置方式下的 作用一样。支持导入的类型有:
    一个或多个拥有 @Configuration 注解的配置类
  • ImportSelector 接口的实现类
  • ImportBeanDefinitionRegistrar 的实现类

1)、如果Import注解中Class为ImportSelector子类,通过invokeAwareMethods(selector)设置aware值,如果类型为 DeferredImportSelector则添加到deferredImportSelectors集合中,待前面的parser.parse(configCandidates)
方法中processDeferredImportSelectors()处理;如果不是,则执行selectImports方法,将获取到的结果递归调用 processImports,解析selectImports得到的结果

2)、如果Import注解中Class为ImportBeanDefinitionRegistrar子类,则添加到importBeanDefinitionRegistrars中,注 意该部分的数据在执行完parser.parse(configCandidates)后调用this.reader.loadBeanDefinitions(configClasses)解 析,否则执行配置信息的解析操作。

四、@EnableAspectJAutoProxy

表示开启AOP代理自动配置,如果配@EnableAspectJAutoProxy表示使用cglib进行代理对象的生成

五、@EnableScheduling

是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值