前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SPI概念及使用方法

SPI概念及使用方法

作者头像
LiosWong
发布2018-10-29 17:41:58
1K0
发布2018-10-29 17:41:58
举报
文章被收录于专栏:后端沉思录后端沉思录

简介

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时:

代码语言:javascript
复制
// Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");

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

代码语言:javascript
复制
// 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机制去实现相应的功能。

首先定义接口
代码语言:javascript
复制
/**
 * @author wenchao.wang
 * @description
 * @date 2018/1/20 下午10:53
 */
public interface BaseApplication {
}
接口实现
代码语言:javascript
复制
@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机制获取接口实现,并把注解值与方法绑定注册
代码语言:javascript
复制
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,写入:

代码语言:javascript
复制
com.lios.api.application.OpenApplication
controller中调用
代码语言:javascript
复制
@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);
            }
        }
        ...
    }
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 项目中使用
    • 首先定义接口
      • 接口实现
        • 使用SPI机制获取接口实现,并把注解值与方法绑定注册
          • 文件配置
            • controller中调用
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档