java学习之路

spring boot 源码解析(七) springBoot原理

2020-12-15  本文已影响0人  唯有努力不欺人丶

其实关于这块我是觉得翻来覆去的讲了好多遍了,尤其是开头讲的启动,自动配置之类的,不过课程这么设置也应该有它自己的原理。下面让我们按照教程一步一步学习了解SpringBoot的启动原理。

SpringBoot启动原理

因为之前我自己一步一步往下找走过这个,但是很多方法都是临时百度或者连蒙带猜的,这里老师一步一步讲解能让思路更清晰。我这里用图文并茂的方式记录下。

  1. 在启动类中的run方法


    启动类中启动
  2. 点进去发现本质是创建SpringApplication对象并运行run方法
    创建SpringApplication对象并运行run方法
    2.1 . 创建SpringApplication对象过程
@SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //这句代码是判断当前应用是不是web应用
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //获取类路径META-INF/spring.factories下配置的所有ApplicationContextInitlalizer保存
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //获取类路径META-INF/spring.factories下配置的所有ApplicationListener保存
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 从多个配置类中找到有main方法的主配置类。
        this.mainApplicationClass = deduceMainApplicationClass();
    }
WebApplicationType源码
获取配置文件中的类
这一块springBoot自动注入的时候看过
创建SpringApplication

2.2. 运行run方法:


运行run方法
下面一步一步分析源码:
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
         //据说这个注解以上都是jwt的东西,从这个注解往下开始分析
        //获取类路径/META-INF/spring.factories下所有的SpringApplicationRunListeners
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //回调所有SpringApplicationRunListener的starting方法
        listeners.starting();
        try {
        //封装命令行参数。
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
        //控制台打印这个spring的图标及版本信息
            Banner printedBanner = printBanner(environment);
        //创建applicationContext。在这里会决定创建web容器还是普通的。2.x版本又加了个reactive容器
            context = createApplicationContext();
        //1.x版本没这句,但是看代码可以猜一下,应该是/META-INF/spring.factories下所有的异常报告?
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
        //准备上下文环境,将environment保存到ioc中。这个方法中有两个方法:
        //applyInitializers(context)回调所有2.1中保存的ApplicationContextInitlalizer的initialize方法。                
        //listeners.contextPrepared(context):回调所有SpringApplicationRunListener的contextPrepared方法。
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //在这个方法的最后回调所有的SpringApplicationRunListener的contextLoaded方法。
        //刷新容器:IOC容器初始化,如果是web应用,还会创建嵌入式的tomcat
        //扫描,创建,加载所有组件的地方(配置类,组件,自动配置)
            refreshContext(context);
        //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。(注意,这里ApplicationRunner先于CommandLineRunner回调)
            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, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

至此,这个springBoot项目算是启动了。
其实这里听的云里雾里的,毕竟好多延伸出更多的东西。尽量理解吧。

事件监听机制

配置在META-INF/spring.factories:

只需要放在IOC容器中:

这些都是组件。我们在代码中写好,要配置在指定的位置。后两个只要注入bean纳入spring管理就行了,比较简单,而前两个要写在配置文件中,我们可以找一个现成的看看人家怎么写的,参考一下:


怎么配置ApplicationContextInitlalizer和SpringApplicationRunListener

然后我们照着自己写一下(这里四个实现类就不说了,就是自己建个类分别继承上面四个接口,方法只要写打印语句我们看看什么时候输出就行了):


spring.factories中代码 输出顺序如下

启动项目会发现输出顺序如上图。
因为我们方法中都是输出语句所以显着比较简单,但是实际工作中可以在这个方法中写各种逻辑代码的。挺实用一个功能。

SpringBoot场景启动器starter

自定义starter:


一些用得到的经验:


这些经验说完了,下面简单说一下spring 中启动器的常规用法:

总结一下:启动器只用来做依赖导入。 专门写一个自动配置模块。启动器依赖自动配置,别人只需要引入启动器
接下来自己写一个简单的启动器:
首先大概说一下,如果按照官方规范的来做,这个demo最少三个项目:

所以这里也是直接三个项目(因为我是eclipse,所以同时创建三个,hiahia)


如下目录结构

这里面starter里啥也没有就是引用了autoConfigure依赖而已,一个java类都莫得,所以不用多说了。
然后第一个假装是自己项目。用啥引入啥就行。也没啥特别的。
最主要的要说一下这个自动配置项目。
实现了自动配置的功能,而且还是动态的。具体怎么实现的呢?其实简单来说就是一个配置属性类xxxProperties。一个配置类xxxAutoConfiguration。
最后这个自动配置是根据自动扫描META-INF/spring.factories里的类名实现的。
下面附上实现的代码:


配置属性绑定类

这个类就是用来绑定配置文件中的属性的。


用到这种配置属性的类
注意这个类上没有任何注解!因为所有的注入都统一用自动配置类完成
自动配置类
这个类把之前的配置属性类和那个service都注入进来了。接下来重点是怎么让这个类自动执行呢?META-INF/spring.factories
这个可以参照spring的示例写

至此这个自动配置就完成了,然后把starter中引入这个依赖。再在我们的项目中引入starter依赖就完事了。
接着我们就可以测试一下啦:

配置我们之前自动注入类中需要的配置
注入我们需要用到的service
然后接口访问:
测试成功!配置参数动态注入成功!
到了这里我们想要的效果就实现啦。这个代码主要就是为了实现这个思路,功能比较浅薄。但是我们举一反三,就知道为什么spring boot只配置一些重要参数就能启动一些东西啦!
本篇笔记就记到这里,如果稍微有帮到你记得点个喜欢点个关注!这一篇其实都是原理啊,底层啊之类的东西,挺好的,不见得学了能立竿见影的看到进步,可是拓宽思路和原理,尤其是知道底层对一些问题的分析也更容易。而且这篇算是我看的教程的springboot基础篇的结束了,下面就要讲一些整合中间件的教程了!感觉学了快一个月,现在翻源码颇有心得了,对spring boot 起码有个浅显的认识而不是之前只会使用啦,每天进步一点点~~我们共勉!
上一篇下一篇

猜你喜欢

热点阅读