<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                [TOC] 我們經常需要在容器啟動的時候做一些鉤子動作,比如注冊消息消費者,監聽配置等,今天就總結下`SpringBoot`留給開發者的7個啟動擴展點。 ## 容器刷新完成擴展點 ### 1、監聽容器刷新完成擴展點`ApplicationListener<ContextRefreshedEvent>` #### 基本用法 熟悉`Spring`的同學一定知道,容器刷新成功意味著所有的`Bean`初始化已經完成,當容器刷新之后`Spring`將會調用容器內所有實現了`ApplicationListener<ContextRefreshedEvent>`的`Bean`的`onApplicationEvent`方法,應用程序可以以此達到監聽容器初始化完成事件的目的。 ```java @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`),系統會存在兩個容器,一個是`root application context`,另一個就是我們自己的`context`(作為`root application context`的子容器)。如果按照上面這種寫法,就會造成`onApplicationEvent`方法被執行兩次。解決此問題的方法如下: ~~~go @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++; } } } ~~~ #### 高階玩法 當然這個擴展還可以有更高階的玩法:**自定義事件**,可以借助`Spring`以最小成本實現一個觀察者模式: * 先自定義一個事件: ~~~go 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方法 } ~~~ * 注冊一個事件監聽器 ~~~go @Component public class NotifyListener implements ApplicationListener<NotifyEvent> { @Override public void onApplicationEvent(NotifyEvent event) { System.out.println("郵件地址:" + event.getEmail()); System.out.println("郵件內容:" + event.getContent()); } } ~~~ * 發布事件 ~~~go @RunWith(SpringRunner.class) @SpringBootTest public class ListenerTest { @Autowired private WebApplicationContext webApplicationContext; @Test public void testListener() { NotifyEvent event = new NotifyEvent("object", "abc@qq.com", "This is the content"); webApplicationContext.publishEvent(event); } } ~~~ * 執行單元測試可以看到郵件的地址和內容都被打印出來了 ### 2、`SpringBoot`的`CommandLineRunner`接口 當容器上下文初始化完成之后,`SpringBoot`也會調用所有實現了`CommandLineRunner`接口的`run`方法,下面這段代碼可起到和上文同樣的作用: ~~~go @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++; } } ~~~ 對于這個擴展點的使用有額外兩點需要注意: * 多個實現了`CommandLineRunner`的`Bean`的執行順序可以根據`Bean`上的`@Order`注解調整 * 其`run`方法可以接受從控制臺輸入的參數,跟`ApplicationListener<ContextRefreshedEvent>`這種擴展相比,更加靈活 ~~~go // 從控制臺輸入參數示例 java -jar CommandLineAppStartupRunner.jar abc abcd ~~~ ### 3、`SpringBoot`的`ApplicationRunner`接口 這個擴展和`SpringBoot`的`CommandLineRunner`接口的擴展類似,只不過接受的參數是一個`ApplicationArguments`類,對控制臺輸入的參數提供了更好的封裝,以`--`開頭的被視為帶選項的參數,否則是普通的參數 ~~~go @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++; } } ~~~ 比如: ~~~go java?-jar?CommandLineAppStartupRunner.jar?abc?abcd?--autho=mark?verbose ~~~ ## 二、Bean初始化完成擴展點 前面的內容總結了針對容器初始化的擴展點,在有些場景,比如監聽消息的時候,我們希望`Bean`初始化完成之后立刻注冊監聽器,而不是等到整個容器刷新完成,`Spring`針對這種場景同樣留足了擴展點: ### 1、`@PostConstruct`注解 `@PostConstruct`注解一般放在`Bean`的方法上,被`@PostConstruct`修飾的方法會在`Bean`初始化后馬上調用: ~~~go @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`一致,只不過相應的`Bean`需要實現`afterPropertiesSet`方法 ~~~go @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`的定義** ~~~go 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`注入** ~~~go @Bean(initMethod="init") public InitMethodExampleBean initMethodExampleBean() { return new InitMethodExampleBean(); } ~~~ ### 4、通過構造函數注入 `Spring`也支持通過構造函數注入,我們可以把搞事情的代碼寫在構造函數中,同樣能達到目的 ~~~go @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初始化完成擴展點執行順序? 可以用一個簡單的測試: ~~~go @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`后輸出: ~~~go [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 ~~~ > 文章轉載自:https://blog.csdn.net/weixin_36380516/article/details/115388363
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看