java 轻量rpc框架
重要要点
|
HTTP-RPC是一个开放源代码框架,用于简化基于REST的应用程序的开发。 它允许开发人员使用便捷的类似RPC的隐喻来创建和访问基于HTTP的Web服务,同时保留基本的REST原理,例如无状态和统一资源访问。
该项目当前包括对在Java中实现REST服务以及在Java,Objective-C / Swift或JavaScript中使用消费服务的支持。 服务器组件提供了轻巧的替代方案,可以替代其他更大的基于Java的REST框架,使其成为微服务和“物联网”(IOT)应用程序的理想选择。 一致的跨平台客户端API使得与目标设备或操作系统无关的服务交互变得很容易。
通过将HTTP动词(例如GET或POST)应用于目标资源来访问HTTP-RPC服务。 目标由表示资源名称的路径指定,并且通常表示为以URI表示的名词,例如/ calendar或/ contacts。
通过查询字符串或在请求正文中提供参数,例如HTML表单。 尽管通常也支持不返回值的操作,但结果通常以JSON形式返回。
例如,以下请求可能会检索两个数字的和,其值由a和b查询参数指定:
GET /math/sum?a=2&b=4
或者,可以将参数值指定为列表,而不是两个固定变量:
GET /math/sum?values=1&values=2&values=3
无论哪种情况,服务都会作为响应返回值6。
POST,PUT和DELETE操作的访问方式类似。
HTTP-RPC服务器库以单个32KB JAR文件的形式分发,没有外部依赖性。 它包括以下软件包/类:
org.httprpc
WebService-HTTP-RPC服务的抽象基类RPC-指定“远程过程调用”或服务方法的注释
org.httprpc.beans
BeanAdapter-适配器类,将Java Bean实例的内容显示为映射,适用于序列化为JSON
org.httprpc.sql
ResultSetAdapter-适配器类,将JDBC结果集的内容显示为可迭代列表,适用于流式传输到JSON
参数-用于简化已执行语句的执行的类
org.httprpc.util
IteratorAdapter-适配器类,将迭代器的内容显示为可迭代的列表,适用于流式传输到JSON。
这些类的每一个将在下面更详细地讨论。
WebService是HTTP-RPC Web服务的抽象基类。 通过将公共方法添加到具体的服务实现中来定义服务操作。
@RPC批注用于将方法标记为可远程访问。 该注释将HTTP动词和资源路径与该方法相关联。 发布服务后,所有带有公共注释的方法将自动变为可用于远程执行。
例如,以下类可能用于实现上一节中讨论的简单加法运算:
public class MathService extends WebService {
@RPC(method="GET", path="sum")
public double getSum(double a, double b) {
return a + b;
}
@RPC(method="GET", path="sum")
public double getSum(List<Double> values) {
double total = 0;
for (double value : values) {
total += value;
}
return total;
}
}
请注意,这两种方法都映射到路径/ math / sum。 根据提供的参数值的名称选择执行的最佳方法。 例如,以下请求将导致调用第一个方法:
GET /math/sum?a=2&b=4
该请求将调用第二种方法:
GET /math/sum?values=1&values=2&values=3
方法参数可以是任何数字基本类型或包装类,布尔值,java.lang.Boolean或java.lang.String。 参数也可以是java.net.URL或java.util.List的实例。 URL参数代表二进制内容,例如JPEG或PNG图像。 列表参数代表多值参数。 列表元素可以是任何受支持的简单类型。 例如List <Integer>或List <URL>。
方法可以返回任何数字原始类型或包装类,布尔值,java.lang.Boolean或java.lang.CharSequence。 它们还可能返回java.util.List或java.util.Map的实例。
结果映射到其JSON等效项,如下所示:
请注意,列表和地图类型不需要支持随机访问。 迭代性就足够了。 另外,实现java.lang.AutoCloseable的List和Map类型在将其值写入输出流后将自动关闭。 这允许服务实现流式传输响应数据,而不是在写入之前将其缓存在内存中。
例如,org.httprpc.sql.ResultSetAdapter类包装java.sql.ResultSet的一个实例,并将其内容公开为可向前滚动且可自动关闭的地图值列表。 关闭列表还会关闭基础结果集,从而确保不会泄漏数据库资源。
ResultSetAdapter稍后将详细讨论。
WebService提供了以下方法,这些方法允许扩展类获取有关当前请求的其他信息:
getLocale()
-返回与当前请求关联的语言环境
getUserName()
-返回与当前请求关联的用户名,如果请求未通过身份验证,则返回null
getUserRoles()
-返回代表用户所属角色的集合,如果请求未通过身份验证,则返回null
这些方法返回的值通过受保护的设置器填充,每个服务请求调用一次。 尽管这些设置程序并不意味着由应用程序代码调用,但是可以将它们用于促进服务实现的单元测试。
BeanAdapter类允许从服务方法返回Java Bean对象的内容。 此类实现Map接口,并将Bean定义的任何属性公开为地图中的条目,从而允许将自定义数据类型序列化为JSON。
例如,以下Bean类可能用于表示有关值集合的基本统计数据:
public class Statistics {
private int count = 0;
private double sum = 0;
private double average = 0;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public double getSum() {
return sum;
}
public void setSum(double sum) {
this.sum = sum;
}
public double getAverage() {
return average;
}
public void setAverage(double average) {
this.average = average;
}
}
使用此类,getStatistics()方法的实现可能如下所示:
@RPC(method="GET", path="statistics")
public Map<String, ?> getStatistics(List<Double> values) {
Statistics statistics = new Statistics();
int n = values.size();
statistics.setCount(n);
for (int i = 0; i < n; i++) {
statistics.setSum(statistics.getSum() + values.get(i));
}
statistics.setAverage(statistics.getSum() / n);
return new BeanAdapter(statistics);
}
尽管这些值实际上存储在强类型的Statistics对象中,但是适配器使数据显示为映射,从而允许将其作为JSON对象返回给调用方。
请注意,如果属性返回嵌套的Bean类型,则该属性的值将自动包装在BeanAdapter实例中。 此外,如果属性返回List或Map类型,则该值将包装在适当类型的适配器中,该适配器会自动适应其子元素。 这允许服务方法返回诸如树的递归结构。
BeanAdapter可用于轻松地将JPA查询的结果转换为JSON。 在此处可以找到将BeanAdapter与Hibernate一起使用的示例。
ResultSetAdapter类允许从服务方法有效地返回SQL查询的结果。 此类实现List接口,并使JDBC结果集中的每一行显示为Map的实例,从而呈现适合序列化为JSON的数据。 它还实现了AutoCloseable接口,以确保关闭基础结果集并且不会泄漏数据库资源。
ResultSetAdapter仅向前滚动; 它的内容不能通过get()和size()方法访问。 这允许将结果集的内容直接返回给调用方,而无需任何中间缓冲。 调用者可以简单地执行JDBC查询,将结果集传递给ResultSetAdapter构造函数,然后返回适配器实例:
@RPC(method="GET", path="data")
public ResultSetAdapter getData() throws SQLException {
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from some_table");
return new ResultSetAdapter(resultSet);
}
Parameters类提供了一种使用命名参数值而不是索引参数执行准备好的语句的方法。 与JPQL中一样,参数名称由前导:字符指定。 例如:
SELECT * FROM some_table
WHERE column_a = :a OR column_b = :b OR column_c = COALESCE(:c, 4.0)
parse()方法用于从SQL语句创建Parameters实例。 它需要一个包含SQL文本的java.io.Reader作为参数。 例如:
Parameters parameters = Parameters.parse(new StringReader(sql));
Parameters类的getSQL()方法以标准JDBC语法返回已解析SQL:
SELECT * FROM some_table
WHERE column_a = ? OR column_b = ? OR column_c = COALESCE(?, 4.0)
此值用于创建实际的准备好的语句:
PreparedStatement statement = DriverManager.getConnection(url).prepareStatement(parameters.getSQL());
使用apply()方法将参数值应用于语句。 此方法的第一个参数是预处理语句,第二个是包含语句参数的映射:
HashMap<String, Object> arguments = new HashMap<>();
arguments.put("a", "hello");
arguments.put("b", 3);
parameters.apply(statement, arguments);
由于显式创建和填充实参映射可能很麻烦,因此WebService类提供了以下静态便利方法来帮助简化映射的创建:
public static <K> Map<K, ?> mapOf(Map.Entry<K, ?>... entries) { ... }
public static <K> Map.Entry<K, ?> entry(K key, Object value) { ... }
使用便捷方法,可以将应用参数值的代码简化为以下代码:
parameters.apply(statement, mapOf(entry("a", "hello"), entry("b", 3)));
一旦应用,该语句即可执行:
return new ResultSetAdapter(statement.executeQuery());
在此处可以找到一个示例,该示例演示了如何使用ResultSetAdapter和Parameters类访问MySQL数据库。
IteratorAdapter类允许从服务方法有效地返回任意游标的内容。 此类实现List接口,并使迭代器生成的用于序列化为JSON的每个元素,包括嵌套的List和Map结构。 与ResultSetAdapter一样,IteratorAdapter实现AutoCloseable接口。 如果基础迭代器类型也实现了AutoCloseable,则IteratorAdapter将确保基础游标已关闭,以免资源泄漏。
与ResultSetAdapter一样,IteratorAdapter仅向前滚动,因此无法通过get()和size()方法访问其内容。 这允许将游标的内容直接返回给调用方,而无需任何中间缓冲。
IteratorAdapter通常用于序列化NoSQL数据库(例如MongoDB)产生的结果数据。 在此处可以找到将IteratorAdapter与Mongo一起使用的示例。
HTTP-RPC客户端库提供了用于在多个平台上调用服务操作的一致接口。 例如,下面的代码片段演示了如何使用Java客户端的WebServiceProxy类访问前面讨论的math服务的方法。 首先,它创建一个WebServiceProxy实例,并为它配置十个线程池以执行请求。 然后,它调用服务的getSum(double,double)方法,为“ a”传递值2,为“ b”传递值4。 最后,它执行getSum(List <Double>)方法,并将值1、2和3作为参数传递。 像上一节中讨论的WebService类一样,WebServiceProxy提供了静态实用程序方法来帮助简化参数映射的创建:
// Create service
URL serverURL = new URL("https://localhost:8443");
ExecutorService executorService = Executors.newFixedThreadPool(10);
WebServiceProxy serviceProxy = new WebServiceProxy(serverURL, executorService);
// Get sum of "a" and "b"
serviceProxy.invoke("GET", "/math/sum", mapOf(entry("a", 2), entry("b", 4)), new ResultHandler<Number>() {
@Override public void execute(Number result, Exception exception) {
// result is 6
}
});
// Get sum of all values
serviceProxy.invoke("GET", "/math/sum", mapOf(entry("values", listOf(1, 2, 3))), new ResultHandler<Number>() {
@Override public void execute(Number result, Exception exception) {
// result is 6
}
});
结果处理程序是将在请求完成后执行的回调。 在Java 7中,匿名内部类通常用于实现结果处理程序。 在Java 8或更高版本中,可以代替使用lambda表达式,从而将调用代码减少为以下内容:
// Get sum of "a" and "b"
serviceProxy.invoke("GET", "/math/sum", mapOf(entry("a", 2), entry("b", 4)), (result, exception) -> {
// result is 6
});
// Get sum of all values
serviceProxy.invoke("GET", "/math/sum", mapOf(entry("values", listOf(1, 2, 3))), (result, exception) -> {
// result is 6
});
下面的示例演示如何从Swift代码访问数学服务。 由默认URL会话支持的WSWebServiceProxy实例和支持十个并发操作的委托队列用于执行远程方法。 结果处理程序使用闭包实现:
// Configure session
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.requestCachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
let delegateQueue = NSOperationQueue() delegateQueue.maxConcurrentOperationCount = 10
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: delegateQueue)
// Initialize service proxy and invoke methods
let serverURL = NSURL(string: "https://localhost:8443")
let serviceProxy = WSWebServiceProxy(session: session, serverURL: serverURL!)
// Get sum of "a" and "b"
serviceProxy.invoke("GET", path: "/math/sum", arguments: ["a": 2, "b": 4]) {(result, error) in
// result is 6
}
// Get sum of all values
serviceProxy.invoke("GET", path: "/math/sum", arguments: ["values": [1, 2, 3]]) {(result, error) in
// result is 6
}
最后,此示例演示如何从JavaScript客户端访问服务。 WebServiceProxy的实例用于调用方法,而闭包用于实现结果处理程序:
// Create service proxy
var serviceProxy = new WebServiceProxy();
// Get sum of "a" and "b"
serviceProxy.invoke("GET", "/math/sum", {a:4, b:2}, function(result, error) {
// result is 6
});
// Get sum of all values
serviceProxy.invoke("GET", "/math/sum", {values:[1, 2, 3, 4]}, function(result, error) {
// result is 6
});
本文介绍了HTTP-RPC框架,并提供了一些示例,说明如何使用它轻松地以Java,Objective-C / Swift和JavaScript创建和使用RESTful Web服务。 该项目正在GitHub上积极开发,将来可能会增加对其他平台的支持。 鼓励反馈,欢迎贡献。
有关更多信息,请参见项目自述文件或通过gk_brown@verizon.net与作者联系。
java 轻量rpc框架