zoukankan      html  css  js  c++  java
  • springboot源码解析-管中窥豹系列之Runner(三)

    一、前言

    • Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去。
    • 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot源码管中窥豹系列。

     简介

    二、Runner

    • 假如我们想在springboot项目启动完成之后,做点什么,我们应该怎么办呢?
    • 注意我们可以写在bean的初始化方法里面(我们后面讲),但是我们要用到其它已经加载了的bean的能力,又怎么办呢?
    • 当然加顺序,加依赖也能解决,就是麻烦

    这一节我们讨论一下springboot项目的Runner,Runner是在spring加载完毕执行的,springboot有两种Runner:

    • ApplicationRunner
    • CommandLineRunner
    @FunctionalInterface
    public interface ApplicationRunner {
    
    	void run(ApplicationArguments args) throws Exception;
    
    }
    
    @FunctionalInterface
    public interface CommandLineRunner {
    
    	void run(String... args) throws Exception;
    
    }
    
    
    • 两种除了参数不同,其它没区别
    • ApplicationArguments是对传参数组的封装,本质也没区别
    • 只有执行顺序上有区别,下面源码会看到

    三、用法

    实现接口就可以了

    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloRunner implements ApplicationRunner {
        
        @Override
        public void run(ApplicationArguments args) throws Exception {
            System.out.println("hello runner");
        }
    }
    
    • 因为这时候spring已经加载完毕,你可以引入其它bean
    • 启动项目,你会发现在日志最下方打印了上面的话

    四、源码解读

    我们直接找SpringApplication类的run方法,想看整体框架的去第一节。

    public ConfigurableApplicationContext run(String... args) {
    		
        ...
    
        try {
            
            ...
    
            callRunners(context, applicationArguments);
            
            ...
    
        }
        catch (Throwable ex) {
            
            ...
    
        }
    
        ...
    
        return context;
    }
    

    我们直接定位到callRunners方法。

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        // (1) 找到ApplicationRunner的实现类,加到list里面
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        // (2) 找到CommandLineRunner的实现类,加到list里面
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        // (3) 排序
        AnnotationAwareOrderComparator.sort(runners);
        // (4) 钩子回调
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
    

    总共分四步:

    • (1) 找到ApplicationRunner的实现类,加到list里面

    • (2) 找到CommandLineRunner的实现类,加到list里面

    • (3) 排序

    • (4) 钩子回调

    我们看一下canllRunner

    private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
        try {
            (runner).run(args);
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
        }
    }
    
    private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
        try {
            (runner).run(args.getSourceArgs());
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
        }
    }
    
    • 除了传参方式,都一样。
    • 上面说的执行顺序问题,是先添加的ApplicationRunner,如果只有@Component,先执行ApplicationRunner

    欢迎关注公众号:丰极,更多技术学习分享。

  • 相关阅读:
    PHP (20140519)
    PHP (20140516)
    js(20140517)在JS方法中返回多个值的三种方法
    PHP (20140515)
    PHP (20140514)
    Java内网发送邮件
    每日一“酷”之Cookie
    每日一“酷”之Queue
    每日一“酷”之pprint
    每日一“酷”之copy
  • 原文地址:https://www.cnblogs.com/zhangbin1989/p/14260778.html
Copyright © 2011-2022 走看看