Dataway接口配置服务扩展能力说明文档。以下是实例。
1.CompilerSpiListener
CompilerSpiListener 也叫做编译拦截器,DataQL 在真正执行查询之前调用。可以使用这个拦截器来实现数据库方言的配置,这样前台就可以只关注 SQL 了。
// 编译拦截器(统一处理SQL方言) apiBinder.bindSpiListener(CompilerSpiListener.class, new CompilerSpiListener() { @Override public QIL compiler(ApiInfo apiInfo, String query, DataQL dataQl) throws IOException { if (query.contains(DataBaseType.MySQL.value)) { query = "hint FRAGMENT_SQL_PAGE_DIALECT = \"mysql\" ;" + query; } else { query = "hint FRAGMENT_SQL_PAGE_DIALECT = \"postgresql\" ;" + query; } return dataQl.compilerQuery(query); } });
2.FxSqlCheckChainSpi
FxSqlCheckChainSpi 是 4.2.0 加入的新特性,应用可以通过这个扩展点对于执行 @@sql 时对 SQL 进行检查和改写。可以用来查看执行的 SQL 或统一修改某些规则。
// 如果存在后续 那么继续执行检查,否则使用 EXIT 常量控制退出后续的检查。 apiBinder.bindSpiListener(FxSqlCheckChainSpi.class, infoObject -> { System.out.println(String.format("[%s] %s", infoObject.getSourceName(), infoObject.getQueryString().trim())); // 可以用来查看执行的 SQL 或统一修改某些规则。 return FxSqlCheckChainSpi.NEXT; });
3.ResultProcessChainSpi
一个已经发布的接口被调用之后,一定会触发这个拦截器。而 ResultProcessChainSpi 拦截器的处理有两个方法,分别应对了两个不同的情况:
- callAfter:结果拦截,用于处理 Query 正确执行之后的二次结果处理。
- callError:异常拦截,当 Query 执行发生异常时(也可以用来记录错误日志)。
// 返回结果封装 apiBinder.bindSpiListener(ResultProcessChainSpi.class, new ResultProcessChainSpi() { @Override public Object callAfter(boolean formPre, ApiInfo apiInfo, Object result) { return BaseResult.getInstance(result); } });
// 所有返回的结果,都把 API 的 Method 和 path 返回 apiBinder.bindSpiListener(ResultProcessChainSpi.class, new ResultProcessChainSpi() { @Override public Object callError(boolean formPre, ApiInfo apiInfo, Throwable e) { return new HashMap<String, Object>() {{ put("method", apiInfo.getMethod()); put("path", apiInfo.getApiPath()); put("errorMessage", e.); }}; } });
实现调用缓存
public class ApiCacheSpi implements PreExecuteChainSpi, ResultProcessChainSpi { private Map<String,Object> cacheMap = ... // for example public void preExecute(ApiInfo apiInfo, BasicFuture<Object> future) { String cacheKey = ... if (this.cacheMap.containsKey(cacheKey)) { Object cacheValue = cacheMap.get(cacheKey); future.completed(cacheValue); return; } } public Object callAfter(boolean formPre, ApiInfo apiInfo, Object result) { // formPre 为 true,表示 preExecute 已经处理过。 // apiInfo.isPerform() 为 true 表示,API 调用是从 UI 界面发起的。 if (formPre || apiInfo.isPerform()) { return result; } // String cacheKey = ... this.cacheMap.put(cacheKey, result); return result; } }
4.SerializationChainSpi
SerializationChainSpi 是 4.1.7 加入的新特性,这个接口允许开发者自定义 Dataway 结果的序列化逻辑。它的使用场景如下:
- 修改 JSON 序列化的规则,例如:不输出为空的列。
- 自定义输出格式。例如:使用 XML来作为响应结果,或者使用 RPC 的序列化协议。
// 不输出为空数据 apiBinder.bindSpiListener(SerializationChainSpi.class, (apiInfo, mimeType, result) -> JSON.toJSONString(result)); // 二进制序列化 apiBinder.bindSpiListener(SerializationChainSpi.class, (apiInfo, mimeType, result) -> { BufferedImage bi = new BufferedImage(150, 70, BufferedImage.TYPE_INT_RGB); //高度70,宽度150 Graphics2D g2 = (Graphics2D) bi.getGraphics(); // background color g2.fillRect(0, 0, 150, 70); g2.setColor(Color.WHITE); // text g2.setFont(new Font("宋体", Font.BOLD, 18)); g2.setColor(Color.BLACK); g2.drawString(String.valueOf(result), 3, 50); // save to bytes ByteArrayOutputStream oat = new ByteArrayOutputStream(); try { ImageIO.write(bi, "JPEG", oat); } catch (IOException e) { e.printStackTrace(); } // response return SerializationChainSpi.SerializationInfo.ofBytes(// mimeType.getMimeType("jpeg"), // response context-type oat.toByteArray() // response body ); });