Dubbo 泛化调用的参数解析问题及一个强大的参数解析工具 PojoUtils

排查了3个多小时,因为一个简单的错误,发现一个强大的参数解析工具,记录一下。

背景

Nodejs 通过 tether 调用 Java Dubbo 服务。请求类的某个参数对象 EsCondition 有 fieldName, op, value 三个字段,value 的参数值正确解析, fieldName, op 的参数值解析为 null 。 深入到 Dubbo 源码进行单步调试定位到,发现字段 fieldName, op 缺少 setter 方法。 加上之后就正常了。

502996-20190218213157568-1565661774.png


JavaBean约定

定位到问题后,有一种如梦方醒的感觉。问题就是这么简单,可是却花了很多时间。如果我注意到一个类里有 a, b, c 三个字段, c 解析成功了,有 getter/setter 方法,而 b, c 没有 setter 方法,通过仔细对比或许就可以解决这个问题。

事实上, Java 与框架进行交互有个基本的约定: JavaBean 约定, JavaBean 中的每个实例属性都有 setter/getter 方法。完整的约定如下:

  • 有一个 public 默认构造器(尤其要有无参构造器);
  • 属性设置成 private, 使用 public 的 getter,setter 方法访问,同时getter,setter 方法与属性名也需要对应。例如属性 name,getter 方法就要写成 public String getName(){ return name; }
  • 实现序列化接口。

如果缺乏默认构造器,那么在构造 Java 入参对象会有问题; 如果缺失 getter / setter ,在设置或获取入参对象的字段的值就会有问题。事实上,很多框架都使用了反射的方式,通过 getter/setter 方法来操作 JavaBean 中的字段来进行操作。因此,保证 JavaBean 约定,是与众多框架进行交互的重要前提。 而要保证 JavaBean 约定,只要自动生成 setter/getter 方法,或者在类上增加一个 @Getter, @ Setter, 或 @Data 注解即可。JavaBeans 的百科: “JavaBeans”

约定胜于配置。 建立一些约定进行简单处理,往往比灵活而复杂处理各种配置更加容易理解。 读者可阅: “约定优于配置”

PojoUtils

这里有个强大的参数解析类 com.alibaba.dubbo.common.utils.PojoUtils,用于将嵌套的 Map 转换成嵌套的对象。特别实用。PoJoUtils 与 CompatibleTypeUtils, ReflectUtils, ClassHelper 联合实现了这个功能。 这里先给出一个测试用例。

package shared.util;

import com.google.common.collect.ImmutableMap;
import org.junit.Test;
import shared.utils.PojoUtils;
import zzz.study.apidesign.export.common.Condition;
import zzz.study.apidesign.export.common.ExportParam;
import zzz.study.apidesign.export.common.SearchParam;
import zzz.study.patterns.composite.escondition.Match;
import zzz.study.patterns.composite.escondition.Op;
import zzz.study.patterns.composite.escondition.Range;

import java.util.Arrays;
import java.util.Objects;

public class PojoUtilsTest {

  @Test
  public void testRealize() {
    ExportParam exportParam = new ExportParam();
    exportParam.setBizType("order");
    exportParam.setCategory("baozhengjin");
    exportParam.setSource("wsc");
    exportParam.setRequestId("request"+System.currentTimeMillis());
    exportParam.setExtra(ImmutableMap.of("account", "qin"));
    exportParam.setStrategy("standard");
    SearchParam searchParam = new SearchParam();
    searchParam.setBizId(123L);
    searchParam.setStartTime(151456798L);
    searchParam.setEndTime(153456789L);
    searchParam.setConditions(Arrays.asList(
        new Condition("name", Op.eq, "sisi"),
        new Condition("pay_time", Op.range, new Range(152987654L, 153987654)),
        new Condition("state", Op.in, Arrays.asList(5,6)),
        new Condition("goods_title", Op.match, new Match("走过路过不要错过", "100%"))
    ));
    exportParam.setSearch(searchParam);

    Object paramGeneralized = PojoUtils.generalize(exportParam);

    ExportParam exportParamRestored = (ExportParam) PojoUtils.realize(paramGeneralized, ExportParam.class);
    assert Objects.equals(exportParamRestored, exportParam);

  }
}

generalize 将一个对象转换成 Map,在 dubbo 服务端,realize 将 Map 还原为对象。

代码实现

读者可以引用 dubbo 2 以上版本,查看 com.alibaba.dubbo.common.utils.PojoUtils 这个类的实现。里面分不同情况进行了解析,同时使用了递归技术来对嵌套的结果进行解析。

/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.common.utils;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * PojoUtils. Travel object deeply, and convert complex type to simple type.
 * <p>
 * Simple type below will be remained:
 * <ul>
 * <li> Primitive Type, also include <b>String</b>, <b>Number</b>(Integer, Long), <b>Date</b>
 * <li> Array of Primitive Type
 * <li> Collection, eg: List, Map, Set etc.
 * </ul>
 * <p>
 * Other type will be covert to a map which contains the attributes a
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值