Java動態(tài)獲取實現(xiàn)類的方式詳解
應(yīng)用場景
支付的時候,有不同的渠道,比如微信還是支付寶?
這個時候,就需要根據(jù)渠道類型,選擇走哪個渠道。
讀
先來看下讀
/** * 根據(jù)接口和注解@SupportCodes的值,獲取接口實現(xiàn)類 * * @param interfaceClass 接口 * @param sopportCode 接口實現(xiàn)類的注解@SupportCodes的值 * @return 接口實現(xiàn)類 */ public static <T> T getServiceImpl(Class<T> interfaceClass, String sopportCode) { // 如果沒有初始化,則初始化 if (null == serviceImplFactoryMap.get(interfaceClass)) { // 初始化 init(interfaceClass); if (null == serviceImplFactoryMap) { return null; } } // 如果初始化后,還是沒有,則返回null if (null == serviceImplFactoryMap.get(interfaceClass)) { return null; } // 返回接口實現(xiàn)類 return (T)((Map)serviceImplFactoryMap.get(interfaceClass)).get(sopportCode); }
為什么要先看讀?讀是需求,因為有讀的需求,才需要實現(xiàn)寫的功能。
重點來看入?yún)?,任何功能,無非是入?yún)⒑统鰠ⅰ?/p>
入?yún)ⅲ航涌诤颓来a。
出參:渠道實現(xiàn)類。
那具體怎么實現(xiàn)呢?
類似這種需求,基本上都是map。key是渠道代碼,value實現(xiàn)類。
大概的實現(xiàn)思路,基本上就是這樣。存儲用到的數(shù)據(jù)結(jié)構(gòu)是map。
寫
剛才看了讀方法,再來看下寫。
讀,無非是從map讀。寫,也一樣,無非是把數(shù)據(jù)寫到map。
那具體怎么寫呢?直接看代碼
/** * 初始化 * * @param interfaceClass 接口 * @author javaself */ protected static <T> void init(Class<T> interfaceClass) { log.info("begin to init " + interfaceClass); synchronized (FactoryUtil.class) { try { // 如果已經(jīng)初始化,則返回 if (null != serviceImplFactoryMap.get(interfaceClass)) { return; } Map<String, Object> serviceMap = new HashMap(); // 獲取所有實現(xiàn)類 Map<String, T> beans = appContext.getBeansOfType(interfaceClass); //實現(xiàn)類名字作為key,實現(xiàn)類實例作為value Set<Entry<String, T>> entrySet = beans.entrySet(); Iterator<Entry<String, T>> iterator = entrySet.iterator(); // 遍歷所有實現(xiàn)類,獲取注解@SupportCodes的值,作為key,實現(xiàn)類實例作為value while (iterator.hasNext()) { Object interfaceServiceImpl = ((Entry)iterator.next()).getValue(); SupportCodes annotationValue = (SupportCodes)interfaceServiceImpl.getClass().getAnnotation(SupportCodes.class); if (null != annotationValue) { for (String flag : annotationValue.value()) { //可能有多個注解值,所以遍歷 //寫到map: key=注解值,value=實現(xiàn)類實例 serviceMap.put(flag, interfaceServiceImpl); log.info(flag + "=>" + interfaceServiceImpl.getClass()); } } } //寫到map: key=接口,value=上面的map serviceImplFactoryMap.put(interfaceClass, serviceMap); } catch (Exception e) { log.error("init failed", e); throw new RuntimeException(e); } } log.info(interfaceClass + "inited"); }
寫方法的入?yún)⑹牵航涌凇?/p>
也就是說,根據(jù)接口,可以獲取不同實現(xiàn)類。具體是基于spring的獲取bean的功能。
拿到不同實現(xiàn)類之后,再根據(jù)注解代碼,組裝成key/value寫到map。key是注解代碼,value是實現(xiàn)類。
注解代碼是干嘛用的?就是前面的渠道代碼。屬于業(yè)務(wù)代碼。具體是基于自定義注解實現(xiàn)。自定義注解用的時候,有幾步,首先,是自定義注解,其次,注解到類上面去,最后,讀注解的值的時候直接使用spring提供的工具類即可。
到這里其實基本上已經(jīng)寫完了,核心思路就是怎么玩弄渠道代碼和渠道實現(xiàn)類。
何時寫?
讀的時候,寫。也就是說,第一次用到的時候,寫。而不是啟動項目的時候,寫。
只需要寫一次即可,也就是說,只在第一次用到的時候,才寫。后面直接用即可。
寫的時候,注意并發(fā)問題。類似這種只寫一次,但是又需要注意并發(fā)問題的情況,一般直接使用同步關(guān)鍵字即可。
何時讀?
支付系統(tǒng)里面根據(jù)接口和渠道代碼,決定走哪個渠道實現(xiàn)類
IGetPayInstructionService payService = FactoryUtil.getServiceImpl(IGetPayInstructionService.class, channelCode);
渠道代碼的值,從哪里來?網(wǎng)關(guān)入口會判斷。具體的話,用微信還是支付寶掃碼的時候,可以根據(jù)請求頭判斷是哪個app。
完整代碼
工具類
package com.xxx.commons.factory; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.stereotype.Component; /** * 工廠工具類:用于獲取接口實現(xiàn)類 * * --- * 應(yīng)用場景:當(dāng)一個接口有多個實現(xiàn)類時,通過該工具類獲取對應(yīng)的實現(xiàn)類。 * 不同實現(xiàn)類,通過注解@SupportCodes來區(qū)分。獲取的時候,也是通過@SupportCodes的值來獲取。 * * --- * 舉例說明 * * 不同渠道的支付接口,都實現(xiàn)了IPayService接口,如下:微信支付、支付寶支付 * * @author javaself */ @Component public class FactoryUtil implements ApplicationContextAware { private static Log log = LogFactory.getLog(FactoryUtil.class); private static Map<Class<?>, Map<String, Object>> serviceImplFactoryMap = new HashMap(); private static AbstractApplicationContext appContext; /** * 根據(jù)接口和注解@SupportCodes的值,獲取接口實現(xiàn)類 * * @param interfaceClass 接口 * @param sopportCode 接口實現(xiàn)類的注解@SupportCodes的值 * @return 接口實現(xiàn)類 */ public static <T> T getServiceImpl(Class<T> interfaceClass, String sopportCode) { // 如果沒有初始化,則初始化 if (null == serviceImplFactoryMap.get(interfaceClass)) { // 初始化 init(interfaceClass); if (null == serviceImplFactoryMap) { return null; } } // 如果初始化后,還是沒有,則返回null if (null == serviceImplFactoryMap.get(interfaceClass)) { return null; } // 返回接口實現(xiàn)類 return (T)((Map)serviceImplFactoryMap.get(interfaceClass)).get(sopportCode); } /** * 初始化 * * @param interfaceClass 接口 * @author javaself */ protected static <T> void init(Class<T> interfaceClass) { log.info("begin to init " + interfaceClass); synchronized (FactoryUtil.class) { try { // 如果已經(jīng)初始化,則返回 if (null != serviceImplFactoryMap.get(interfaceClass)) { return; } Map<String, Object> serviceMap = new HashMap(); // 獲取所有實現(xiàn)類 Map<String, T> beans = appContext.getBeansOfType(interfaceClass); //實現(xiàn)類名字作為key,實現(xiàn)類實例作為value Set<Entry<String, T>> entrySet = beans.entrySet(); Iterator<Entry<String, T>> iterator = entrySet.iterator(); // 遍歷所有實現(xiàn)類,獲取注解@SupportCodes的值,作為key,實現(xiàn)類實例作為value while (iterator.hasNext()) { Object interfaceServiceImpl = ((Entry)iterator.next()).getValue(); SupportCodes annotationValue = (SupportCodes)interfaceServiceImpl.getClass().getAnnotation(SupportCodes.class); if (null != annotationValue) { for (String flag : annotationValue.value()) { //可能有多個注解值,所以遍歷 //寫到map: key=注解值,value=實現(xiàn)類實例 serviceMap.put(flag, interfaceServiceImpl); log.info(flag + "=>" + interfaceServiceImpl.getClass()); } } } //寫到map: key=接口,value=上面的map serviceImplFactoryMap.put(interfaceClass, serviceMap); } catch (Exception e) { log.error("init failed", e); throw new RuntimeException(e); } } log.info(interfaceClass + "inited"); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { appContext = (AbstractApplicationContext)applicationContext; } }
注解類
package com.xxx.commons.factory; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({java.lang.annotation.ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface SupportCodes { String[] value(); }
接口實現(xiàn)類
/** * 微信公眾號支付 * */ @SupportCodes("WeChatPay") @Service public class WeChatPayService implements IGetPayInstructionService {
以上就是Java動態(tài)獲取實現(xiàn)類的方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Java動態(tài)獲取實現(xiàn)類的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mockito 結(jié)合 Springboot 進(jìn)行應(yīng)用測試的方法詳解
這篇文章主要介紹了Mockito 結(jié)合 Springboot 進(jìn)行應(yīng)用測試的方法詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11idea2022創(chuàng)建javaweb項目步驟(超詳細(xì))
本文主要介紹了idea2022創(chuàng)建javaweb項目步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07idea springboot遠(yuǎn)程debug的操作方法
這篇文章主要介紹了idea springboot遠(yuǎn)程debug的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10詳解SpringBoot?Start組件開發(fā)之記錄接口日志信息
這篇文章主要為大家介紹了SpringBoot-Start組件開發(fā)之記錄接口日志信息詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Spring?Boot中的max-http-header-size配置方式
這篇文章主要介紹了Spring?Boot中的max-http-header-size配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09