Nop入门-如何通过配置扩展服务函数的返回对象

讲解视频:www.bilibili.com/video/BV12K...

Nop平台的服务框架是下一代RPC服务框架NopGraphQL。NopGraphQL与普通的REST框架不同,它采用了最小化信息表达的架构设计,将GraphQL的任务分解组合能力推广到了所有RPC服务调用场景。一个POJO的服务函数(只需增加少量注解)可以同时支持GraphQL、REST和gRPC等多种调用方式。

依托于NopGraphQL,采用Nop平台实现的后端服务拥有远超普通Web服务的可扩展性。本文将介绍在不修改DTO类也不修改服务函数的情况下,如何通过配置来扩展服务函数的返回值。

关于NopGraphQL的设计详解,参见为什么在数学的意义上GraphQL严格的优于REST?,业务开发自由之路:如何打破框架束缚,实现真正的框架中立性

使用NopGraphQL实现服务函数非常简单,它使用的注解个数和隐含约定远少于SpringMVC。比如,在下面的示例中,我们定义了一个查询操作

java 复制代码
@BizModel("Demo")
public class DemoBizModel {
  @BizQuery
  public CustomObj testCustomObj(@Name("name") String name) {
    CustomObj obj = new CustomObj();
    obj.setName(name);
    obj.setStatus(1);
    return obj;
  }
}
  • @BizModel注解用于定义服务对象名。多个不同的BizModel类可以对应于同一个服务对象,它们的服务函数会聚合到一起对外暴露。
  • @BizQuery注解表示这是一个查询操作,如果是@BizMutation则表示是修改操作。修改操作会自动打开事务环境,一般不需要手工标注@Transactional
  • @Name注解用于指定服务函数的参数名。与SpringMVC不同,NopGraphQL并没有假定它在Web环境中调用,因此所有概念都与Web无关。具体通过REST服务调用时,可以通过url参数传递参数,也可以通过HTTP JSON Body来传递参数。
  • 服务函数上无需指定REST链接。NopGraphQL在REST调用模式下固定使用/r/{bizObjName}__{bizAction}这一链接模式。返回值会被自动包装为ApiResponse<T>对象。

在上面的示例中,CustomObj具有两个属性name和status。现在要求返回一个status_label字段,它对应于status属性的中文描述。比如,如果status为1,则status_label返回"已启用",如果status为0,则返回"已禁用"。

要实现这一功能,对于传统的RPC框架或者Web框架,肯定是需要修改服务函数的源码,在其中加载字典表然后实现文本翻译。返回的结果对象CustomObj上也需要主动增加status_label字段。 但是,在NopGraphQL中,我们无需修改testCustomObj函数,也不需要修改CustomObj类。生成status_label的逻辑是一个按照确定性的规则进行衍生计算的逻辑,所以它本质上与服务函数中如何构建CustomObj的逻辑是解耦的。 服务函数只需要知道如何加载、构建CustomObj对象就好了,并不需要将所有返回属性都计算出来。在NopGraphQL引擎中,服务函数的返回值并不是是直接返回给前端的DTO数据。服务函数返回的结果对象还需要进行GraphQL引擎的后处理,执行各种各样的DataLoader,最终才会将DataLoader的返回结果合成为一个JSON数据。

如果要扩展返回值对象,增加status_label属性,我们只需要标记CustomObj是一个BizObject,然后在model目录下增加一个Meta元数据定义。

java 复制代码
@BizObjName("CustomObj")
public class CustomObj {
  private String name;
  private int status;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getStatus() {
    return status;
  }

  public void setStatus(int status) {
    this.status = status;
  }

  public boolean isActive(){
    return status == 1;
  }
}
xml 复制代码
<!--  resources/_vfs/nop/demo/model/CustomObj/CustomObj.xmeta -->
<meta x:schema="/nop/schema/xmeta.xdef" xmlns:x="/nop/schema/xdsl.xdef">
  <props>

    <prop name="name" displayName="名称" queryable="true" insertable="true" updatable="true">
      <schema type="String"/>
    </prop>

    <prop name="status" displayName="状态" queryable="true" insertable="true" updatable="true">
      <schema type="Integer"/>
    </prop>

    <prop name="status_label" displayName="状态文本">
      <schema type="String"/>
      <getter>
        <c:script><![CDATA[
            if(entity.status == 1)
                return "已启用";
            return "已禁用";
        ]]></c:script>
      </getter>
    </prop>
  </props>
</meta>
  • 在XMeta文件中可以增加一个自定义属性status_label,通过getter段来编写它的计算逻辑。
  • 在XMeta文件中没有定义的字段,即使在CustomObj上存在,也不会返回到前台。比如CustomObj对象上具有isActive函数,但是CustomObj.xmeta文件中并没有定义active字段,返回结果中也就不会包含这个属性。

一旦启用了XMeta这个DSL模型,那么就可以进一步利用Nop平台提供的元编程能力,通过x:gen-extendsx:post-extends等内置的编译期代码生成器来动态生成扩展逻辑。

xml 复制代码
<meta x:schema="/nop/schema/xmeta.xdef" xmlns:x="/nop/schema/xdsl.xdef"
      xmlns:xpl="xpl" xmlns:meta-gen="meta-gen" xmlns:c="c">

    <x:post-extends>
        <meta-gen:DefaultMetaPostExtends xpl:lib="/nop/core/xlib/meta-gen.xlib"/>
    </x:post-extends>

    <props>

        <prop name="name" displayName="名称" queryable="true" insertable="true" updatable="true">
            <schema type="String"/>
        </prop>

        <prop name="status" displayName="状态" queryable="true" insertable="true" updatable="true">
            <schema type="Integer" dict="core/active-status"/>
        </prop>
    </props>
</meta>

在上面的示例中,我们没有显式添加status_label字段,而是为status字段指定了字典表core/active-statusx:post-extends段调用了<meta-gen:DefaultMetaPostExtends>标签,这个标签引入了一系列的编译期生成规则,会自动查找所有配置了dict的字段,逐个为它们生成对应的label字段。

完整的示例可以参见 nop-spring-simple-demo这个示例项目

Nop平台中所有自动生成数据库实体对象都已经标记为BizObject,并且自动生成了对应的XMeta元数据描述文件。因此所有的增删改查操作的输入输出对象都是可以通过XMeta进行扩展的。

基于可逆计算理论设计的低代码平台NopPlatform已开源:

相关推荐
非ban必选4 小时前
spring-ai-alibaba第七章阿里dashscope集成RedisChatMemory实现对话记忆
java·后端·spring
hello_ejb36 小时前
聊聊Spring AI的RetrievalAugmentationAdvisor
人工智能·spring·restful
inquisiter6 小时前
UEFI镜像结构布局
linux·spring
程序媛学姐7 小时前
SpringKafka错误处理:重试机制与死信队列
java·开发语言·spring·kafka
生无谓8 小时前
SpringAop动态代理和AspectJ静态代理
spring
栗筝i9 小时前
Spring 核心技术解析【纯干货版】- XIX:Spring 日志模块 Spring-Jcl 模块精讲
java·后端·spring
我命由我1234512 小时前
Spring Boot 自定义日志打印(日志级别、logback-spring.xml 文件、自定义日志打印解读)
java·开发语言·jvm·spring boot·spring·java-ee·logback
杉之17 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
小李同学_LHY20 小时前
三.微服务架构中的精妙设计:服务注册/服务发现-Eureka
java·spring boot·spring·springcloud