springboot如何通過不同的策略動(dòng)態(tài)調(diào)用不同的實(shí)現(xiàn)類
通過不同的策略動(dòng)態(tài)調(diào)用不同的實(shí)現(xiàn)類
經(jīng)常遇到這樣的一個(gè)需求,前端傳的實(shí)體類型相同,后端需要根據(jù)實(shí)體類中的某一個(gè)字符串,動(dòng)態(tài)地調(diào)用某一個(gè)類的方法。
在SpringBoot中,我們可以理解成,一個(gè)Controller接口對應(yīng)多個(gè)ServiceImpl,使用這種方式,如果后期需要添加一個(gè)功能,僅僅創(chuàng)建一個(gè)ServiceImpl就可以滿足需求,而不用再額外創(chuàng)建一個(gè)Controller接口。
現(xiàn)在假設(shè)一個(gè)情景,前端傳入不同的用戶類型,后端返回該用戶的任務(wù)。
你可能問我,為什么不直接把(用戶類型,用戶任務(wù))存入數(shù)據(jù)庫?
現(xiàn)在只是一個(gè)簡單的場景而已,實(shí)際中更為復(fù)雜,無法直接存入數(shù)據(jù)庫。
代碼演示
我們先定義一個(gè)接口
public interface UserService { //返回用戶的主要任務(wù) String task(); }
兩個(gè)實(shí)現(xiàn)類
@Service("student") public class StudentServiceImpl implements UserService { ? ? @Override ? ? public String task() { ? ? ? ? return "學(xué)習(xí)"; ? ? } }
@Service("teacher") public class TeacherServiceImpl implements UserService { ? ? @Override ? ? public String task() { ? ? ? ? return "教書"; ? ? } }
實(shí)現(xiàn)動(dòng)態(tài)調(diào)用的核心類
@Service public class UserContext {? ? ? @Autowired ? ? Map<String, UserService> userMap; ? ? ? public UserService getUserService(String type) { ? ? ? ? return userMap.get(type); ? ? } }
Spring會(huì)自動(dòng)地將形如(@Service后面的名稱,實(shí)現(xiàn)該接口的類)注入到該userMap中
在啟動(dòng)后,userMap中就存在兩個(gè)元素,("student",StudentServiceImpl)與("teacher",TeacherServiceImpl)
getUserService方法返回userMap中key=type的UserService對象
實(shí)體類
public class User { private String type; private String task; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getTask() { return task; } public void setTask(String task) { this.task = task; } }
Controller層接口
@RestController @RequestMapping("/user") public class UserController { @Autowired UserContext userContext; @PostMapping("/getTask") public String getTask(@RequestBody User user) { UserService userService = userContext.getUserService(user.getType()); return userService.task(); } }
測試樣例:
可能用到的場景舉例
關(guān)于庫存的儀表盤統(tǒng)計(jì)
前端傳入?yún)^(qū)域id,倉庫id,物品id等信息
后端依據(jù)參數(shù)動(dòng)態(tài)地選擇某一個(gè)物品實(shí)現(xiàn)類,最后返回統(tǒng)計(jì)的信息。
這里有幾個(gè)問題,為什么不一次性將所有物品id傳入,一次性獲取所有物品的庫存?
一次性傳入,可能后端處理時(shí)間邊長,失敗率也高,一旦失敗,整個(gè)儀表盤沒有任何數(shù)據(jù)。而且后期可能面臨的一個(gè)需求,不同的物品,需要有不同的接口刷新速度,暢銷的物品接口調(diào)用頻率快。所以可能需要將物品分組,一個(gè)小組是同一種類型,使用一個(gè)實(shí)現(xiàn)類。
比如,這里有100種物品,按類型或者其他屬性分成了10組,每個(gè)組之間,有一個(gè)不同的屬性groupId,但10組共用一個(gè)接口,進(jìn)入接口后,再進(jìn)入10個(gè)不同的實(shí)現(xiàn)類,在實(shí)現(xiàn)類中調(diào)用具體的計(jì)算邏輯。
spring中動(dòng)態(tài)選擇實(shí)現(xiàn)類
在spring中當(dāng)一個(gè)接口有多個(gè)實(shí)現(xiàn)類的時(shí)候,通過創(chuàng)建簡單工廠類,根據(jù)傳入的不同的參數(shù)獲取不同的接口實(shí)現(xiàn)類。
public interface ExecuteService {?? ? ?? ?ExecuteEnum getCode(); ? ? // 業(yè)務(wù)方法 ? ? void execute(); }
@Service public class FirstExecuteServiceImpl implements ExecuteService {?? ? ?? ?@Override ?? ?public ExecuteEnum getCode() { ?? ??? ?return ExecuteEnum.FIRST; ?? ?} ?? ? ?? ?public void execute() { ?? ? ? ?System.out.println("11111111111"); ?? ?} }
@Service public class SecondExecuteServiceImpl implements ExecuteService {?? ? ?? ?@Override ?? ?public ExecuteEnum getCode() { ?? ??? ?return ExecuteEnum.SECOND; ?? ?} ?? ? ?? ?public void execute() { ?? ? ? ?System.out.println("222222222"); ?? ?} }
?? ?public enum ExecuteEnum { ?? ??? ?FIRST, ?? ??? ?SECOND,; ?? ?}
方案一
@Component public class ExecuteServiceFactory implements ApplicationContextAware { ?? ? ?? ?private final static Map<ExecuteEnum, ExecuteService> EXECUTE_SERVICES = new HashMap<>(); ?? ?@Override ?? ?public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ?? ??? ?Map<String, ExecuteService> types = applicationContext.getBeansOfType(ExecuteService.class); ?? ??? ?types.values().forEach(e -> EXECUTE_SERVICES.putIfAbsent(e.getCode(), e)); ?? ?}?? ? }
方案二
@Component public class ExecuteServiceFactory implements InitializingBean { ? ? @Autowired ? ? private List<ExecuteService> executeServices; ? ? public final static Map<ExecuteEnum, ExecuteService> EXECUTE_SERVICES = new HashMap<>(); ? ? @Override ? ? public void afterPropertiesSet() throws Exception { ? ? ? ? executeServices.forEach(l -> EXECUTE_SERVICES.putIfAbsent(l.getCode(), l)); ? ? } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Eureka注冊不上或注冊后IP不對(多網(wǎng)卡的坑及解決)
這篇文章主要介紹了Eureka注冊不上或注冊后IP不對(多網(wǎng)卡的坑及解決),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Java實(shí)現(xiàn)基于token認(rèn)證的方法示例
這篇文章主要介紹了Java實(shí)現(xiàn)基于token認(rèn)證的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Java中使用回調(diào)函數(shù)的方法實(shí)例
本文主要介紹了Java中使用回調(diào)函數(shù)的方法實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05淺談Java中Lock和Synchronized的區(qū)別
這篇文章主要介紹了Java中Lock和Synchronized的區(qū)別,Lock和Synchronized都是java中去用來解決線程安全問題的一個(gè)工具,但是具體有什么區(qū)別呢?下面我們一起進(jìn)入文章了解具體詳細(xì)內(nèi)容吧,需要的朋友可以參考一下2022-04-04解析使用jdbc,hibernate處理clob/blob字段的詳解
本篇是對使用jdbc,hibernate處理clob/blob字段進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Java中的StackOverflowError錯(cuò)誤問題及解決方法
這篇文章主要介紹了Java中的StackOverflowError錯(cuò)誤,在本文中,我們仔細(xì)研究了StackOverflower錯(cuò)誤,包括Java代碼如何導(dǎo)致它,以及我們?nèi)绾卧\斷和修復(fù)它,需要的朋友可以參考下2022-07-07