<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初始化時搞點事情

          共 11065字,需瀏覽 23分鐘

           ·

          2021-05-05 06:59


          我們經(jīng)常需要在容器啟動的時候做一些鉤子動作,比如注冊消息消費者,監(jiān)聽配置等,今天就總結(jié)下SpringBoot留給開發(fā)者的7個啟動擴展點。

          容器刷新完成擴展點

          1、監(jiān)聽容器刷新完成擴展點ApplicationListener<ContextRefreshedEvent>

          基本用法

          熟悉Spring的同學(xué)一定知道,容器刷新成功意味著所有的Bean初始化已經(jīng)完成,當(dāng)容器刷新之后Spring將會調(diào)用容器內(nèi)所有實現(xiàn)了ApplicationListener<ContextRefreshedEvent>BeanonApplicationEvent方法,應(yīng)用程序可以以此達到監(jiān)聽容器初始化完成事件的目的。

          @Component
          public class StartupApplicationListenerExample implements 
            ApplicationListener<ContextRefreshedEvent
          {

              private static final Logger LOG 
                = Logger.getLogger(StartupApplicationListenerExample.class);

              public static int counter;

              @Override public void onApplicationEvent(ContextRefreshedEvent event) {
                  LOG.info("Increment counter");
                  counter++;
              }
          }

          易錯的點

          這個擴展點用在web容器中的時候需要額外注意,在web 項目中(例如spring mvc),系統(tǒng)會存在兩個容器,一個是root application context,另一個就是我們自己的context(作為root application context的子容器)。如果按照上面這種寫法,就會造成onApplicationEvent方法被執(zhí)行兩次。解決此問題的方法如下:

          @Component
          public class StartupApplicationListenerExample implements 
            ApplicationListener<ContextRefreshedEvent
          {

              private static final Logger LOG 
                = Logger.getLogger(StartupApplicationListenerExample.class);

              public static int counter;

              @Override public void onApplicationEvent(ContextRefreshedEvent event) {
                  if (event.getApplicationContext().getParent() == null) {
                      // root application context 沒有parent
                      LOG.info("Increment counter");
                      counter++;
                  }
              }
          }

          高階玩法

          當(dāng)然這個擴展還可以有更高階的玩法:自定義事件,可以借助Spring以最小成本實現(xiàn)一個觀察者模式:

          • 先自定義一個事件:
          public class NotifyEvent extends ApplicationEvent {
              private String email;
              private String content;
              public NotifyEvent(Object source) {
                  super(source);
              }
              public NotifyEvent(Object source, String email, String content) {
                  super(source);
                  this.email = email;
                  this.content = content;
              }
              // 省略getter/setter方法
          }
          • 注冊一個事件監(jiān)聽器
          @Component
          public class NotifyListener implements ApplicationListener<NotifyEvent{

              @Override
              public void onApplicationEvent(NotifyEvent event) {
                  System.out.println("郵件地址:" + event.getEmail());
                  System.out.println("郵件內(nèi)容:" + event.getContent());
              }
          }
          • 發(fā)布事件
          @RunWith(SpringRunner.class)
          @SpringBootTest
          public class ListenerTest 
          {
              @Autowired
              private WebApplicationContext webApplicationContext;

              @Test
              public void testListener() {
                  NotifyEvent event = new NotifyEvent("object""[email protected]""This is the content");
                  webApplicationContext.publishEvent(event);
              }
          }
          • 執(zhí)行單元測試可以看到郵件的地址和內(nèi)容都被打印出來了

          2、SpringBootCommandLineRunner接口

          當(dāng)容器上下文初始化完成之后,SpringBoot也會調(diào)用所有實現(xiàn)了CommandLineRunner接口的run方法,下面這段代碼可起到和上文同樣的作用:

          @Component
          public class CommandLineAppStartupRunner implements CommandLineRunner {
              private static final Logger LOG =
                LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

              public static int counter;

              @Override
              public void run(String...args) throws Exception {
                  LOG.info("Increment counter");
                  counter++;
              }
          }

          對于這個擴展點的使用有額外兩點需要注意:

          • 多個實現(xiàn)了CommandLineRunnerBean的執(zhí)行順序可以根據(jù)Bean上的@Order注解調(diào)整
          • run方法可以接受從控制臺輸入的參數(shù),跟ApplicationListener<ContextRefreshedEvent>這種擴展相比,更加靈活
          // 從控制臺輸入?yún)?shù)示例
          java -jar CommandLineAppStartupRunner.jar abc abcd

          3、SpringBootApplicationRunner接口

          這個擴展和SpringBootCommandLineRunner接口的擴展類似,只不過接受的參數(shù)是一個ApplicationArguments類,對控制臺輸入的參數(shù)提供了更好的封裝,以--開頭的被視為帶選項的參數(shù),否則是普通的參數(shù)

          @Component
          public class AppStartupRunner implements ApplicationRunner {
              private static final Logger LOG =
                LoggerFactory.getLogger(AppStartupRunner.class);

              public static int counter;

              @Override
              public void run(ApplicationArguments args) throws Exception {
                  LOG.info("Application started with option names : {}"
                    args.getOptionNames());
                  LOG.info("Increment counter");
                  counter++;
              }
          }

          比如:

          java -jar CommandLineAppStartupRunner.jar abc abcd --autho=mark verbose

          Bean初始化完成擴展點

          前面的內(nèi)容總結(jié)了針對容器初始化的擴展點,在有些場景,比如監(jiān)聽消息的時候,我們希望Bean初始化完成之后立刻注冊監(jiān)聽器,而不是等到整個容器刷新完成,Spring針對這種場景同樣留足了擴展點:

          1、@PostConstruct注解

          @PostConstruct注解一般放在Bean的方法上,被@PostConstruct修飾的方法會在Bean初始化后馬上調(diào)用:

          @Component
          public class PostConstructExampleBean {

              private static final Logger LOG 
                = Logger.getLogger(PostConstructExampleBean.class);

              @Autowired
              private Environment environment;

              @PostConstruct
              public void init() {
                  LOG.info(Arrays.asList(environment.getDefaultProfiles()));
              }
          }

          2、 InitializingBean接口

          InitializingBean的用法基本上與@PostConstruct一致,只不過相應(yīng)的Bean需要實現(xiàn)afterPropertiesSet方法

          @Component
          public class InitializingBeanExampleBean implements InitializingBean {

              private static final Logger LOG 
                = Logger.getLogger(InitializingBeanExampleBean.class);

              @Autowired
              private Environment environment;

              @Override
              public void afterPropertiesSet() throws Exception {
                  LOG.info(Arrays.asList(environment.getDefaultProfiles()));
              }
          }

          3、@Bean注解的初始化方法

          通過@Bean注入Bean的時候可以指定初始化方法:

          Bean的定義

          public class InitMethodExampleBean {

              private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);

              @Autowired
              private Environment environment;

              public void init() {
                  LOG.info(Arrays.asList(environment.getDefaultProfiles()));
              }
          }

          Bean注入

          @Bean(initMethod="init")
          public InitMethodExampleBean initMethodExampleBean() {
              return new InitMethodExampleBean();
          }

          4、通過構(gòu)造函數(shù)注入

          Spring也支持通過構(gòu)造函數(shù)注入,我們可以把搞事情的代碼寫在構(gòu)造函數(shù)中,同樣能達到目的

          @Component 
          public class LogicInConstructorExampleBean {

              private static final Logger LOG 
                = Logger.getLogger(LogicInConstructorExampleBean.class);

              private final Environment environment;

              @Autowired
              public LogicInConstructorExampleBean(Environment environment) {
                  this.environment = environment;
                  LOG.info(Arrays.asList(environment.getDefaultProfiles()));
              }
          }

          Bean初始化完成擴展點執(zhí)行順序?

          可以用一個簡單的測試:

          @Component
          @Scope(value = "prototype")
          public class AllStrategiesExampleBean implements InitializingBean {

              private static final Logger LOG 
                = Logger.getLogger(AllStrategiesExampleBean.class);

              public AllStrategiesExampleBean() {
                  LOG.info("Constructor");
              }

              @Override
              public void afterPropertiesSet() throws Exception {
                  LOG.info("InitializingBean");
              }

              @PostConstruct
              public void postConstruct() {
                  LOG.info("PostConstruct");
              }

              public void init() {
                  LOG.info("init-method");
              }
          }

          實例化這個Bean后輸出:

          [main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
          [main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
          [main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
          [main] INFO o.b.startup.AllStrategiesExampleBean - init-method

          (完)


          —————END—————

          推薦閱讀:


          最近面試BAT,整理一份面試資料Java面試BAT通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
          獲取方式:關(guān)注公眾號并回復(fù) java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
          明天見(??ω??)??
          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  澳门AV在线 | 精品一二三V | 五月天淫香淫色 | 免费在线无毒av毛片久 | 欧美精品乱码99久久蜜桃 |