成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

了解這些,你就可以在Spring啟動時為所欲為了

開發 前端
Spring 是一個控制反轉依賴管理的容器,作為 Java Web 的開發人員,基本沒有不熟悉 Spring 技術棧的,盡管在依賴注入領域,Java Web 領域不乏其他優秀的框架,如 google 開源的依賴管理框架 guice,如 Jersey web 框架等。

[[374930]]

Spring 是一個控制反轉依賴管理的容器,作為 Java Web 的開發人員,基本沒有不熟悉 Spring 技術棧的,盡管在依賴注入領域,Java Web 領域不乏其他優秀的框架,如 google 開源的依賴管理框架 guice,如 Jersey web 框架等。但 Spring 已經是 Java Web 領域使用最多,應用最廣泛的 Java 框架。

此文將專注講解如何在 Spring 容器啟動時實現我們自己想要實現的邏輯。我們時常會遇到在 Spring 啟動的時候必須完成一些初始化的操作,如創建定時任務,創建連接池等。

如果沒有 Spring 容器,不依賴于 Spring 的實現,回歸 Java 類實現本身,我們可以在靜態代碼塊,在類構造函數中實現相應的邏輯,Java 類的初始化順序依次是靜態變量 > 靜態代碼塊 > 全局變量 > 初始化代碼塊 > 構造器。

比如,Log4j 的初始化,就是在 LogManager 的靜態代碼塊中實現的:

  1. static { 
  2.     Hierarchy h = new Hierarchy(new RootLogger((LevelLevel.DEBUG)); 
  3.     repositorySelector = new DefaultRepositorySelector(h); 
  4.  
  5.     String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null); 
  6.  
  7.     if(override == null || "false".equalsIgnoreCase(override)) { 
  8.           String configurationOptionStr = OptionConverter.getSystemProperty(DEFAULT_CONFIGURATION_KEY, null); 
  9.           String configuratorClassName = OptionConverter.getSystemProperty(CONFIGURATOR_CLASS_KEY, null); 
  10.  
  11.           URL url = null
  12.  
  13.           if(configurationOptionStr == null) { 
  14.             url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); 
  15.             if(url == null) { 
  16.               url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); 
  17.             } 
  18.           } else { 
  19.             try { 
  20.               url = new URL(configurationOptionStr); 
  21.             } catch (MalformedURLException ex) { 
  22.               url = Loader.getResource(configurationOptionStr); 
  23.             } 
  24.           } 
  25.  
  26.           if(url != null) { 
  27.             LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); 
  28.             try { 
  29.                 OptionConverter.selectAndConfigure(url, configuratorClassName,LogManager.getLoggerRepository()); 
  30.             } catch (NoClassDefFoundError e) { 
  31.                 LogLog.warn("Error during default initialization", e); 
  32.             } 
  33.           } else { 
  34.               LogLog.debug("Could not find resource: ["+configurationOptionStr+"]."); 
  35.           } 
  36.     } else { 
  37.             LogLog.debug("Default initialization of overridden by " +  DEFAULT_INIT_OVERRIDE_KEY + "property."); 
  38.     } 

比如在構造函數中實現相應的邏輯:

  1. @Component 
  2. public class CustomBean { 
  3.  
  4.     @Autowired 
  5.     private Environment env; 
  6.  
  7.     public CustomBean() { 
  8.         env.getActiveProfiles(); 
  9.     } 

這里考驗一下各位,上面的代碼是否可以正常運行。—— 不行,構造函數中的env將會發生NullPointException異常。這是因為在 Spring 中將先初始化 Bean,也就是會先調用類的構造函數,然后才注入成員變量依賴的 Bean(@Autowired和@Resource注解修飾的成員變量),注意@Value等注解的配置的注入也是在構造函數之后。

PostConstruct

在 Spring 中,我們可以使用@PostConstruct在 Bean 初始化之后實現相應的初始化邏輯,@PostConstruct修飾的方法將在 Bean 初始化完成之后執行,此時 Bean 的依賴也已經注入完成,因此可以在方法中調用注入的依賴 Bean。

  1. @Component 
  2. public class CustomBean { 
  3.  
  4.     @Autowired 
  5.     private Environment env; 
  6.  
  7.     @PostConstruce 
  8.     public void init() { 
  9.         env.getActiveProfiles(); 
  10.     } 

與@PostConstruct相對應的,如果想在 Bean 注銷時完成一些清掃工作,如關閉線程池等,可以使用@PreDestroy注解:

  1. @Component 
  2. public class CustomBean { 
  3.  
  4.     @Autowired 
  5.     private ExecutorService executor = Executors.newFixedThreadPool(1) 
  6.  
  7.     @PreDestroy 
  8.     public void destroy() { 
  9.         env.getActiveProfiles(); 
  10.     } 

InitializingBean

實現 Spring 的InitializingBean接口同樣可以實現以上在 Bean 初始化完成之后執行相應邏輯的功能,實現InitializingBean接口,在afterPropertiesSet方法中實現邏輯:

  1. @Component 
  2. public class CustomBean implements InitializingBean { 
  3.  
  4.     private static final Logger LOG 
  5.       = Logger.getLogger(InitializingBeanExampleBean.class); 
  6.  
  7.     @Autowired 
  8.     private Environment environment; 
  9.  
  10.     @Override 
  11.     public void afterPropertiesSet() throws Exception { 
  12.         LOG.info(environment.getDefaultProfiles()); 
  13.     } 

ApplicationListener

我們可以在 Spring 容器初始化的時候實現我們想要的初始化邏輯。這時我們就可以使用到 Spring 的初始化事件。Spring 有一套完整的事件機制,在 Spring 啟動的時候,Spring 容器本身預設了很多事件,在 Spring 初始化的整個過程中在相應的節點觸發相應的事件,我們可以通過監聽這些事件來實現我們的初始化邏輯。Spring 的事件實現如下:

  • ApplicationEvent,事件對象,由 ApplicationContext 發布,不同的實現類代表不同的事件類型。
  • ApplicationListener,監聽對象,任何實現了此接口的 Bean 都會收到相應的事件通知。實現了 ApplicationListener 接口之后,需要實現方法 onApplicationEvent(),在容器將所有的 Bean 都初始化完成之后,就會執行該方法。

與 Spring Context 生命周期相關的幾個事件有以下幾個:

  • ApplicationStartingEvent: 這個事件在 Spring Boot 應用運行開始時,且進行任何處理之前發送(除了監聽器和初始化器注冊之外)。
  • ContextRefreshedEvent: ApplicationContext 被初始化或刷新時,該事件被發布。這也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法來發生。
  • ContextStartedEvent: 當使用 ConfigurableApplicationContext 接口中的 start() 方法啟動 ApplicationContext 時,該事件被觸發。你可以查詢你的數據庫,或者你可以在接受到這個事件后重啟任何停止的應用程序。
  • ApplicationReadyEvent: 這個事件在任何 application/ command-line runners 調用之后發送。
  • ContextClosedEvent: 當使用 ConfigurableApplicationContext 接口中的 close() 方法關閉 ApplicationContext 時,該事件被觸發。一個已關閉的上下文到達生命周期末端;它不能被刷新或重啟。
  • ContextStoppedEvent: Spring 最后完成的事件。

因此,如果我們想在 Spring 啟動的時候實現一些相應的邏輯,可以找到 Spring 啟動過程中符合我們需要的事件,通過監聽相應的事件來完成我們的邏輯:

  1. @Component 
  2. @Slf4j 
  3. public class StartupApplicationListenerExample implements ApplicationListener<ContextRefreshedEvent> { 
  4.  
  5.     @Override 
  6.     public void onApplicationEvent(ContextRefreshedEvent event) { 
  7.         log.info("Subject ContextRefreshedEvent"); 
  8.     } 

除了通過實現ApplicationListener接口來監聽相應的事件,Spring 的事件機制也實現了通過@EventListener注解來監聽相對應事件:

  1. @Component 
  2. @Slf4j 
  3. public class StartupApplicationListenerExample { 
  4.  
  5.     @EventListener 
  6.     public void onApplicationEvent(ContextRefreshedEvent event) { 
  7.         log.info("Subject ContextRefreshedEvent"); 
  8.     } 

Spring Event 是一套完善的進程內事件發布訂閱機制,我們除了用來監聽 Spring 內置的事件,也可以使用 Spring Event 實現自定義的事件發布訂閱功能。

Constructor 注入

在學習 Spring 的注入機制的時候,我們都知道 Spring 可以通過構造函數、Setter 和反射成員變量注入等方式。上面我們在成員變量上通過@Autoware注解注入依賴 Bean,但是在 Bean 的構造函數函數中卻無法使用到注入的 Bean(因為 Bean 還未注入),其實我們也是使用 Spring 的構造函數注入方式, 這也是 Spring 推薦的注入機制(在我們使用 IDEA 的時候,如果沒有關閉相應的代碼 Warning 機制,會發現在成員變量上的@Autoware是黃色的,也就是 idea 不建議的代碼)。Spring 更推薦構造函數注入的方式:

  1. @Component 
  2. @Slf4j 
  3. public class ConstructorBean { 
  4.  
  5.     private final Environment environment; 
  6.  
  7.     @Autowired 
  8.     public LogicInConstructorExampleBean(Environment environment) { 
  9.         this.environment = environment; 
  10.         log.info(Arrays.asList(environment.getDefaultProfiles())); 
  11.     } 

CommandLineRunner

如果我們的項目使用的是 Spring Boot,那么可以使用 Spring Boot 提供的 CommandLineRunner 接口來實現初始化邏輯,Spring Boot 將在啟動初始化完成之后調用實現了CommandLineRunner的接口的run方法:

  1. @Component 
  2. @Slf4j 
  3. public class CommandLineAppStartupRunner implements CommandLineRunner { 
  4.  
  5.     @Override 
  6.     public void run(String...args) throws Exception { 
  7.         log.info("Increment counter"); 
  8.     } 

并且,多個CommandLineRunner實現,可以通過@Order來控制它們的執行順序。

SmartLifecycle

還有一種更高級的方法來實現我們的邏輯。這可以 Spring 高級開發必備技能哦。SmartLifecycle 不僅僅能在初始化后執行一個邏輯,還能再關閉前執行一個邏輯,并且也可以控制多個 SmartLifecycle 的執行順序,就像這個類名表示的一樣,這是一個智能的生命周期管理接口。

  • start():bean 初始化完畢后,該方法會被執行。
  • stop():容器關閉后,spring 容器發現當前對象實現了 SmartLifecycle,就調用 stop(Runnable), 如果只是實現了 Lifecycle,就調用 stop()。
  • isRunning:當前狀態,用來判你的斷組件是否在運行。
  • getPhase:控制多個 SmartLifecycle 的回調順序的,返回值越小越靠前執行 start() 方法,越靠后執行 stop() 方法。
  • isAutoStartup():start 方法被執行前先看此方法返回值,返回 false 就不執行 start 方法了。
  • stop(Runnable):容器關閉后,spring 容器發現當前對象實現了 SmartLifecycle,就調用 stop(Runnable), 如果只是實現了 Lifecycle,就調用 stop()。
  1. @Component 
  2. public class SmartLifecycleExample implements SmartLifecycle { 
  3.  
  4.     private boolean isRunning = false
  5.  
  6.     @Override 
  7.     public void start() { 
  8.         System.out.println("start"); 
  9.         isRunning = true
  10.     } 
  11.  
  12.     @Override 
  13.     public int getPhase() { 
  14.         // 默認為 0 
  15.         return 0; 
  16.     } 
  17.  
  18.     @Override 
  19.     public boolean isAutoStartup() { 
  20.         // 默認為 false 
  21.         return true
  22.     } 
  23.  
  24.     @Override 
  25.     public boolean isRunning() { 
  26.         // 默認返回 false 
  27.         return isRunning; 
  28.     } 
  29.  
  30.     @Override 
  31.     public void stop(Runnable callback) { 
  32.         System.out.println("stop(Runnable)"); 
  33.         callback.run(); 
  34.         isRunning = false
  35.     } 
  36.  
  37.     @Override 
  38.     public void stop() { 
  39.         System.out.println("stop"); 
  40.  
  41.         isRunning = false
  42.     } 
  43.  

本文轉載自微信公眾號「碼哥字節」,可以通過以下二維碼關注。轉載本文請聯系碼哥字節公眾號。  

 

責任編輯:武曉燕 來源: 碼哥字節
相關推薦

2018-03-01 15:00:15

Oracle數據中心云計算

2017-02-13 09:33:32

2020-07-06 10:55:38

CIO首席信息官IT

2014-06-19 14:49:37

iCloud認證令牌密碼

2022-10-17 07:16:08

SQL機器學習AI

2022-01-29 08:34:54

OTN網絡架構網絡

2018-07-19 06:07:22

物聯網安全物聯網IOT

2021-08-27 23:13:03

Windows 11Windows微軟

2021-02-23 12:30:21

VS CodeGithub代碼

2022-02-28 17:57:44

云遷移云計算

2022-02-28 22:58:04

云遷移IT開發

2018-11-27 09:21:41

負載均衡機器Session

2019-04-22 12:25:40

UbuntuLinux IP地址

2018-12-03 08:04:25

負載均衡機器流量

2015-07-10 11:18:19

2020-11-19 08:00:03

打工人離職工作

2020-09-17 15:59:37

Java技術開發

2021-02-23 07:01:24

js小游戲技術

2009-10-21 09:46:13

VB使用ArrayLi

2009-03-17 08:46:57

Windows 7微軟發布
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 天堂一区二区三区四区 | 久操伊人 | 免费一看一级毛片 | 中文字幕一区二区不卡 | 国产精品一区二区精品 | 国产精品一区二区视频 | 欧美亚洲一区二区三区 | 成人av色 | 亚洲成av片人久久久 | 亚洲一级视频在线 | 99久久99 | 天天色影视综合 | 国产精品1区2区3区 一区中文字幕 | 久久久久久久久91 | 国产久 | 欧美aaa一级片 | 天天干com | 狠狠爱网址 | 久久久噜噜噜久久中文字幕色伊伊 | 精品视频免费 | 亚洲黄色av | 日韩视频在线免费观看 | 日韩视频精品 | 日韩在线中文字幕 | 欧美日韩在线观看一区 | 久久久成人精品 | 韩国精品在线观看 | 国产精品美女久久久 | 91视视频在线观看入口直接观看 | 久久国产一区二区 | 久久男女视频 | 久久一二 | 一区二区三区观看视频 | 亚洲精品九九 | 高清国产午夜精品久久久久久 | 国产精品日本一区二区在线播放 | h片在线播放 | 成人在线a | 亚洲二区在线 | 久久精品色欧美aⅴ一区二区 | av在线免费网 |