当前位置: 首页 > 知识库问答 >
问题:

如何摆脱间歇性的非法参数异常,通过使用反射的方法调用

浦泳
2023-03-14

我需要将 POJO 对象转换为其相应的 protobuf 消息。我正在编写一个实用程序,它将使用反射并生成 protobuf 消息。

我有以下 POJO 模型 -

public class Person {
   int id;
   Map<String,String> properties;
   String name;
   Address address;
   Gender gender;
}

public class Address {
   private String line1;
   private String line2;
   private String line3;
   private String City;
   private String state;
}

public enum Gender {
   MALE,FEMALE
}

相应的原型消息 -

message PersonMsg {
  int32 id = 1;
  map<string, string> properties = 2;
  string name = 3;
  AddressMsg address = 4;
  Gender gender = 5;
}

message AddressMsg {
  string line1 = 1;
  string line2 = 2;
  string line3 = 3;
  string city = 4;
  string state = 5;
  string country = 6;
  string pincode = 7;

}

enum Gender{
  Gender_DEFAULT = 0;
  Gender_MALE = 1;
  Gender_FEMALE = 2;
}

转换器逻辑类似于 - 对于标量/基元类型动态调用 gette/setter 并在 protobuf 生成器中填充数据。如果字段是自定义类型(在本例中为地址字段),则递归调用该方法并填充字段 -

private static GeneratedMessageV3 getMessage(Object o) throws ClassNotFoundException
    String protoName = o.getClass().getName()+"Proto";
    Class outerClass = Class.forName(protoName);
    String msgName = protoName+"$"+ o.getClass().getSimpleName()+"Msg";
    Class modelClass = o.getClass();
    Class protoMsg = Class.forName(msgName);

    try {
        Method method = protoMsg.getMethod("newBuilder", null);
        com.google.protobuf.GeneratedMessageV3.Builder builder =  (com.google.protobuf.GeneratedMessageV3.Builder)method.invoke(null, null);

        Field[] fields = o.getClass().getDeclaredFields();
        for(Field field : fields){
            if(field.getType().isPrimitive() || field.getType().isAssignableFrom(String.class)){
                Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
                Object value = getMethod.invoke(o, null);
                Method setMethod = findSetterMethod(builder.getClass(), field);
                setMethod.invoke(builder, value);
            } else if(field.getType().isEnum()){
                Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
                Enum modelEnumValue = (Enum) getMethod.invoke(o, null);
                String protoEnumClassName = field.getType().getName()+"Proto$"+field.getType().getSimpleName();
                Class protoEnumClass = Class.forName(protoEnumClassName);
                Method enumMethod = protoEnumClass.getDeclaredMethod("values", null);
                Enum[] protoValues = (Enum[]) enumMethod.invoke(null, null);
                Enum protoValue = null;
                for(Enum value : protoValues){
                    if(value.name().replace(field.getType().getSimpleName()+"_", "").equals(modelEnumValue.name())){
                        protoValue = value;
                    }
                }
                if(protoValue!=null) {
                    Method setMethod = findSetterMethod(builder.getClass(), field);
                    setMethod.invoke(builder, protoValue);
                }
            } else if(Collection.class.isAssignableFrom(field.getType()) || Map.class.isAssignableFrom(field.getType())){
                if(Map.class.isAssignableFrom(field.getType())){
                    populateMap(o, field, builder);
                }
            } else{
                System.out.println("Field is custom type :"+ field.getName());
                Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
                System.out.println("Found Getter Method :"+ getMethod);
                Object modelValue = getMethod.invoke(o, null);
                System.out.println("Model Value :"+ modelValue);
                Method setMethod = findSetterMethod(builder.getClass(), field);
                setMethod.invoke(builder, getMessage(modelValue));
            }
        }
        GeneratedMessageV3 msg = (GeneratedMessageV3) builder.build();
        return msg;
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}```

此代码工作正常,并将 pojo 对象转换为 protobuf 消息。但有时它会在运行时间歇性地失败 - “java.lang.IllegalArgumentException: 参数类型不匹配”

这在行失败 - “setMethod.invoke(builder, getMessage(modelValue));”

知道如果在运行之间根本没有更改代码,为什么这会失败吗?如何摆脱/解决此异常

共有1个答案

夏学名
2023-03-14

方法“findSetterMethod”中的逻辑是按名称搜索setter方法。因此,如果名称匹配,则返回找到的第一个方法。Protobuf 生成相同名称的方法,但接受不同的参数 - Builder,Protobuf 消息自己。因此,当选择了类型错误的方法时,它曾经失败。修复了代码以检查正确的参数类型及其现在工作正常。

 类似资料:
  • 我正在使用mockito并使用java6和Spring进行开发。

  • 我用java反射写了下面的代码。在这段代码中,我调用了有参数的方法1(),代码运行时没有错误。没关系。但是我如何调用没有参数的方法2()和方法3()呢?如何在java反射中调用没有参数的方法?Java支持这样的功能吗?

  • 我有一个方法如下: 为了通过反射调用此函数,我尝试了以下代码段,但结果不成功: 那么,有没有一种方法可以通过Java反射调用像findEntity这样的方法? 谢谢

  • 问题内容: 考虑以下代码: 我对标有的参数的值感兴趣。假设我已经通过反射(使用)弄清楚了哪个方法参数具有注释。(我知道这是参数列表中的第三个参数。) 现在如何检索值以进一步使用? 问题答案: 你不能 反射无法访问局部变量,包括方法参数。 如果您需要该功能,则需要拦截方法调用,可以通过以下几种方法之一进行操作: AOP(AspectJ / Spring AOP等) 代理(JDK,CGLib等) 在所

  • 问题内容: 我在类中有以下两种方法: 下面的代码行 印刷品,如何使其印刷? 问题答案: 要使用反射调用以原始类型作为参数的方法: 你可以用 要么 同样适用于其他原始类型

  • 问题内容: 我需要获取具有特定批注的字段的值,因此通过反射,我能够获取此Field Object。问题在于,尽管我事先知道它将始终具有getter方法,但该字段将始终是私有的。我知道我可以使用setAccesible(true)并获取其值(当没有PermissionManager时),尽管我更喜欢调用其getter方法。 我知道可以通过查找“ get + fieldName”来查找该方法(尽管例如

  • 我正在处理一个非常简单的point类,但我得到了一个错误,我无法确定字符串/双值问题发生的位置或如何修复它。 编辑 我忘记添加我正在接收的错误:

  • 虽然知道使用反射不是一种好的测试方法,但我正在尝试测试我的代码。我有一个外部类作为public,有一个私有的内部类和一个公共方法,如下所示, 我的主要java类如下所示 这是投掷 线程“main”java中出现异常。lang.NoSuchMethodException:汽车。外部$内部。java上的test()。lang.Class。car上的getDeclaredMethod(Class.jav