Jackson多態反序列化的使用

緣起

最近看Apache Druid的源代碼(0.5很老的版本),印象最深的就是對Jackson的多態反序列化和注入的使用了,這裏也屬於自己的知識盲點,看着複雜的json直接反序列化爲可用對象,直呼過癮。所以一直想找個機會實踐一下,這不需求就來了。

需求

我們的實時指標統計是通過Flink Steam SQL計算爲維度、指標後後入Hbase,然後通過Phoenix SQL給前臺展示。現在新接入Kylin數據源,需要兼容查詢Kylin數據源。

看到這個需求,變化點在查詢方式上,抽象出查詢服務,提供多態支持,但各個指標查詢需要的信息是不同的。如Kylin數據源需要查詢的表名,聚合函數等,但Hbase數據源壓根不需要這些信息,這時候就該Jackson多態反序列化出廠了。

實踐

數據庫新增querySpec字段,描述指標的個性化需求,如{“type”:“rc”,“tableName”:“TASK_SNAPSHOT”,“fieldName”:“EVENTCODE”}
{“type”:“hbase”}

看如下代碼:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

/**
 * Created by yihaibo on 2019-09-11.
 */
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, property = "type", defaultImpl = HbaseQuerySpec.class )
@JsonSubTypes(value = {
        @JsonSubTypes.Type(value = HbaseQuerySpec.class, name = "hbase"),
        @JsonSubTypes.Type(value = RcHttpQuerySpec.class, name = "rc")
})
public interface QuerySpec {
    String getMetricQueryServiceName();
}

/**
* hbase數據源個性化信息
*/
public class HbaseQuerySpec implements QuerySpec {
    @Override
    public String getMetricQueryServiceName() {
        return "hbaseQueryService";
    }
}

/**
* kylin數據源個性化信息(已通過http服務化)
*/
public class RcHttpQuerySpec implements QuerySpec {
    @Getter
    @Setter
    @JsonProperty
    private String tableName;

    @Getter
    @Setter
    @JsonProperty
    private String fieldName;

    @Override
    @JsonIgnore
    public String getMetricQueryServiceName() {
        return "rcMetricQueryService";
    }
}

我們看到新增了getMetricQueryServiceName方法,用於指定處理該數據源的service名稱。

有了個性化參數,可以寫查詢服務了

public interface MetricQueryService {
   /**
     * 
     * @param dataQueryDTO  通用查詢參數
     * @param querySpec  個性化配置信息
     * @return
     */
    List<Map<String,Object>> queryData(DataQueryDTO dataQueryDTO, QuerySpec querySpec);
}

  /**
     * 查詢Hbase原始數據
     * */
@Service("hbaseQueryService")
public class HbaseMetricQueryService implements MetricQueryService {
    @Override
    public List<Map<String,Object>> queryData(DataQueryDTO dataQueryDTO, QuerySpec querySpec){
        ....
    }

  /**
     * 查詢kylin數據
     * */
@Service("rcMetricQueryService")
public class RcMetricQueryService implements MetricQueryService {
    @Override
    public List<Map<String, Object>> queryData(DataQueryDTO dataQueryDTO, QuerySpec querySpec) {
        RcHttpQuerySpec rcHttpQuerySpec = (RcHttpQuerySpec)querySpec;
        ...
 }

反序列化querySpec,獲取服務名稱,然後通過spring獲取到該service bean,傳遞處理參數

QuerySpec querySpec = JsonUtil.decode(querySpec, QuerySpec.class);
String serviceName = querySpec.getMetricQueryServiceName();
MetricQueryService metricQueryService = CtxUtil.getBean(serviceName, MetricQueryService.class);
List<Map<String,Object>> data = metricQueryService.queryData(dataQueryDTO, querySpec);
@Component
@Slf4j
public class CtxUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        applicationContext = ctx;
        log.info("init ctx ok");
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

    public static <T> T getBean(String name, Class<T> clazz) {
        return applicationContext.getBean(name, clazz);
    }
}

反思

Jackson多態反序列化的確是開發力氣,可以去掉不少if else樣例代碼,精簡配置。但感覺用Jackson注入還是不太合適,Jackson對象還是作爲POJO比較合適,邏輯層還是service層或單獨bo去處理,需要注入可以用spring或guice,職則方面,方便test case。

最近在研究Apache Druid,主要學習一些分佈式處理知識,以及想看下列式存儲到底是如何做的。實時OLAP的確用處多多,Apache Kylin也在擴展實時處理,期待兩着的碰撞與融合。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章