thrift/swift:对swift2thrift-generator-cli IDL生成工具的改进
swift2thrift-generator-cli是thrift/swift提供的一个IDL文件命令行生成工具,它可以根据一个java服务接口类(interface,class)生成对应的IDL文件。
问题描述但是后续的开发过程中发现使用swift2thrift-generator-cli生成IDL有一个问题: 比如一个服务方法: public test(Integer arg);
在生成thrift client代码时,对应的接口方法变成了 @ThriftMethod(value = "test")
public test(@ThriftField(value=1,name="arg",requiredness=Requiredness.NONE) final int arg);
一个类型: @ThriftStruct
public final TestBean{
private Integer id;
@ThriftField(1)
public Integer getId(){
return id;
}
@ThriftField
public void setId(Integer id){
this.id = id;
}
}
在生成thrift client代码时,对应的类变成了: @ThriftStruct("TestBean")
public final TestBean{
private int id;
@ThriftField(value=1,name="id",requiredness=Requiredness.NONE)
public int getId(){
return id;
}
@ThriftField
public void setId(int id){
this.id = id;
}
}
仔细想想这是个大问题:比如我想传一个null参数,在这种情况下这就不可能了, 手工解决办法当然有,地球人都知道的,手工解决办法很简单在服务方法或类定义时加上 public test( @ThriftField(value=1,requiredness=Requiredness.OPTIONAL)Integer arg);
这样在生成的thrift 接口代码中arg参数的类型就是希望的Integer。 我需要自动化解决办法但是如果服务接口如果非常庞大,涉及的类也很多,手工维护这些属性标记就是个灾难。 怎么办呢? 改造目标从swift2thrift-generator-cli源码入门,在此基础上修改swift2thrift-generator-cli生成IDL的逻辑,对于一个字段或参数,如果它是primitive类型,就指定为 问题分析ThriftFieldMetadata通过分析swift的源码发现,不论是类的字段还是服务方法的参数,都是一个field,用 Requiredness在thrift IDL规范中每个field都可以指定必要性( 基本思路了解了上面这个关键点,我的解决方案基本思路成形了: decoratordecorator的实现并不复杂,全部代码如下(代码中用到了google guava提供的cache技术用于减少重复对象创建提高性能,真正核心关键的地方就是 /** * {@link ThriftFieldMetadata}的代理类, * 重载{@link #getRequiredness()}方法,根据参数类型对返回值进行修改 * @author guyadong * */
@Immutable
public class DecoratorThriftFieldMetadata extends ThriftFieldMetadata {
private static final Logger logger = Logger.getLogger(DecoratorThriftFieldMetadata.class.getName());
private static Boolean primitiveOptional = null;
/** * {@link DecoratorThriftFieldMetadata}缓存对象,* 保存每个{@link ThriftFieldMetadata}对应的{@link DecoratorThriftFieldMetadata}实例 */
private static final LoadingCache<ThriftFieldMetadata,DecoratorThriftFieldMetadata>
FIELDS_CACHE =
CacheBuilder.newBuilder().build(
new CacheLoader<ThriftFieldMetadata,DecoratorThriftFieldMetadata>(){
@Override
public DecoratorThriftFieldMetadata load(ThriftFieldMetadata key) throws Exception {
return new DecoratorThriftFieldMetadata(key);
}});
/** 将{@link ThriftFieldMetadata}转换为 {@link DecoratorThriftFieldMetadata}对象 */
public static final Function<ThriftFieldMetadata,ThriftFieldMetadata>
FIELD_TRANSFORMER =
new Function<ThriftFieldMetadata,ThriftFieldMetadata>(){
@Nullable
@Override
public ThriftFieldMetadata apply(@Nullable ThriftFieldMetadata input) {
return null == input || input instanceof DecoratorThriftFieldMetadata
? input
: FIELDS_CACHE.getUnchecked(input);
}};
private final Type javaType;
private DecoratorThriftFieldMetadata(ThriftFieldMetadata input){
super(
input.getId(),input.getRequiredness(),input.getThriftType(),input.getName(),input.getType(),input.getInjections(),input.getConstructorInjection(),input.getMethodInjection(),input.getExtraction(),input.getCoercion());
// 获取field的类型
List<ThriftInjection> injections = getInjections();
checkState(injections.size()>0,"invalid size of injections");
ThriftInjection injection = injections.get(0);
if(injection instanceof ThriftParameterInjection){
javaType = ((ThriftParameterInjection)injection).getJavaType();
}else if(injection instanceof ThriftFieldInjection){
javaType = ((ThriftFieldInjection)injection).getField().getType();
}else{
javaType = null;
// 对于不支持的数据类型无法获取field类型,输出警告
logger.warning(
String.format("UNSUPPORED TYPE %s,can't get Java Type. "
+ "(不识别的ThriftInjection实例类型,无法实现requiredness转义)",null == injection? null : injection.getClass().getName()));
}
}
/** 重载方法,实现 requiredness 转义 */
@Override
public Requiredness getRequiredness() {
Requiredness requiredness = super.getRequiredness();
checkState(Requiredness.UNSPECIFIED != requiredness);
// 当为primitive类型时,Requiredness 为REQUIRED
// 当为primitive类型的Object封装类型时(Long,Integer,Boolean),Requiredness为OPTIONAL
if( !Boolean.FALSE.equals(primitiveOptional)
&& javaType instanceof Class<?>
&& requiredness == Requiredness.NONE){
Class<?> parameterClass = (Class<?>)javaType;
if(parameterClass.isPrimitive()){
requiredness = Requiredness.REQUIRED;
// logger.info(String.format("%s %s",parameterClass.getSimpleName(),requiredness));
}else if(Primitives.isWrapperType(parameterClass)){
requiredness = Requiredness.OPTIONAL;
// logger.info(String.format("%s %s",requiredness));
}
}
return requiredness;
}
/** * 设置optional标记<br> * 指定{@link #getRequiredness}方法调用时是否对primitive类型及其封装类型(Integer,Long)参数的返回值进行替换<br> * 默认值:{@code true}<br> * 该方法只能被调用一次 * @param optional * @see #getRequiredness() * @throws IllegalStateException 方法已经被调用 */
public static synchronized void setPrimitiveOptional(boolean optional) {
checkState(null == DecoratorThriftFieldMetadata.primitiveOptional,"primitiveOptional is initialized already.");
DecoratorThriftFieldMetadata.primitiveOptional = optional;
}
}
偷天换日有了上面的decorator,要让它发挥做用,还要做进一步的工作,需要用将原本对 /** * 重载{@link #getParameters()}方法,用{@link DecoratorThriftFieldMetadata}替换{@link ThriftFieldMetadata} * @author guyadong * */
@Immutable
public class ThriftMethodMetadataCustom extends ThriftMethodMetadata {
private final List<ThriftFieldMetadata> parameters;
public ThriftMethodMetadataCustom(String serviceName,Method method,ThriftCatalog catalog)
{
super(serviceName,method,catalog);
parameters = Lists.transform(super.getParameters(),DecoratorThriftFieldMetadata.requirednessTransformer);
// 这里用到了定义在DecoratorThriftFieldMetadata 中的 Function常量
}
@Override
public List<ThriftFieldMetadata> getParameters()
{
// 返回转换成DecoratorThriftFieldMetadata类型的参数对象表
return parameters;
}
}
对于 /** * {@link ThriftStructMetadata}的代理类<br> * 重载所有{@link ThriftFieldMetadata}相关方法 * @author guyadong * */
@Immutable
public class DecoratorThriftStructMetadata extends ThriftStructMetadata {
/** {@link DecoratorThriftStructMetadata}缓存对象,* 保存每个{@link ThriftStructMetadata}对应的{@link DecoratorThriftStructMetadata}实例 */
private static final LoadingCache<ThriftStructMetadata,DecoratorThriftStructMetadata>
STRUCTS_CACHE =
CacheBuilder.newBuilder().build(
new CacheLoader<ThriftStructMetadata,DecoratorThriftStructMetadata>(){
@Override
public DecoratorThriftStructMetadata load(ThriftStructMetadata key) throws Exception {
return new DecoratorThriftStructMetadata(key);
}});
/** 将{@link ThriftStructMetadata}转换为 {@link DecoratorThriftStructMetadata}对象 */
public static final Function<ThriftStructMetadata,ThriftStructMetadata>
STRUCT_TRANSFORMER = new Function<ThriftStructMetadata,ThriftStructMetadata>(){
@Nullable
@Override
public ThriftStructMetadata apply(@Nullable ThriftStructMetadata input) {
return null == input || input instanceof DecoratorThriftStructMetadata
? input
: STRUCTS_CACHE.getUnchecked(input);
}};
private DecoratorThriftStructMetadata(ThriftStructMetadata input){
super(input.getStructName(),input.getStructType(),input.getBuilderType(),input.getMetadataType(),input.getBuilderMethod(),input.getDocumentation(),ImmutableList.copyOf(input.getFields()),input.getMethodInjections());
}
@Override
public ThriftFieldMetadata getField(int id) {
return DecoratorThriftFieldMetadata.FIELD_TRANSFORMER.apply(super.getField(id));
}
@Override
public Collection<ThriftFieldMetadata> getFields() {
return Collections2.transform(super.getFields(),DecoratorThriftFieldMetadata.FIELD_TRANSFORMER);
}
@Override
public Collection<ThriftFieldMetadata> getFields(FieldKind type) {
return Collections2.transform(super.getFields(type),DecoratorThriftFieldMetadata.FIELD_TRANSFORMER);
}
}
按照 上面的思路,以此类推要换掉在IDL生成过程中涉及ThriftFieldMetadata访问所有环节。就可以了。 完整代码限于篇幅,这里不再贴更多代码,需要完整的代码可以访问码云上的Git仓库: 需要用maven编译,下载代码后执行
同时上面的gitee项目地址中还包含对应的maven插件,更多详细信息参见 <dependency>
<groupId>com.gitee.l0km</groupId>
<artifactId>swift2thrift-maven-plugin</artifactId>
<version>1.7</version>
</dependency>
maven 插件 <dependency>
<groupId>com.gitee.l0km</groupId>
<artifactId>swift2thrift-maven-plugin</artifactId>
<version>1.7</version>
</dependency>
后记那现在可以传递一个类型为Integer的null值到服务端了么?
啊?!!!那不是白干了?那你废半天劲写这一大堆文字干嘛?说说为什么不行啊?
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- Head First 软件开发(Software Development) 7-9 CI, TDD,
- VB6 API声明路径
- c# – ReaderWriterLock在ServiceBehavior构造函数中不起作
- Oracle_071_lesson_p21
- oracle中函数 trunc(),round(),ceil(),floor的使用详解
- 在线检测正则表达式
- zTree组织机构树(涉及技术包含:zTree参数配置,ajax框架,
- ios – 如何在WKWebView历史记录中返回起始点
- ruby-on-rails – 随时随地安排顺序任务
- Swift-Xcode真机运行出现Reason: image not found错误