Spring通過(guò)工具類實(shí)現(xiàn)獲取容器中的Bean
1. Aware 接口
小伙伴們知道,Spring 容器最大的特點(diǎn)在于所有的 Bean 對(duì)于 Spring 容器的存在是沒(méi)有意識(shí)的,因此我們常說(shuō)理論上你可以無(wú)縫將 Spring 容器切換為其他容器(然而在現(xiàn)實(shí)世界中,我們其實(shí)沒(méi)有這樣的選擇,除了 Spring 容器,難道還有更好用的?)。
當(dāng)然這只是一個(gè)理論,在實(shí)際開發(fā)中,我們往往要用到 Spring 容器為我們提供的諸多資源,例如想要獲取到容器中的配置、獲取到容器中的 Bean 等等。在這種情況下,就需要 Spring 容器中的 Bean 真正的意識(shí)到 Spring 容器的存在,才能要到這些東西,那么如何讓一個(gè) Bean 意識(shí)到 Spring 容器的存在呢?
這就依賴于 Spring 容器給我們提供的各種 Aware 接口了。
/** ?*?A?marker?superinterface?indicating?that?a?bean?is?eligible?to?be?notified?by?the ?*?Spring?container?of?a?particular?framework?object?through?a?callback-style?method. ?*?The?actual?method?signature?is?determined?by?individual?subinterfaces?but?should ?*?typically?consist?of?just?one?void-returning?method?that?accepts?a?single?argument. ?* ?*?<p>Note?that?merely?implementing?{@link?Aware}?provides?no?default?functionality. ?*?Rather,?processing?must?be?done?explicitly,?for?example?in?a ?*?{@link?org.springframework.beans.factory.config.BeanPostProcessor}. ?*?Refer?to?{@link?org.springframework.context.support.ApplicationContextAwareProcessor} ?*?for?an?example?of?processing?specific?{@code?*Aware}?interface?callbacks. ?* ?*?@author?Chris?Beams ?*?@author?Juergen?Hoeller ?*?@since?3.1 ?*/ public?interface?Aware?{ }
從這個(gè)接口的注釋中,我們也能大概看出來(lái),這個(gè)接口的子類,主要是提供了一些只有一個(gè)參數(shù)的 set 方法,通過(guò)這些方法可以讓 Spring 容器感知到某一件事情。
Aware 的實(shí)現(xiàn)有很多,大的方向來(lái)說(shuō)主要有如下一些:
每一個(gè) Aware 的作用如下:
- ApplicationEventPublisherAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取事件發(fā)布的能力。
- ServletContextAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到 ServletContext 對(duì)象。
- MessageSourceAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到 MessageSource 對(duì)象,MessageSource 支持多消息源,主要用于主要用于國(guó)際化。
- ResourceLoaderAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) ResourceLoader,Spring ResourceLoader 則為我們提供了一個(gè)統(tǒng)一的 getResource() 方法來(lái)通過(guò)資源路徑檢索外部資源,例如文本文件、XML 文件、屬性文件或圖像文件等。
- ApplicationStartupAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) ApplicationStartup 對(duì)象,這個(gè)比較新,是 Spring 5.3 中新推出的,通過(guò) ApplicationStartup 可以標(biāo)記應(yīng)用程序啟動(dòng)期間的步驟,并收集有關(guān)執(zhí)行上下文或其處理時(shí)間的數(shù)據(jù)。
- NotificationPublisherAware:實(shí)現(xiàn)該接的對(duì)象可以獲取到一個(gè) NotificationPublisher 對(duì)象,通過(guò)該對(duì)象可以實(shí)現(xiàn)通知的發(fā)送。
- EnvironmentAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) Environment 對(duì)象,通過(guò) Environment 可以獲取到容器的環(huán)境信息。
- BeanFactoryAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) BeanFactory 對(duì)象,通過(guò) BeanFactory 可以完成 Bean 的查詢等操作。
- ImportAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) AnnotationMetadata 對(duì)象,ImportAware 接口是需要和 @Import 注解一起使用的。在 @Import 作為元注解使用時(shí),通過(guò) @Import 導(dǎo)入的配置類如果實(shí)現(xiàn)了 ImportAware 接口就可以獲取到導(dǎo)入該配置類接口的數(shù)據(jù)配置。
- EmbeddedValueResolverAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) StringValueResolver 對(duì)象,通過(guò) StringValueResolver 對(duì)象,可以讀取到 Spring 容器中的 properties 配置的值(YAML 配置也可以)。
- ServletConfigAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) ServletConfig 對(duì)象,不過(guò)這個(gè)似乎沒(méi)什么用,我們很少自己去配置 ServletConfig。
- LoadTimeWeaverAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) LoadTimeWeaver 對(duì)象,通過(guò)該對(duì)象可以獲取加載 Spring Bean 時(shí)織入的第三方模塊,如 AspectJ 等。
- BeanClassLoaderAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) ClassLoader 對(duì)象,ClassLoader 能干嘛不需要我多說(shuō)了吧。
- BeanNameAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè)當(dāng)前 Bean 的名稱。
- ApplicationContextAware:實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) ApplicationContext 對(duì)象,通過(guò) ApplicationContext 可以獲取容器中的 Bean、環(huán)境等信息。
這是 Spring 中提供的一堆 Aware。
接下來(lái)松哥隨便寫個(gè)例子大家來(lái)看下 Aware 的用法。
2. BeanFactoryAware
實(shí)現(xiàn)該接口的對(duì)象可以獲取到一個(gè) BeanFactory 對(duì)象,通過(guò) BeanFactory 可以完成 Bean 的查詢等操作。這算是一個(gè)比較常見的 Aware 了,我們一起來(lái)看下。
這里為了省事,我就在 Spring Boot 中來(lái)和大家演示。
首先我們來(lái)定義一個(gè)簡(jiǎn)單的 UserService:
@Service public?class?UserService?{ ????public?void?hello()?{ ????????System.out.println("hello?javaboy!"); ????} }
然后提供一個(gè)工具類:
@Component public?class?BeanUtils?implements?BeanFactoryAware?{ ????private?static?BeanFactory?beanFactory?=?null; ????@Override ????public?void?setBeanFactory(BeanFactory?beanFactory)?throws?BeansException?{ ????????BeanUtils.beanFactory?=?beanFactory; ????} ????public?static?<T>?T?getBean(String?beanName)?{ ????????return?(T)?beanFactory.getBean(beanName); ????} }
有了這個(gè)工具類,接下來(lái)我們就可以在一個(gè)非 Spring 管理的 Bean 中,隨時(shí)隨地的查詢 Bean 了,像下面這樣:
UserService?userService?=?BeanUtils.getBean("userService"); userService.hello();
3. TienChin 項(xiàng)目實(shí)踐
為什么會(huì)有今天這篇文章呢?主要是在松哥最近做的 TienChin 項(xiàng)目中,有一個(gè)地方涉及到這塊知識(shí)點(diǎn)了,但是有的小伙伴不熟悉,因此就拎出來(lái)和大家梳理下。
在 TienChin 項(xiàng)目中,在記錄日志的時(shí)候,因?yàn)槿罩臼且粋€(gè)延遲任務(wù),所以提前準(zhǔn)備好了相關(guān)的 Bean 已經(jīng)注冊(cè)到 Spring 容器中了,像下面這樣:
@Configuration public?class?ThreadPoolConfig?{ ????/** ?????*?執(zhí)行周期性或定時(shí)任務(wù) ?????*/ ????@Bean(name?=?"scheduledExecutorService") ????protected?ScheduledExecutorService?scheduledExecutorService()?{ ????????return?new?ScheduledThreadPoolExecutor(corePoolSize, ????????????????new?BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), ????????????????new?ThreadPoolExecutor.CallerRunsPolicy())?{ ????????????@Override ????????????protected?void?afterExecute(Runnable?r,?Throwable?t)?{ ????????????????super.afterExecute(r,?t); ????????????????Threads.printException(r,?t); ????????????} ????????}; ????} } @Component public?final?class?SpringUtils?implements?BeanFactoryPostProcessor,?ApplicationContextAware?{ ????/** ?????*?Spring應(yīng)用上下文環(huán)境 ?????*/ ????private?static?ConfigurableListableBeanFactory?beanFactory; ????private?static?ApplicationContext?applicationContext; ????@Override ????public?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?throws?BeansException?{ ????????SpringUtils.beanFactory?=?beanFactory; ????} ????@Override ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{ ????????SpringUtils.applicationContext?=?applicationContext; ????} ????/** ?????*?獲取對(duì)象 ?????* ?????*?@param?name ?????*?@return?Object?一個(gè)以所給名字注冊(cè)的bean的實(shí)例 ?????*?@throws?org.springframework.beans.BeansException ?????*/ ????@SuppressWarnings("unchecked") ????public?static?<T>?T?getBean(String?name)?throws?BeansException?{ ????????return?(T)?beanFactory.getBean(name); ????} ????/** ?????*?獲取類型為requiredType的對(duì)象 ?????* ?????*?@param?clz ?????*?@return ?????*?@throws?org.springframework.beans.BeansException ?????*/ ????public?static?<T>?T?getBean(Class<T>?clz)?throws?BeansException?{ ????????T?result?=?(T)?beanFactory.getBean(clz); ????????return?result; ????} }
而寫日志的異步任務(wù)工具類,并非一個(gè)容器,所以要通過(guò)這個(gè)工具類獲取相應(yīng)的 Bean,如下:
public?class?AsyncManager?{ ????/** ?????*?操作延遲10毫秒 ?????*/ ????private?final?int?OPERATE_DELAY_TIME?=?10; ????/** ?????*?異步操作任務(wù)調(diào)度線程池 ?????*/ ????private?ScheduledExecutorService?executor?=?SpringUtils.getBean("scheduledExecutorService"); ????/** ?????*?單例模式 ?????*/ ????private?AsyncManager()?{ ????} ????private?static?AsyncManager?me?=?new?AsyncManager(); ????public?static?AsyncManager?me()?{ ????????return?me; ????} ????/** ?????*?執(zhí)行任務(wù) ?????* ?????*?@param?task?任務(wù) ?????*/ ????public?void?execute(TimerTask?task)?{ ????????executor.schedule(task,?OPERATE_DELAY_TIME,?TimeUnit.MILLISECONDS); ????} }
有了 SpringUtils 我們就可以在一個(gè)非 Spring 容器所管理的 Bean 中,獲取到 Spring 容器中的 Bean 了。
到此這篇關(guān)于Spring通過(guò)工具類實(shí)現(xiàn)獲取容器中的Bean的文章就介紹到這了,更多相關(guān)Spring獲取Bean內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Cache+Redis緩存數(shù)據(jù)的實(shí)現(xiàn)示例
本文主要介紹了Spring?Cache+Redis緩存數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01Java解壓縮zip - 解壓縮多個(gè)文件或文件夾實(shí)例
本篇文章主要介紹了Java解壓縮zip - 解壓縮多個(gè)文件或文件夾實(shí)例,非常具有實(shí)用價(jià)值,有需要的可以了解一下。2016-12-12ANSI,Unicode,BMP,UTF等編碼概念實(shí)例講解
這篇文章主要介紹了ANSI,Unicode,BMP,UTF等編碼概念實(shí)例講解,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12Java中mybatis關(guān)于example類的使用詳解
這篇文章主要介紹了Java中mybatis中關(guān)于example類的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07SpringBoot應(yīng)用能直接運(yùn)行java -jar的原因分析
這篇文章主要介紹了SpringBoot應(yīng)用為什么能直接運(yùn)行java -jar,首先明確一點(diǎn),普通jar包是不能直接運(yùn)行的,比如工具類jar,要能運(yùn)行,至少得要一個(gè)main函數(shù)作為入口吧?本文給大家介紹了詳細(xì)的原因分析,需要的朋友可以參考下2024-03-03MyBatis將查詢出的兩列數(shù)據(jù)裝配成鍵值對(duì)的操作方法
這篇文章主要介紹了MyBatis將查詢出的兩列數(shù)據(jù)裝配成鍵值對(duì)的操作代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08