使用swift由java类生成thrift文件,再用thrift生成js文件
1.什么是swift
首先,这个swift指的的Facebook的另一个开源项目,thrift的子项目,不是ios的那个swift;swift用于将已有的java代码生成thrift文件,支持java中的string、int、long、double、boolean等基本类型以及枚举、类、异常,不支持java.sql.Timestamp、java.util.Date,但是可以用long替代;相比于thrift,swift可以使用自己定义的实体类,而thrift需要在.thrift文件里定义struct等,生成的代码冗长,不利于重用。
2.如何使用swift
2.1下载swift
swift2thrift-generator,用于将java代码生成thrift文件,下载地址:
http://central.maven.org/maven2/com/facebook/swift/swift2thrift-generator-cli
我自己试的时候首先用的版本是0.23.1,生成thrift的时候莫名其妙报错,后来用的0.15.6才能够正常生成,建议使用0.15.6,较为稳定。
2.2编写java代码
在swift中service一般以接口的形式存在,整个类用@ThriftService注解,方法用@ThriftMethod注解,如果方法可能抛出异常则用@ThriftMethod(exception = {@ThriftException(type = InvalidException.class, id = 1)}),在括号里定义exception,如下:
import com.facebook.swift.service.ThriftMethod;
import com.facebook.swift.service.ThriftService;
import com.facebook.swift.service.ThriftException;
@ThriftService
public interface DbSourceTableMgr {
@ThriftMethod
public ReturnResult add(DbType dbType, String address, int port, String user, String pwd, String desc,
String tablename, boolean ismulti, String multirule, FieldInfo fieldInfo);
@ThriftMethod(exception = {@ThriftException(type = InvalidException.class, id = 1)})
public ReturnResult delTable(String id)throws InvalidException;
}
定义好了服务类,接下来需要定义服务类里面用到的相关实体类和枚举以及异常。用@ThriftStruct注解类、枚举、异常,成员变量的get方法用@ThriftField(1)注解,括号里面的表示字段的ID,这个在thrift文件里有体现。set方法不需要ID。
FieldInfo.java
import com.facebook.swift.codec.ThriftField;
import com.facebook.swift.codec.ThriftStruct;
@ThriftStruct
public final class FieldInfo {
private String fieldName;
private String aliesName;
private String desc;
private int order;
private FieldType fieldType;
@ThriftField(1)
public String getFieldName() {
return fieldName;
}
@ThriftField
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
@ThriftField(2)
public String getAliesName() {
return aliesName;
}
@ThriftField
public void setAliesName(String aliesName) {
this.aliesName = aliesName;
}
@ThriftField(3)
public String getDesc() {
return desc;
}
@ThriftField
public void setDesc(String desc) {
this.desc = desc;
}
@ThriftField(4)
public int getOrder() {
return order;
}
@ThriftField
public void setOrder(int order) {
this.order = order;
}
@ThriftField(5)
public FieldType getFieldType() {
return fieldType;
}
@ThriftField
public void setFieldType(FieldType fieldType) {
this.fieldType = fieldType;
}
}
DbType.java
import com.facebook.swift.codec.ThriftEnum;
@ThriftEnum
public enum DbType {
ORACLE,POSTGRESQL;
}
FieldType.java
import com.facebook.swift.codec.ThriftEnum;
@ThriftEnum
public enum FieldType {
LONGLAT,IMAGE,ID;
}
InvalidException.java
import com.facebook.swift.codec.ThriftConstructor;
import com.facebook.swift.codec.ThriftField;
import com.facebook.swift.codec.ThriftStruct;
@ThriftStruct
public final class InvalidException extends Exception{
/**
*
*/
private static final long serialVersionUID = 1L;
private String id;
private String why;
@ThriftField(1)
public String getId() {
return id;
}
@ThriftField
public void setId(String id) {
this.id = id;
}
@ThriftField(2)
public String getWhy() {
return why;
}
@ThriftField
public void setWhy(String why) {
this.why = why;
}
@ThriftConstructor
public InvalidException(String id, String why) {
super();
this.id = id;
this.why = why;
}
}
构造器用@ThriftConstructor注解
ReturnResult.java
import com.facebook.swift.codec.ThriftConstructor;
import com.facebook.swift.codec.ThriftField;
import com.facebook.swift.codec.ThriftStruct;
@ThriftStruct
public final class ReturnResult {
private String id;
private boolean status;
private String message;
@ThriftConstructor
public ReturnResult(String id, boolean status, String message) {
super();
this.id = id;
this.status = status;
this.message = message;
}
@ThriftField(1)
public String getId() {
return id;
}
@ThriftField
public void setId(String id) {
this.id = id;
}
@ThriftField(2)
public boolean isStatus() {
return status;
}
@ThriftField
public void setStatus(boolean status) {
this.status = status;
}
@ThriftField(3)
public String getMessage() {
return message;
}
@ThriftField
public void setMessage(String message) {
this.message = message;
}
}
2.3生成thrift文件
类全部定义好了,接下来用swift2thrift生成thrift文件, ^ 是windows下的分割符,-package 后跟你java类的包名,-recursive是递归生成,在这里只体现了生成DbSourceTableMgr 相应的thrift文件,但是这个类里面用到了其他的很多实体类,使用了-recursive 命令,就会自动的把用到的类全部生成。命令如下:
java -cp libs/swift2thrift-generator-cli-0.15.6-standalone.jar;bin^ com.facebook.swift.generator.swift2thrift.Main^ -package com.link2map.swift^ DbSourceTableMgr ^ -namespace java com.link2map.swift^ -out DbSourceTableMgr.thrift^ -recursive
其中libs/swift2thrift-generator-cli-0.15.6-standalone.jar;是jar包的位置,bin是class文件的位置。
最后生成的swift文件如下:
namespace java.swift com.link2map.swift
namespace java com.link2map.swift
enum DbType {
ORACLE, POSTGRESQL
}
enum FieldType {
LONGLAT, IMAGE, ID
}
struct ReturnResult {
1: string id;
2: bool status;
3: string message;
}
exception InvalidException {
1: string id;
2: string why;
}
struct FieldInfo {
1: string fieldName;
2: string aliesName;
3: string desc;
4: i32 order;
5: FieldType fieldType;
}
service DbSourceTableMgr {
ReturnResult add(1: DbType arg0, 2: string arg1, 3: i32 arg2, 4: string arg3, 5: string arg4, 6: string arg5, 7: string arg6, 8: bool arg7, 9: string arg8, 10: FieldInfo arg9);
ReturnResult delTable(1: string arg0) throws (1: InvalidException ex1);
}
如何通过thrift文件生成js代码请看前一篇博文。这样前端代码也有了,下面就是使用了。
3.搭建后端服务
3.1实现服务接口
之前定义好了DbSourceTableMgr 这个接口,现在需要将其实现,实现类为MgrImp.java
package com.link2map.swift;
import com.link2map.swift.DbSourceTableMgr;
import com.link2map.swift.FieldInfo;
public class MgrImp implements DbSourceTableMgr{
@Override
public ReturnResult add(DbType dbType, String address, int port, String user, String pwd, String desc,
String tablename, boolean ismulti, String multirule, FieldInfo fieldInfo) {
System.out.println(address+"||");
System.out.println(fieldInfo.getAliesName());
System.out.println(fieldInfo.getFieldName());
System.out.println(dbType==null);
System.out.println(fieldInfo.getFieldType());
return new ReturnResult("12340987",true,"sucess");
}
@Override
public ReturnResult delTable(String id) throws InvalidException{
if(id.equals("10000")) {
throw new InvalidException("10000", "cuole");
}
return new ReturnResult("11111",false,"fail");
}
}
具体的服务就是MgrImp这个类来处理了。接下来是Vertx服务:
package service;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TJSONProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TMemoryBuffer;
import com.facebook.nifty.processor.NiftyProcessor;
import com.facebook.nifty.processor.NiftyProcessorAdapters;
import com.facebook.swift.codec.ThriftCodecManager;
import com.facebook.swift.service.ThriftEventHandler;
import com.facebook.swift.service.ThriftServiceProcessor;
import com.google.common.collect.ImmutableList;
import com.link2map.swift.MgrImp;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.CorsHandler;
public class VertxService extends AbstractVerticle{
public static void main(String[] args) {
Vertx vertx=Vertx.vertx();
Router router = Router.router(vertx);
// 解决跨域问题
router.route().handler(
CorsHandler.create("*").allowedMethod(HttpMethod.GET)
.allowedMethod(HttpMethod.POST)
.allowedMethod(HttpMethod.OPTIONS)
.allowedHeader("X-PINGARUNER")
.allowedHeader("Content-Type"));
router.route("/server/*").handler(context->{
context.request().bodyHandler(buffer->{
byte[] arr=buffer.getBytes();
vertx.executeBlocking(future->{
String result=thriftRequest(arr);
future.complete(result);
}, res->{
try {
if(res.succeeded()) {
context.response().end(res.result().toString());
}else {
context.response().end(res.cause().getMessage());
}
}catch(Exception exception) {
exception.printStackTrace();
}
});
});
});
vertx.createHttpServer().requestHandler(router::accept).listen(8088, res->{
if(res.succeeded()) {
System.out.println("Server starts successfully!");
}else {
System.out.println("Server fails to start!");
}
});
vertx.deployVerticle(new VertxService());
}
private static String thriftRequest(byte[] input){
try{
//Input
TMemoryBuffer inbuffer = new TMemoryBuffer(input.length);
System.out.println("input-length:"+input.length);
inbuffer.write(input);
TProtocol inprotocol = new TJSONProtocol(inbuffer);
//Output
TMemoryBuffer outbuffer = new TMemoryBuffer(100);
TProtocol outprotocol = new TJSONProtocol(outbuffer);
//这里是关键的地方,与之前的thrift不同,需要先构建一个NiftyProcessor ,然后用适配器转化为thrift的TProcessor
NiftyProcessor processor_nifty = new ThriftServiceProcessor(new ThriftCodecManager(),ImmutableList.<ThriftEventHandler>of(),MgrImp.class);
//适配NiftyPorcessor——>Tprocessor
TProcessor processor = NiftyProcessorAdapters.processorToTProcessor(processor_nifty);
processor.process(inprotocol, outprotocol);
byte[] output = new byte[outbuffer.length()];
outbuffer.readAll(output, 0, output.length);
return new String(output,"UTF-8");
}catch(Throwable t){
return "Error:"+t.getMessage();
}
}
}
在这个过程中可能是swift打包jar的问题吧,找不到com.facebook.nifty.core.RequestContext、com.facebook.nifty.processor.NiftyProcessor这两个类,自己下一个nifty-core-0.18.0就行了。
后端构建好了,前端如何使用还是请看前一篇博文,用法完全一样。
4.有关swift和thrift的一点说明
4.1
swift和thrift支持的类型有interface、class、enum、exception,以下基本类型boolean、byte、byte[]、int、long、float、double,String,以及map、list、set。
4.2
thrift的实体类的成员变量不支持java.sql.Timestamp和java.util.Date,在这里我们可以用一个long来保存日期的毫秒数,进行替换。
4.3
实体类的成员变量名与方法名避开thrift的保留字,否则生成thrift文件出错,我现在发现的有delete、list、function;