专栏首页后端沉思录SPI概念及使用方法

SPI概念及使用方法

简介

SPI全称Service Provider Interfaces,用于发现接口的实现。在jdbc、日志、dubbo的设计中都使用SPI用于服务的发现。简单的以jdbc为例:

jdbc Driver实现了java.sql.Driver接口,实现具体的功能,也就是Java SQL framework定义了用于数据库连接接口规范,不同的数据库厂商要想使用Java连接数据库必须实现该接口才可以,当厂商实现后,如何使用呢?也就是上图中的在 META-INF/services/下,配置以接口 java.sql.Driver为名称的文件,文件里加上具体的实现 com.mysql.jdbc.Driver即可,在程序中注册注册驱动即可使用,如在使用jdbc时:

// Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");

加载该jdbc驱动时,会执行静态块,并使用SPI机制在 java.sql.DriverManager中加载 java.sql.Driver的实现类:

// Register ourselves with the DriverManager
static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
}
static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
    String drivers;
    try {
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
        drivers = null;
    }
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next();
                }
            } catch(Throwable t) {
            // Do nothing
            }
            return null;
        }
    });

    println("DriverManager.initialize: jdbc.drivers = " + drivers);
    ...
}

项目中使用

在项目中我们会对外提供接口,为了在controller内内减少接口数量,使用SPI机制去实现相应的功能。

首先定义接口
/**
 * @author wenchao.wang
 * @description
 * @date 2018/1/20 下午10:53
 */
public interface BaseApplication {
}
接口实现
@Component
public class OpenOrderApplication implements BaseApplication {

    @MethodMapping("com.lios.test1")
    public ApiResponse filter(OpenApiJsonObject openApiJsonObject, String appId) {
        return null;
    }
    @MethodMapping("com.lios.test2")
    @CheckField(value = CheckEnum.JSON_OBJECT)
    public ApiResponse<DataVO> orderPush(OpenApiJsonObject openApiJsonObject, String appId) {
        return null;
    }

    @MethodMapping("com.lios.test3")
    public ApiResponse<DataVO> orderfeedback(OpenApiJsonObject openApiJsonObject, String appId) {
      return null;
    }

}
使用SPI机制获取接口实现,并把注解值与方法绑定注册
public class MappingFactory {
private static ConcurrentHashMap<String, MethodApplication> methodMappings = new ConcurrentHashMap<String, MethodApplication>();
private volatile static boolean init = false;
private MappingFactory() {
}
private static void initHandlerMethod() {
    ServiceLoader<BaseApplication> applications = ServiceLoader.load(BaseApplication.class);
    for (BaseApplication application : applications) {
        Method[] methods = application.getClass().getDeclaredMethods();
        for (Method method : methods) {
            MethodMapping methodMapping = method.getAnnotation(MethodMapping.class);
            if (methodMapping != null && methodMapping.value() != null) {

                String mapping = methodMapping.value();
                addMethodMapping(mapping, new MethodApplication(StringUtils.uncapitalize(application.getClass().getSimpleName()), method));
            }
        }
    }
}
private static void addMethodMapping(String mapping, MethodApplication methodApplication) {
    methodMappings.put(mapping, methodApplication);
}
public static MethodApplication getMethodMapping(String url) {
    if (!init) {
        initHandlerMethod();
        init = true;
    }
    return methodMappings.get(url);
 }
}
文件配置

在META-INF/services中添加文件com.lios.base.application.BaseApplication,写入:

com.lios.api.application.OpenApplication
controller中调用
@RestController
public class OpenController {
    private static final Logger LOGGER = LoggerFactory.getLogger(OpenController.class);
    @Autowired
    private Map<String, BaseApplication> baseApplications;
    @RequestMapping(value = "/open/gateway/{method:.+}", method = RequestMethod.POST)
    @ResponseBody
    public OpenApiResponse dispatcher(@PathVariable String method, @RequestBody OpenApiJsonObject openApiJsonObject) {
        ...
        MethodApplication methodApplication = MappingFactory.getMethodMapping(method);
        if (methodApplication != null && baseApplications.get(methodApplication.getApplicationName()) != null) {
            try {
                return (OpenApiResponse) methodApplication.getMethod().invoke(baseApplications.get(methodApplication.getApplicationName()), openApiJsonObject, appId);
            } catch (Exception e) {
               LOGGER.error(e.getMessage(), e);
            }
        }
        ...
    }
}

本文分享自微信公众号 - 后端沉思录(LiosWangs),作者:LiosWong

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自定义参数解析器

    开发中,app端给服务端会传基础参数、其他参数,一般基础参数app端都会传给服务端,其他参数则是根据不同接口传不同参数。若以表单的形式提交的数据:

    LiosWong
  • 动态代理(一)

    代理模式是Java的一种设计模式,开发中可能会有一种场景,某个类的方法需要补充,但是由于不想在原有的类基础上改动,该如何做呢,如下:

    LiosWong
  • BeanDefinitionRegistryPostProcessor

    最近在看spring集成mybatis的代码,发现MapperScannerConfigurer实现了BeanDefinitionRegistryPostPro...

    LiosWong
  • java浅拷贝和深拷贝(基础也是很重要的)

    对于的github基础代码https://github.com/chywx/JavaSE

    陈灬大灬海
  • 在Java中12个常见的语法糖!

    本文从 Java 编译原理角度,深入字节码及 class 文件,抽丝剥茧,了解 Java 中的语法糖原理及用法,帮助大家在学会如何使用 Java 语法糖的同时,...

    Java3y
  • ulimit命令详解

    ulimit用来限制每个用户可使用的资源,如CPU、内存、句柄等。下面以CentOS 6.5为例进行总结。

    用户5807183
  • WPF自学入门(十一)WPF MVVM模式Command命令

    在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新。但是这并不是我们使用MVVM的正确方式。正如上一...

    黄昏前黎明后
  • 不懂这12个语法糖,别说你会Java!

    本文从 Java 编译原理角度,深入字节码及 class 文件,抽丝剥茧,了解 Java 中的语法糖原理及用法,帮助大家在学会如何使用 Java 语法糖的同时,...

    纯洁的微笑
  • IEEE Trans 2006 使用K-SVD构造超完备字典以进行稀疏表示(稀疏分解)

    K-SVD可以看做K-means的一种泛化形式,K-means算法总每个信号量只能用一个原子来近似表示,而K-SVD中每个信号是用多个原子的线性组合来表示的。 ...

    闪电gogogo
  • [javaSE] IO流(对象序列化)

    获取ObjectOutputStream对象,new出来,构造参数:FileOutputStream对象目标文件

    陶士涵

扫码关注云+社区

领取腾讯云代金券