首頁 > 軟體

使用spring動態獲取介面的不同實現類

2022-02-25 13:00:25

spring動態獲取介面的不同實現類

最近做專案,有個需求是和外部對接,從介面獲取新聞資料,把資料和快取中的資料對比,多了的新增到資料庫,少了的刪掉。

介面有兩個,一開始我是在業務類裡寫了兩個方法,程式碼太長,簡單說就是兩個部分:

public Object saveANews() {
	//1、獲取A介面新聞列表
    //2、和快取對比,存資料到資料庫
}
 
public Object saveBNews() {
	//1、獲取B新聞列表
    //2、和快取對比,存資料到資料庫
}

寫完後我發現,因為操作的是資料庫的同一張表,2的部分程式碼完全一模一樣,只有1的部分不同,而1的部分其實就只有一行程式碼。。。

這必須得複用啊,而且是一個業務,也沒必要分別用兩個方法,於是我改成了這樣:

//業務介面實現方法
public Object saveNews(NewsUtilService service) {
	//1、獲取介面新聞列表
    List<NewsVO> list = service.queryNews();
    //2、和快取對比,存資料到資料庫
}
 
//定義新聞資料介面
public interface NewsUtilService {
    List<NewsVO> queryNews();
}
 
//介面的兩個實現類
@Service
public class ANewsDataServiceImpl implements NewsUtilService { 
    @Autowired
	private NewsDataMapper newsDataMapper;
 
    @Override
	public List<NewsVO> queryNews(){
		//對接資料
	}
}
 
@Service
public class BNewsDataServiceImpl implements NewsUtilService {
    @Override
	public List<NewsVO> queryNews(){
		//對接資料
	}
}
 
//定義工廠類
@Service
public class NewsUtilServiceFactory {	
	/**
	 * 
	 * Method Name:  getNewsUtilService
	 * @param source
	 * @return 
	 */
	public NewsUtilService getNewsUtilService(String source){
		switch(source){
			case "a":
				return new ANewsDataServiceImpl();
			case "b":
				return new BNewsDataServiceImpl();
			default:
				return null;
		}
	}
}
 
//控制層呼叫
@RestController
public class NewsDataController {
    @Resource
    private NewsDataService newsDataService;
    
    @Resource
    private NewsUtilServiceFactory factory;
 
    public Object getNewsData(){
        String[] sources = {"a","b"};
    		for (int i = 0; i < sources.length; i++) {
    			NewsUtilService newsUtilService = factory.getNewsUtilService(sources[i]);
    			newsDataService.saveNews(newsUtilService);
			}
    }
}

本來以為這就大工告成了,誰知道執行後控制檯居然報錯了:

經過一步步偵錯,總算髮現了是什麼問題:

其中一個實現類中注入的Mapper沒有範例化,是null。

一開始我還以為是建構函式呼叫和注入的順序問題,查了半天才明白不是,問題在這裡:

使用new關鍵字範例化的物件不是被spring建立的,不歸spring管,所以A類實現類中Mapper注入的註解根本不生效!

但是因為業務需要,那個mapper又需要用到,怎麼辦呢?

當時想到了兩種解決辦法

1、在介面的方法引數里加入mapper,把mapper作為引數傳進去,但這實在太奇怪了,先不說B類實現類根本用不到mapper,而且一個介面定義出來後根本不管它的實現類吧,因為實現類的問題去改介面,,,似乎有點非呀。

於是決定用第二種,修改工廠類變成如下:

//定義工廠類
@Service
public class NewsUtilServiceFactory {	
    @Autowired
    private ANewsDataServiceImpl aNewsDataServiceImpl;
    @Autowired
    private BNewsDataServiceImpl bNewsDataServiceImpl; 
	public NewsUtilService getNewsUtilService(String source){
		switch(source){
			case "a":
				return aNewsDataServiceImpl;
			case "b":
				return bNewsDataServiceImpl;
			default:
				return null;
		}
	}
}

程式碼寫出來自己都無語了,先把所有的實現類都範例化出來,在根據輸入返回。這不是工廠模式,是商店模式吧。。。但是當時也想不到其他辦法,就先這麼寫了,但一直覺得肯定有其他解決方案,直到今天有空,去查了一下,才發現自己都多low。。。

其實spring可以動態獲取實現類的~~~

@Service
public class NewsUtilServiceFactory {	
	@Autowired
	private ApplicationContext applicationContext;	
	public NewsUtilService getNewsUtilService(String source){
		switch(source){
			case "web":
				return applicationContext.getBean(WebNewsDataServiceImpl.class);
			case "oa":
				return applicationContext.getBean(OANewDataServiceImpl.class);
			default:
				return null;
		}
	}
}

這才是正確寫法有木有!

總算弄出來了,趕緊記錄下來先~

獲取某介面所有實現類

在springboot專案中,為了方便,我們可能需要獲取某一個介面下面的所有實現類,根據名稱進行匹配使用。

正文

1、ServiceLocator.java

package com.yang.config;
import com.yang.workOrder.service.IRootService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
 * explain:獲取應用上下文並獲取相應的介面實現類
 *
 * @author yang
 * @date 2021/1/5
 */
@Component
public class ServiceLocator implements ApplicationContextAware {
    /**
     * 用於儲存介面實現類名及對應的類
     */
    private Map<String, IRootService> map;
    /**
     * 獲取應用上下文並獲取相應的介面實現類
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //根據介面型別返回相應的所有bean
        map = applicationContext.getBeansOfType(IRootService.class);
    }
    /**
     * 獲取所有實現集合
     * @return
     */
    public Map<String, IRootService> getMap() {
        return map;
    }
    /**
     * 獲取對應服務
     * @param key
     * @return
     */
    public IRootService getService(String key) {
        return map.get(key);
    }
}

2、IRootService.java

package com.yang.workOrder.service;
import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;
/**
 * explain:基礎流程操作服務介面
 *
 * @author yang
 * @date 2021/1/5
 */
public interface IRootService {
    /**
     * 開始流程
     * @param workOrder
     * @return
     */
    boolean startProcess(WorkOrder workOrder);
}

3、RootA001ServiceImpl.java

package com.yang.workOrder.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;
import com.yang.workOrder.service.IRootService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
 * explain:A_001流程審批實現類
 *
 * @author yang
 * @date 2021/1/5
 */
@Service("A_001")
public class RootA001ServiceImpl implements IRootService {
    private static final Logger LOGGER = LoggerFactory.getLogger(RootA001ServiceImpl.class);
    @Override
    public boolean startProcess(WorkOrder workOrder) {
        return false;
    }
}

4、RootA002ServiceImpl.java

package com.yang.workOrder.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;
import com.yang.workOrder.service.IRootService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
 * explain:A_002流程審批實現類
 *
 * @author yang
 * @date 2021/1/5
 */
@Service("A_002")
public class RootA002ServiceImpl implements IRootService {
    private static final Logger LOGGER = LoggerFactory.getLogger(RootA002ServiceImpl.class);
    @Override
    public boolean startProcess(WorkOrder workOrder) {
        return false;
    }
}

結果

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com