1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        SpringBoot總結(jié)之CommandLineRunner

        共 9312字,需瀏覽 19分鐘

         ·

        2021-06-02 09:34

        筆者基于目前業(yè)務需求需要提前將部分數(shù)據(jù)加載到Spring容器中。大家可以想一下解決方案,下面評論去留言。筆者能夠想到的解決方案:

        1、 定義靜態(tài)常量,隨著類的生命周期加載而提前加載(這種方式可能對于工作經(jīng)驗較少的伙伴,選擇是最多的);

        2、 實現(xiàn)CommandLineRunner接口;容器啟動之后,加載實現(xiàn)類的邏輯資源,已達到完成資源初始化的任務;

        3、 @PostConstruct;在具體Bean的實例化過程中執(zhí)行,@PostConstruct注解的方法,會在構(gòu)造方法之后執(zhí)行;

        加載順序為:Constructor > @Autowired > @PostConstruct > 靜態(tài)方法;

        特點:

        • 只有一個非靜態(tài)方法能使用此注解
        • 被注解的方法不得有任何參數(shù)
        • 被注解的方法返回值必須為void
        • 被注解方法不得拋出已檢查異常
        • 此方法只會被執(zhí)行一次

        4、 實現(xiàn)InitializingBean接口;重寫afterPropertiesSet()方法;

        以上方案供大家參考,提供一種解決思路。但是日常開發(fā)中有可能需要實現(xiàn)在項目啟動后執(zhí)行的功能,因此誕生了此篇文章。

        思路:SpringBoot提供的一種簡單的實現(xiàn)方案,實現(xiàn)CommandLineRunner接口,實現(xiàn)功能的代碼放在實現(xiàn)的run方法中加載,并且如果多個類需要夾加載順序,則實現(xiàn)類上使用@Order注解,且value值越小則優(yōu)先級越高。

        實踐

        上面筆者做了簡單的介紹,下面我們進入實戰(zhàn)part。

        基于CommandLineRunner接口建立兩個實現(xiàn)類為RunnerLoadOne 、RunnerLoadTwo ;并設置加載順序;

        筆者這里使用了ClassDo對象,主要是能夠體現(xiàn)@Order注解的加載順序,實際應用開發(fā)中,大家根據(jù)業(yè)務需求場景適當調(diào)整(學以致用吧)。

        @Component
        @Order(1)
        public class RunnerLoadOne implements CommandLineRunner {

            @Override
            public void run(String... args) throws Exception {
                ClassDo classDo = SpringContextUtil.getBean(ClassDo.class);
                classDo.setClassName("Java");
                System.out.println("------------容器初始化bean之后,加載資源結(jié)束-----------");
            }
        }

        @Component
        @Order(2)
        public class RunnerLoadTwo implements CommandLineRunner {
            @Override
            public void run(String... args) throws Exception {
                ClassDo bean = SpringContextUtil.getBean(ClassDo.class);
                System.out.println("依賴預先加載的資源數(shù)據(jù):" + bean.getClassName());
            }
        }

        啟動主實現(xiàn)類,看到console打印的結(jié)果如下:

        ...
        2020-08-06 21:20:14.582  INFO 6612 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'dataSource' has been autodetected for JMX exposure
        2020-08-06 21:20:14.592  INFO 6612 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
        2020-08-06 21:20:14.686  INFO 6612 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8666 (http) with context path ''
        2020-08-06 21:20:14.693  INFO 6612 --- [           main] com.qxy.InformalEssayApplication         : Started InformalEssayApplication in 121.651 seconds (JVM running for 173.476)
        ------------容器初始化bean之后,加載資源結(jié)束-----------
        依賴預先加載的資源數(shù)據(jù):Java

        源碼跟蹤

        通過上面的實踐操作,大家應該理解如何使用的,下面帶著大家理解一下底層如何實現(xiàn)的;

        常規(guī)操作,主啟動類debugger走起來~

        run()方法

        跟進run方法后,一路F6直達以下方法

        public ConfigurableApplicationContext run(String... args) {
           StopWatch stopWatch = new StopWatch();
           //設置線程啟動計時器
           stopWatch.start();
           ConfigurableApplicationContext context = null;
           Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
           //配置系統(tǒng)屬性:默認缺失外部顯示屏等允許啟動
           configureHeadlessProperty();
           //獲取并啟動事件監(jiān)聽器,如果項目中沒有其他監(jiān)聽器,則默認只有EventPublishingRunListener
           SpringApplicationRunListeners listeners = getRunListeners(args);
           //將事件廣播給listeners
           listeners.starting();
           try {
               //對于實現(xiàn)ApplicationRunner接口,用戶設置ApplicationArguments參數(shù)進行封裝
              ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
              //配置運行環(huán)境:例如激活應用***.yml配置文件      
              ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
              configureIgnoreBeanInfo(environment);
              //加載配置的banner(gif,txt...),即控制臺圖樣
              Banner printedBanner = printBanner(environment);
              //創(chuàng)建上下文對象,并實例化
              context = createApplicationContext();
              exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
              //配置SPring容器      
              prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
              //刷新Spring上下文,創(chuàng)建bean過程中      
              refreshContext(context);
              //空方法,子類實現(xiàn)
              afterRefresh(context, applicationArguments);
              //停止計時器:計算線程啟動共用時間
              stopWatch.stop();
              if (this.logStartupInfo) {
                 new StartupInfoLogger(this.mainApplicationClass)
                       .logStarted(getApplicationLog(), stopWatch);
              }
              //停止事件監(jiān)聽器
              listeners.started(context);
              //開始加載資源
              callRunners(context, applicationArguments);
           }
           catch (Throwable ex) {
              handleRunFailure(context, listeners, exceptionReporters, ex);
              throw new IllegalStateException(ex);
           }

           listeners.running(context);
           return context;
        }

        本篇文章主要是熟悉SpringBoot的CommandLineRunner接口實現(xiàn)原理。因此上面SpringBoot啟動過程方法不做過多介紹。我們直接進入正題CallRunners()方法內(nèi)部。

        callRunners方法

        private void callRunners(ApplicationContext context, ApplicationArguments args) {
            //將實現(xiàn)ApplicationRunner和CommandLineRunner接口的類,存儲到集合中
           List<Object> runners = new ArrayList<>();
           runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
           runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
           //按照加載先后順序排序
           AnnotationAwareOrderComparator.sort(runners);
           for (Object runner : new LinkedHashSet<>(runners)) {
              if (runner instanceof ApplicationRunner) {
                 callRunner((ApplicationRunner) runner, args);
              }
              if (runner instanceof CommandLineRunner) {
                 callRunner((CommandLineRunner) runner, args);
              }
           }
        }

        上面部分代碼非常簡單,對于Spring源碼見到如此簡單邏輯代碼,內(nèi)心是否有一絲絲的激動~

        private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
           try {
               //調(diào)用各個實現(xiàn)類中的邏輯實現(xiàn)
              (runner).run(args.getSourceArgs());
           }
           catch (Exception ex) {
              throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
           }
        }

        到此結(jié)束,再跟進run()方法,就可以看到我們實現(xiàn)的資源加載邏輯啦~

        總結(jié)

        新手跟進源代碼理解總結(jié),若存在不當之處,希望大佬及時指正。相信經(jīng)過上面簡答的介紹,大家應該能夠清晰的理解CommandLineRunner接口的實際應用場景了。

        PS:如果覺得我的分享不錯,歡迎大家隨手點贊、在看。
        END
        瀏覽 48
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            精品国自产拍在线 | 可以看的黄色视频 | 亚洲无码在线免费 | 国产精品秘 麻豆 | 无码秘 蜜桃一区二区 | 人人澡人人人妻人人奭 | 青娱乐国产在线观看 | 嗯灬啊灬嗯灬用点力灬 | 高h辣文肉时间静止 | 日韩欧美国产视频 |