springboot如何通過不同的策略動態調用不同的實現類
通過不同的策略動態調用不同的實現類
經常遇到這樣的一個需求,前端傳的實體類型相同,後端需要根據實體類中的某一個字符串,動態地調用某一個類的方法。
在SpringBoot中,我們可以理解成,一個Controller接口對應多個ServiceImpl,使用這種方式,如果後期需要添加一個功能,僅僅創建一個ServiceImpl就可以滿足需求,而不用再額外創建一個Controller接口。
現在假設一個情景,前端傳入不同的用戶類型,後端返回該用戶的任務。
你可能問我,為什麼不直接把(用戶類型,用戶任務)存入數據庫?
現在隻是一個簡單的場景而已,實際中更為復雜,無法直接存入數據庫。
代碼演示
我們先定義一個接口
public interface UserService { //返回用戶的主要任務 String task(); }
兩個實現類
@Service("student") public class StudentServiceImpl implements UserService { @Override public String task() { return "學習"; } }
@Service("teacher") public class TeacherServiceImpl implements UserService { @Override public String task() { return "教書"; } }
實現動態調用的核心類
@Service public class UserContext { @Autowired Map<String, UserService> userMap; public UserService getUserService(String type) { return userMap.get(type); } }
Spring會自動地將形如(@Service後面的名稱,實現該接口的類)註入到該userMap中
在啟動後,userMap中就存在兩個元素,("student",StudentServiceImpl)與("teacher",TeacherServiceImpl)
getUserService方法返回userMap中key=type的UserService對象
實體類
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(); } }
測試樣例:
可能用到的場景舉例
關於庫存的儀表盤統計
前端傳入區域id,倉庫id,物品id等信息
後端依據參數動態地選擇某一個物品實現類,最後返回統計的信息。
這裡有幾個問題,為什麼不一次性將所有物品id傳入,一次性獲取所有物品的庫存?
一次性傳入,可能後端處理時間邊長,失敗率也高,一旦失敗,整個儀表盤沒有任何數據。而且後期可能面臨的一個需求,不同的物品,需要有不同的接口刷新速度,暢銷的物品接口調用頻率快。所以可能需要將物品分組,一個小組是同一種類型,使用一個實現類。
比如,這裡有100種物品,按類型或者其他屬性分成瞭10組,每個組之間,有一個不同的屬性groupId,但10組共用一個接口,進入接口後,再進入10個不同的實現類,在實現類中調用具體的計算邏輯。
spring中動態選擇實現類
在spring中當一個接口有多個實現類的時候,通過創建簡單工廠類,根據傳入的不同的參數獲取不同的接口實現類。
public interface ExecuteService { ExecuteEnum getCode(); // 業務方法 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)); } }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Spring入門到精通之Bean標簽詳解
- SpringBoot @Autowired註解註入規則介紹
- java基於RMI遠程過程調用詳解
- Java 中的控制反轉(IOC)詳解
- Java基礎之Spring5的核心之一IOC容器