1兢仰、Java SPI機(jī)制 ServiceLoader的基本使用
-
Java SPI 實(shí)際上是“基于接口的編程+策略模式+配置文件”組合實(shí)現(xiàn)的動(dòng)態(tài)加載機(jī)制嬉愧,
提供了通過(guò)interface尋找implement的方法贩挣。類似于IOC的思想,將裝配的控制權(quán)移到程序之外,從而實(shí)現(xiàn)解耦王财。
Java SPI機(jī)制 場(chǎng)景:比如兩個(gè)module卵迂,app依賴basic_live,但這時(shí)basic_live又用到了app模塊中的服務(wù)绒净。
源碼GitHub
2见咒、方式一
- 在調(diào)用module中創(chuàng)建一個(gè)接口文件ContentService
- 在具體實(shí)現(xiàn)的module中的main目錄(Java同級(jí))下建立resources/META-INF/services/,因?yàn)镾erviceLoader中會(huì)訪問(wèn)到(查看ServiceLoader.PREFIX)
- 在services文件夾中創(chuàng)建文件挂疆,以接口全名命名
- 在具體實(shí)現(xiàn)的module中創(chuàng)建類來(lái)實(shí)現(xiàn)接口功能ContentServiceImpl改览,然后把全類名填到上面創(chuàng)建的文件中
3、方式二
在方式一中缤言,不僅要?jiǎng)?chuàng)建各種路徑宝当、文件,容易寫(xiě)錯(cuò)墨闲,所以Google發(fā)布了一個(gè)庫(kù)autoservice幫助大家
在具體實(shí)現(xiàn)module中添加依賴今妄,kotlin的可以用kpt
implementation 'com.google.auto.service:auto-service:1.0.1' annotationProcessor 'com.google.auto.service:auto-service:1.0.1' kapt 'com.google.auto.service:auto-service:1.0.1'
實(shí)現(xiàn)接口,并加上@AutoService
@AutoService(ContentService.class) public class ContentServiceImpl implements ContentService { @Override public String getTitle() { return "the title from app module"; } }
4鸳碧、使用
在ServiceFactory中加了緩存,通過(guò)
ContentService service = ServiceFactory.getInstance().getService(ContentService.class)
獲取public class ServiceFactory { private static class SingleTonHolder { private static final ServiceFactory INSTANCE = new ServiceFactory(); } public static ServiceFactory getInstance() { return SingleTonHolder.INSTANCE; } private final ArrayMap<Class, ServiceLoader> loaderMap = new ArrayMap<>(); private final ArrayMap<Class, Object> serviceMap = new ArrayMap<>(); private ServiceFactory() { } @Nullable public <T> T getService(Class<T> clazz) { Object o = serviceMap.get(clazz); if (o != null && isInterface(o.getClass(),clazz.getName())) { return (T) o; } ServiceLoader serviceLoader = loaderMap.get(clazz); if (serviceLoader == null) { serviceLoader = ServiceLoader.load(clazz); loaderMap.put(clazz, serviceLoader); } if (serviceLoader != null && serviceLoader.iterator().hasNext()) { T next = (T) serviceLoader.iterator().next(); serviceMap.put(clazz, next); return next; } return null; } public boolean isInterface(Class c, String szInterface) { Class[] face = c.getInterfaces(); for (Class aClass : face) { if (aClass.getName().equals(szInterface)) { return true; } else { Class[] face1 = aClass.getInterfaces(); for (Class value : face1) { if (value.getName().equals(szInterface)) { return true; } else if (isInterface(value, szInterface)) { return true; } } } } if (null != c.getSuperclass()) { return isInterface(c.getSuperclass(), szInterface); } return false; } }