提个Bean/ModelGetter及Controller相关方法的小建议

看了之前 jfinal 实现添加@JsonBody自动注入json数据 的分享,想在这里提个小建议:

现有的实现里面,对于Action的参数的注入,如果内容不是form-urlencoded的兼容的,比如application/json,application/xml,甚至是Protobuf/Hessian/私有协议等,如果没有泛型,那么通过覆盖controller的getBean/getModel方法可以完美实现,但如果入参有泛型信息,就不好处理了,如果作为移动/IOT应用的后端,使用json/Protobuf等协议,并且带如下泛型信息的入参类型是非常常见的:

public class Req<T> {

    // 公共入参

    private String sig;

    private String token;

    private T payload;

    // ....

}

Controller里action的入参:

public class HelloController extends MyBaseController {

    public void hello(Req<Hello> req) {

       // ...

    }

}

由于现有的(jfinal 4.8版本或更早)实现里,getBean/getModel的入参Class<T>实际上是没有带上运行时泛型信息的,如果在MyBaseController里覆盖getBean/getModel方法,那么Hello这个泛型信息就拿不到了,没法通用的创建Req<Hello>的实例了,只能创建Req<Object>,所以处理起来就没那么方便了,如果放弃覆盖getBean/getModel,通过Interceptor也可以实现功能,但浪费了Bean/ModelGetter创建的实例.

所以如果可能,是不是可以作以下修改呢?

BeanGetter/ModelGetter这两个类,modelClass的类型声明为Type,controller的getBean/getModel的入参Class<T>也修改为Type,ParaProcessorBuilder类的101行

Class<?> typeClass = p.getType();

修改为

Type typeClass = p.getParameterizedType();

由于Class实现了Type接口,所以这个改动影响非常少(现有的其他代码不需要任何修改),并且,如果没有泛型信息时,p.getParameterizedType的返回跟getType返回是一样的(参考java.lang.reflect.Executable源码),实际使用modelClass的时候也可以通过转型得到原有的Class<T>.

这样就更通用,使用JFinal时代码也列简洁了.

评论区

JFinal

2020-03-15 17:31

这个建议总体方向上很好,需要具体去实现验证

最重要的是不能对现有代码有影响,由于 jfinal 已迭代发展八年多,有大量的用户,大量的项目需要照顾到升级、兼容

谢谢反馈

JFinal

2020-03-15 17:33

已在 jfinal 源码中添加了 issue,下一版迭代的时候时间如果充裕的话再开发:
https://gitee.com/jfinal/jfinal/issues/I1BO88

registernet

2020-03-15 18:25

@JFinal 非常感谢,^_^

registernet

2020-03-15 18:44

@JFinal 实测还是有影响的:
@Override
public void tryGeneric(Class clz) {
}
这样写会提示未实现父类方法,必须要一致一签名才算覆盖.一个不那么漂亮但可行的方案是为Bean/ModelGetter添加字段,为controller添加Type入参的方法,在ParaProcessorBuilder通过setter将parameterized的字段设值,然后在controller的getBean/getModel方法调用新加的支持泛型的方法.

JFinal

2020-03-15 20:31

@registernet 需要细心设计

无翼

2020-03-23 15:37

https://gitee.com/wukongcrm/72crm-java/blob/master/src/main/java/com/kakarote/crm9/common/config/paragetter/BasePageRequest.java

无翼

2020-03-23 15:44

@无翼 可以参考下这个,通过paragetter实现的
https://gitee.com/wukongcrm/72crm-java/blob/master/src/main/java/com/kakarote/crm9/common/config/paragetter/PageParaGetter.java

registernet

2020-03-27 09:59

@无翼 @JFinal 翻了一下JFinal的源码,参照ParaProcessorBuilder的createParaGetter方法可知 @无翼 的这个方案是可靠的,ParaProcessorBuilder的createParaGetter方法优先取对应class的Getter然后再取内置getter,不过从这里也看出需要提前注册自定义的Getter到ParaProcessorBuilder,即ParaProcessorBuilder.me.regist,只有注册了的类可以正确转换,接近完美了,只差在有子类的情况下如果只注册父类是不够的,需要每一个具体的类都注册一下,在项目的Config入口注册就行了.如果改造BeanGetter/ModelGetter则不需要注册,但改造BeanGetter/ModelGetter会相对麻烦些.非常感谢.

registernet

2020-03-27 10:06

@无翼 PageParaGetter存在扩展性的小问题(你的项目没用到,所以其实目前为止肯定是没问题的),如果controller的入参声明为BasePageRequest>这种TypedParameter比较复杂的时候,会出问题,要么can not cast to JSONObject,要么expect '[', but {, 所以如果使用这个PageParaGetter来接收List,要将它放到一个对象里面包装一下,如BasePageRequest, 然后public class SomeListParam{List list;},或者修改PageParaGetter进行支持.另一个小问题是结构被改变了,入参的结构与接收的BasePageRequest结构不一致,与项目新人交接的时候可能需要解释一下.

registernet

2020-03-27 10:13

BasePageRequest<List<SomeOtherDomain>>
BasePageRequest<SomeListParam>
public class SomeListParam{
List<SomeOtherDomain> list;
}

热门反馈

扫码入社