<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot總結(jié)之CommandLineRunner

          共 9312字,需瀏覽 19分鐘

           ·

          2021-06-02 09:34

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

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

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

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

          加載順序?yàn)椋篊onstructor > @Autowired > @PostConstruct > 靜態(tài)方法;

          特點(diǎn):

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

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

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

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

          實(shí)踐

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

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

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

          @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("依賴預(yù)先加載的資源數(shù)據(jù):" + bean.getClassName());
              }
          }

          啟動(dòng)主實(shí)現(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é)束-----------
          依賴預(yù)先加載的資源數(shù)據(jù):Java

          源碼跟蹤

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

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

          run()方法

          跟進(jìn)run方法后,一路F6直達(dá)以下方法

          public ConfigurableApplicationContext run(String... args) {
             StopWatch stopWatch = new StopWatch();
             //設(shè)置線程啟動(dòng)計(jì)時(shí)器
             stopWatch.start();
             ConfigurableApplicationContext context = null;
             Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
             //配置系統(tǒng)屬性:默認(rèn)缺失外部顯示屏等允許啟動(dòng)
             configureHeadlessProperty();
             //獲取并啟動(dòng)事件監(jiān)聽器,如果項(xiàng)目中沒有其他監(jiān)聽器,則默認(rèn)只有EventPublishingRunListener
             SpringApplicationRunListeners listeners = getRunListeners(args);
             //將事件廣播給listeners
             listeners.starting();
             try {
                 //對(duì)于實(shí)現(xiàn)ApplicationRunner接口,用戶設(shè)置ApplicationArguments參數(shù)進(jìn)行封裝
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                      args);
                //配置運(yùn)行環(huán)境:例如激活應(yīng)用***.yml配置文件      
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                      applicationArguments);
                configureIgnoreBeanInfo(environment);
                //加載配置的banner(gif,txt...),即控制臺(tái)圖樣
                Banner printedBanner = printBanner(environment);
                //創(chuàng)建上下文對(duì)象,并實(shí)例化
                context = createApplicationContext();
                exceptionReporters = getSpringFactoriesInstances(
                      SpringBootExceptionReporter.class,
                      new Class[] { ConfigurableApplicationContext.class }, context);
                //配置SPring容器      
                prepareContext(context, environment, listeners, applicationArguments,
                      printedBanner);
                //刷新Spring上下文,創(chuàng)建bean過程中      
                refreshContext(context);
                //空方法,子類實(shí)現(xiàn)
                afterRefresh(context, applicationArguments);
                //停止計(jì)時(shí)器:計(jì)算線程啟動(dòng)共用時(shí)間
                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接口實(shí)現(xiàn)原理。因此上面SpringBoot啟動(dòng)過程方法不做過多介紹。我們直接進(jìn)入正題CallRunners()方法內(nèi)部。

          callRunners方法

          private void callRunners(ApplicationContext context, ApplicationArguments args) {
              //將實(shí)現(xiàn)ApplicationRunner和CommandLineRunner接口的類,存儲(chǔ)到集合中
             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);
                }
             }
          }

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

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

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

          總結(jié)

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

          PS:如果覺得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、在看。
          END
          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丁香五月天天操天天爽 | 欧美人妻网站 | 亚洲 欧美 国产 另类 | 日本高清黄页免费网站大全 | 久久新 |