Building Microservices with Spring Boot and Apache Thrift. Part 2. Swifty services
http://bsideup.blogspot.com/2015/04/spring-boot-thrift-part2.html
In previous article I showed you how to use pure Apache Thrift to create protocols for your services. This time I will explain how to use Facebook Swift (not the best name for project BTW :)) to get rid of Thrift IDL files for server code.
Few words about Swift. Swift - Java annotation-based generator for Apache Thrift. It's priceless when you develop your APIs in Java and want to expose them to the other world using Apache Thrift.
Nothing special, Spring Boot with few Facebook Swift libraries.
Next we need to add Swift protocol classes:
Exception is simple Swift struct which extend Exception (See Ma no TException)
Enums are handled with Swift, so we don't need to annotate them (but we can)
Next - application main class for Spring Boot:
As you can see, only difference here (compared to Thrift version) is setUp method.
Diff with Thrift version
Diff with Thrift version
Now if you will run tests you should see all tests green.
Example output for our service will be:
Next time I will show you how to write Async Thrift services using Swift with minimal changes. Stay tuned!
Few words about Swift. Swift - Java annotation-based generator for Apache Thrift. It's priceless when you develop your APIs in Java and want to expose them to the other world using Apache Thrift.
Protocol
Lets recreate our previous example ( https://github.com/bsideup/spring-boot-thrift ) with it. Start with simple build.gradle file:apply plugin: 'java' | |
apply plugin: 'spring-boot' | |
buildscript { | |
repositories { | |
jcenter() | |
} | |
dependencies { | |
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE") | |
} | |
} | |
repositories { | |
jcenter() | |
} | |
sourceCompatibility = 1.8 | |
targetCompatibility = 1.8 | |
dependencies { | |
compile "org.projectlombok:lombok:1.16.2" | |
compile("org.springframework.boot:spring-boot-starter-web") | |
compile 'com.facebook.swift:swift-annotations:0.14.1' | |
compile 'com.facebook.swift:swift-service:0.14.1' | |
compile 'com.facebook.swift:swift-codec:0.14.1' | |
compile 'com.facebook.nifty:nifty-core:0.14.1' | |
compile 'org.apache.thrift:libthrift:0.9.1' | |
testCompile "org.springframework.boot:spring-boot-starter-test" | |
} |
Nothing special, Spring Boot with few Facebook Swift libraries.
Next we need to add Swift protocol classes:
package com.example.calculator.protocol; | |
import com.facebook.swift.service.ThriftMethod; | |
import com.facebook.swift.service.ThriftService; | |
import com.google.common.util.concurrent.ListenableFuture; | |
@ThriftService | |
public interface TCalculatorService { | |
@ThriftMethod | |
int calculate(int num1, int num2, TOperation op) throws TDivisionByZeroException; | |
} |
Exception is simple Swift struct which extend Exception (See Ma no TException)
package com.example.calculator.protocol; | |
import com.facebook.swift.codec.ThriftStruct; | |
@ThriftStruct | |
public final class TDivisionByZeroException extends Exception { | |
} |
Enums are handled with Swift, so we don't need to annotate them (but we can)
package com.example.calculator.protocol; | |
public enum TOperation { | |
ADD, | |
SUBTRACT, | |
MULTIPLY, | |
DIVIDE | |
} |
Next - application main class for Spring Boot:
package com.example.calculator; | |
import com.example.calculator.protocol.TCalculatorService; | |
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 org.apache.thrift.protocol.*; | |
import org.apache.thrift.server.TServlet; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | |
import org.springframework.context.annotation.*; | |
import javax.servlet.Servlet; | |
import java.util.Arrays; | |
@Configuration | |
@EnableAutoConfiguration | |
@ComponentScan | |
public class CalculatorApplication { | |
public static void main(String[] args) { | |
SpringApplication.run(CalculatorApplication.class, args); | |
} | |
@Bean | |
TProtocolFactory tProtocolFactory() { | |
return new TBinaryProtocol.Factory(); | |
} | |
@Bean | |
ThriftCodecManager thriftCodecManager() { | |
return new ThriftCodecManager(); | |
} | |
@Bean | |
Servlet thrift(ThriftCodecManager thriftCodecManager, TProtocolFactory protocolFactory, TCalculatorService exampleService) { | |
ThriftServiceProcessor processor = new ThriftServiceProcessor(thriftCodecManager, Arrays.<ThriftEventHandler>asList(), exampleService); | |
return new TServlet( | |
NiftyProcessorAdapters.processorToTProcessor(processor), | |
protocolFactory, | |
protocolFactory | |
); | |
} | |
} |
Test
Now we're ready to write some tests:package com.example.calculator; | |
import com.example.calculator.protocol.TCalculatorService; | |
import com.example.calculator.protocol.TDivisionByZeroException; | |
import com.example.calculator.protocol.TOperation; | |
import com.facebook.nifty.client.HttpClientConnector; | |
import com.facebook.swift.codec.ThriftCodecManager; | |
import com.facebook.swift.service.ThriftClientManager; | |
import org.apache.thrift.protocol.TProtocolFactory; | |
import org.junit.Before; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.boot.test.IntegrationTest; | |
import org.springframework.boot.test.SpringApplicationConfiguration; | |
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; | |
import org.springframework.test.context.web.WebAppConfiguration; | |
import java.net.URI; | |
import static org.junit.Assert.*; | |
@RunWith(SpringJUnit4ClassRunner.class) | |
@SpringApplicationConfiguration(classes = CalculatorApplication.class) | |
@WebAppConfiguration | |
@IntegrationTest("server.port:0") | |
public class CalculatorApplicationTest { | |
@Autowired | |
TProtocolFactory protocolFactory; | |
@Autowired | |
ThriftCodecManager thriftCodecManager; | |
@Value("${local.server.port}") | |
protected int port; | |
protected TCalculatorService client; | |
@Before | |
public void setUp() throws Exception { | |
HttpClientConnector connector = new HttpClientConnector(URI.create("http://localhost:" + port + "/thrift/")); | |
ThriftClientManager clientManager = new ThriftClientManager(thriftCodecManager); | |
client = clientManager.createClient(connector, TCalculatorService.class).get(); | |
} | |
@Test | |
public void testAdd() throws Exception { | |
assertEquals(5, client.calculate(2, 3, TOperation.ADD)); | |
} | |
@Test | |
public void testSubtract() throws Exception { | |
assertEquals(3, client.calculate(5, 2, TOperation.SUBTRACT)); | |
} | |
@Test | |
public void testMultiply() throws Exception { | |
assertEquals(10, client.calculate(5, 2, TOperation.MULTIPLY)); | |
} | |
@Test | |
public void testDivide() throws Exception { | |
assertEquals(2, client.calculate(10, 5, TOperation.DIVIDE)); | |
} | |
@Test(expected = TDivisionByZeroException.class) | |
public void testDivisionByZero() throws Exception { | |
client.calculate(10, 0, TOperation.DIVIDE); | |
} | |
} |
As you can see, only difference here (compared to Thrift version) is setUp method.
Diff with Thrift version
Implementation
We still have no Swift service implementation. Implementation of handler looks almost the same asprevious:package com.example.calculator.handler; | |
import com.example.calculator.protocol.TCalculatorService; | |
import com.example.calculator.protocol.TDivisionByZeroException; | |
import com.example.calculator.protocol.TOperation; | |
import org.springframework.stereotype.Component; | |
import com.example.calculator.service.CalculatorService; | |
import org.springframework.beans.factory.annotation.Autowired; | |
@Component | |
public class CalculatorServiceHandler implements TCalculatorService { | |
@Autowired | |
CalculatorService calculatorService; | |
@Override | |
public int calculate(int num1, int num2, TOperation op) throws TDivisionByZeroException { | |
switch(op) { | |
case ADD: | |
return calculatorService.add(num1, num2); | |
case SUBTRACT: | |
return calculatorService.subtract(num1, num2); | |
case MULTIPLY: | |
return calculatorService.multiply(num1, num2); | |
case DIVIDE: | |
try { | |
return calculatorService.divide(num1, num2); | |
} catch(IllegalArgumentException e) { | |
throw new TDivisionByZeroException(); | |
} | |
default: | |
throw new IllegalArgumentException("Unknown operation " + op); | |
} | |
} | |
} |
Diff with Thrift version
Now if you will run tests you should see all tests green.

Thrift integration
But hey, how about other non-Java consumers of service? Don't worry, Swift comes with a tool for generating *.thrift files from annotated Java classes:https://github.com/facebook/swift/tree/master/swift2thrift-generator-cliExample output for our service will be:
namespace java.swift com.example.calculator.protocol | |
namespace java com.example.calculator.protocol | |
namespace py com.example.calculator.protocol | |
namespace as3 com.example.calculator.protocol | |
enum TOperation { | |
ADD, SUBTRACT, MULTIPLY, DIVIDE | |
} | |
exception TDivisionByZeroException { | |
} | |
service TCalculatorService { | |
i32 calculate(1: i32 arg0, 2: i32 arg1, 3: TOperation arg2) throws (1: TDivisionByZeroException ex1); | |
} |
Conclusion
Full source files for this example can be found at GitHub: https://github.com/bsideup/spring-boot-swiftNext time I will show you how to write Async Thrift services using Swift with minimal changes. Stay tuned!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2013-09-06 SDL2.0 VLC ubuntu安装和黑屏问题
2013-09-06 linux动态库与静态库混合连接 强烈推荐