前置知識
什麼是SPI
之前有寫過一篇文章——>java之spi機制簡介(
http://t。hk。uy/K6N
)不瞭解spi的朋友,可以先查閱這篇文章瞭解下,再閱讀下文
前言
假設大家已經對SPI有一定的瞭解,有使用過JDK提供的SPI的朋友,應該會發現JDK的SPI是無法實現按需載入。那如何解決這個短板問題?
這邊提供2種思路,一種是自己實現一套SPI,另外一種在實現元件很常用的手段,就是當前元件無法滿足時,可以藉助其他元件或者再加代理層。本文實現的思路,就是利用spring的IOC,spring的ioc本質上就是一個鍵值對map,將jdk spi生成的物件注入到spring ioc容器中,間接也擁有了key——>value的對映功能
實現思路
專案啟動時,利用spi載入類並生成物件
將生成的物件注入到spring容器
在業務專案中,使用 @Autowired +
@Qualifier註解,按需引用SPI生成的bean物件
核心程式碼片段
1、spi載入實現
public Map
2、注入spring容器
public class SpiRegister implements ImportBeanDefinitionRegistrar,BeanFactoryAware { private DefaultListableBeanFactory beanFactory; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registerSingleton(importingClassMetadata); } private void registerSingleton(AnnotationMetadata importingClassMetadata) { Class<?> spiInterface = getSpiInterface(importingClassMetadata); if(spiInterface != null){ Map
業務專案如何使用
示例
1、定義spi服務介面
@Spipublic interface HelloService { String sayHello(String username);}
注:
@Spi用來指定哪些spi服務介面需要注入到spring 容器中,同時@Spi還有一個defalutSpiImplClassName屬性,用來指定預設注入spi實現類
2、定義具體實現類
public class HelloServiceCnImpl implements HelloService { @Override @InterceptorMethod(interceptorClasses = {HelloServiceCnInterceptor。class, HelloServiceCnOtherInterceptor。class}) public String sayHello(String username) { return “你好:” + username; }}
public class HelloServiceEnImpl implements HelloService { @Override @InterceptorMethod(interceptorClasses = HelloServiceEnInterceptor。class) public String sayHello(String username) { return “hello:” + username; }}
注:
@InterceptorMethod這個註解是用來做方法增強,和本文的關係不大,可以忽略
3、src/main/resources/下建立/META-INF/services 目錄,新增一個以介面命名的檔案
com。github。lybgeek。spi。HelloService
4、介面命名的檔案填入如下內容
com。github。lybgeek。spi。en。HelloServiceEnImpl
com。github。lybgeek。spi。cn。HelloServiceCnImpl
5、編寫業務controller
@RestController@RequestMapping(“/test”)public class SpiTestController { @SpiAutowired(“helloServiceCnImpl”) private HelloService helloService; @GetMapping(value=“/{username}”) public String sayHello(@PathVariable(“username”) String username){ return helloService。sayHello(username); }}
注:
@SpiAutowired是一個自定義註解,該註解可以看成是@Autowired + @Qualifier
6、啟動類上加@EnableSpi(basePackages = “com。github。lybgeek。spi”)
注:
basePackages用來指定掃描spi的包
7、測試
當 @SpiAutowired(“helloServiceCnImpl”)時,頁面渲染為
當 @SpiAutowired(“helloServiceEnImpl”)時,頁面渲染為
當指定@Autowired @Spi(“com。github。lybgeek。spi。cn。HelloServiceCnImpl”)
此時頁面渲染為
注:
這邊沒有用@SpiAutowired,是因為@SpiAutowired需要指定名稱
總結
本文基於spi按需載入是依賴spring,在某種程度上和spring耦合,有機會的話,再講下如何實現自定義鍵值對SPI
demo連結
https://github。com/lyb-geek/springboot-learning/tree/master/springboot-spi-ioc