使用Maven构建微服务项目:从依赖管理到RESTEasy jar数据包下载指南
这些数据库的大小约为830kb。当然,这些直接的依赖性使用Maven一起建立项目也将带来一些及时的依赖性。

接下来,我将使用“ Maven方法”来构建此项目,例如,在SRC/Main/Java中,使用Maven Build命令等。如果您不想使用Maven,也可以下载Resteasy Jar直接从下载页[16]的数据包。下载时,请忽略Resteasy网站上的提示弹出:JBOSS只是试图指导您采用更多的“企业”方法。您只需要单击“继续下载”即可进行后续操作。
项目设计
以下微服务可用于以非常简单的方式演示一些基本概念。如下图所示,它包括5个级别。
在这里,水果应用是微服务的切入点。 Fruitservice提供主要路径(/水果),并且还充当路由器函数。苹果和水果都是模型。水果包含一些抽象功能,苹果将专门扩展其功能。
与您的想法一致,FruitComparator可以提供比较功能。不熟悉Java比较器的读者可以了解本文中对象的等效性和比较,在这里我将使用字符来替换它们。虽然水果孔板不是模型,但我更喜欢将比较器与要比较的对象类型相似。
模型
让我们从水果开始
package com.lyndseypadget.resteasy.model;
import javax.xml.bind.annotation.XmlElement;
public abstract class Fruit {
private String id;
private String variety;
@XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@XmlElement
public String getVariety() {
return variety;
}
public void setVariety(String variety) {
this.variety = variety;
}
}
然后,苹果在此级别上扩展了它:
package com.lyndseypadget.resteasy.model;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "apple")
public class Apple extends Fruit {
private String color;
@XmlElement
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
以上不是特别惊人的代码,您可能认为这是不值得炫耀的。这只是Java继承的一个简单示例。但关键是这两个注释@xmlelement和@xmlrootelement,它定义了XML Apple结构的外观:
<apple>
<id>1id>
<variety>Golden deliciousvariety>
<color>yellowcolor>
apple>
因为没有明显的构造函数的约定:Java使用隐式的无参数默认构造函数,因此正在发生更微妙的事情。该无参数构造函数对于对JAXB进行神奇效果非常必要(本文解释了这一点,并且在必要时,如何使其与XMLADAPTER一起使用)。
现在我们有了一个对象:定义的苹果。它具有三个属性:ID,多样性和颜色。
服务
Fruitservice用作与微服务相互作用的主要路径(/水果)。在这种情况下,我使用@Path注释直接在该层次结构中定义了第一个路径 /水果 /苹果。随着RESTFUL微服务的扩展,您可能需要在自己的层次结构中定义多个最终路径(例如 /苹果, /香蕉, /橙色)。
package com.lyndseypadget.resteasy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.lyndseypadget.resteasy.model.Apple;
import com.lyndseypadget.resteasy.model.FruitComparator;
@Path("/fruits")
public class FruitService {
private static Map apples = new TreeMap();
private static Comparator comparator = new FruitComparator();
@GET
@Path("/apples")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public List getApples() {
List retVal = new ArrayList(apples.values());
Collections.sort(retVal, comparator);
return retVal;
}
}
此Apple地图可帮助我们根据ID跟踪Apple的数据,从而模拟某些类型的数据持久性层。使用GetApples方法(常见的HTTP请求方法)返回地图跟踪的相关Apple数据。 GET /Apples路由由@Get和@path注释定义,可以在数据传输格式XML或JSON中生成内容。
此方法需要返回列表<Apple>对象,然后使用此比较器按多种属性对列表进行排序。
水果肉饼看起来像这样:
package com.lyndseypadget.resteasy.model;
import java.util.Comparator;
public class FruitComparator implements Comparator {
public int compare(F f1, F f2) {
return f1.getVariety().compareTo(f2.getVariety());
}
}
请注意,如果我们想对Apple的特定属性(例如Color)进行分类,则必须创建一个新的比较器来替换它并提供一个名称,例如AppleComparator。
应用程序
在ResteAsy3.1.x中,您需要定义扩展应用程序的级别。 Resteasy示例文档表明这是单身注册表,如下所示:
package com.lyndseypadget.resteasy;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
public class FruitApplication extends Application
{
HashSet singletons = new HashSet();
public FruitApplication()
{
singletons.add(new FruitService());
}
@Override
public Set getClasses()
{
HashSet set = new HashSet();
return set;
}
@Override
public Set getSingletons()
{
return singletons;
}
}
如果我们只想说明此示例,我们就不需要在此级别上做很多工作,但是我们需要在Web.xml文件中将其连接,这将在后面的“ Web Service Connection”中介绍。
对象构建集合
GET /Apples调用将返回以下数据:
<collection>
<apple>
<id>1id>
<variety>Golden deliciousvariety>
<color>yellowcolor>
apple>
collection>
[
{
"apple": {
"id": 1,
"variety": "Golden delicious",
"color": "yellow"
}
}
]
但是,我们可以更改数据以稍有不同:
<apples>
<apple>
<id>1id>
<variety>Golden deliciousvariety>
<color>yellowcolor>
apple>
apples>
{
"apples": {
"apple": {
"id": 1,
"variety": "Golden delicious",
"color": "yellow"
}
}
}
第二个选项在XML中看起来更好,但对JSON的效果较小。如果您喜欢此结构,则可以使用自己的类型来包装列表<Apple>并修改FruitService.getApples方法以返回此类型:
package com.lyndseypadget.resteasy.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "apples")
public class Apples {
private static Comparator comparator = new FruitComparator();
@XmlElement(name = "apple", type = Apple.class)
private List apples;
public List getApples() {
Collections.sort(apples, comparator);
return apples;
}
public void setApples(Collection apples) {
this.apples = new ArrayList(apples);
}
}
这些评论有效地“备注”了根元素,即收集/列表。通过阅读Javax.xml.bind.annotation的Javadoc文档,您可以尝试使用不同的XML模式映射进行注释。
当然,如果您真的无法通过一般方法签名,则可以编码和编写不同的方法 - 一种用于XML,另一个用于JSON。
一些Web服务连接
从将服务部署到tomcat开始,我使用位于src/main/webapp/web inf/web.xml中的Web应用程序部署描述符文件。它包含以下内容:
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>resteasydisplay-name>
<context-param>
<param-name>javax.ws.rs.core.Applicationparam-name>
<param-value>com.lyndseypadget.resteasy.FruitApplicationparam-value>
context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefixparam-name>
<param-value>/v1param-value>
context-param>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
listener-class>
listener>
<servlet>
<servlet-name>Resteasyservlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcherservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Resteasyservlet-name>
<url-pattern>/v1/*url-pattern>
servlet-mapping>
web-app>
是的,“ servlet-name”是指servlet的名称(即服务)是恢复的。 Servlet映射URL-Pattern(/V1/*)要求Tomcat服务器将包含此模式的传入请求传输到Resteasy Service。有关如何创建此文件以及可用的不同选项的更多信息,请参见Tomcat的应用程序部署文档[17]。
建筑和部署
从项目的根目录中,您可以运行以下内容来构建战争(Web应用程序资源)文件:
mvn clean install
这将在目标文件夹中创建一个新文件夹,其中包含战争文件。虽然可以使用Maven或其他工具部署文件,但我只使用一个简单的复制命令。应当指出的是,每次您将战争重新启动到Tomcat服务器时,您都应首先暂停服务器并删除服务应用程序文件夹(在这种情况下,此文件夹:<TomCatDirectory>/webapps/resteasy)和旧一个战争文件(<tomcatdirectory>/webapps/resteasy.war)。
[sudo] cp target/resteasy.war /webapps/resteasy.war
如果此时的Tomcat服务器正在运行,则将立即部署Web服务。如果没有,则在服务器下次启动时,该服务将自动部署。然后,您可以通过以下地址访问Web服务:<tomcathost>:<tomcatport>/resteasy/v1/v1/fruits/apples。在我的示例中,此地址为:8080/reteasy/v1/fruits/苹果。
通过“内容谈判”
((内容谈判)”测试服务
内容谈判是一种提供不同资源(URI)表达的机制。最基本的是它的含义:
有关内容谈判和标题的更多信息,请参见RFC 2616的第12和14章[18]。在这种情况下,您真正需要知道的是:
如果您尝试将http调用到有效的端点,但是无法协商内容,则意味着没有@produces与接受数据匹配,或者@consumes no @consumes与内容型数据匹配,HTTP状态代码415:无支持的数据将返回转移格式。
获取返回通用数据传输格式的呼叫实际上可以直接进入浏览器。对于诸如get /apples之类的电话,默认情况下将获得XML:
但是,使用Postman [19]之类的工具可能会更有帮助,因为它明确指定了接受标头为应用程序/XML:
两种方法都返回一些有效但不是很有意义的XML,即一个空的Apple列表。但是这里有一些很酷的东西。将Accept标头更改为应用程序/JSON,Great,VOILA! JSON*有效*:
不只是“阅读”
您可能会发现,仅读取Restful Web服务的许多示例,其中一些示例将没有其他提示,例如如何创建,更新和删除这些操作。尽管我们已经有一个用于Web服务的框架,但这是一个无法更改的空列表,这没什么意义。因此,我们应该使用其他一些方法将Apple添加到此列表中或将其从列表中删除。
package com.lyndseypadget.resteasy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.lyndseypadget.resteasy.model.Apple;
import com.lyndseypadget.resteasy.model.FruitComparator;
@Path("/fruits")
public class FruitService {
private static Comparator comparator = new FruitComparator();
private static Map apples = new TreeMap();
private static int appleCount = 0;
@GET
@Path("/apples")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public List getApples() {
List retVal = new ArrayList(apples.values());
Collections.sort(retVal, comparator);
return retVal;
}
@GET
@Path("/apples/{id}")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response getApple(@PathParam("id") String id) {
Apple found = apples.get(id);
if(found == null) {
return Response.status(404).build();
}
return Response.ok(found).build();
}
@DELETE
@Path("/apples/{id}")
public Response deleteApple(@PathParam("id") String id) {
apples.remove(id);
return Response.status(200).build();
}
@POST
@Path("/apples")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response createApple(Apple apple) {
String newId = Integer.toString(++appleCount);
apple.setId(newId);
apples.put(newId, apple);
return Response.status(201).header("Location", newId).build();
}
}
这样,已经添加了一些新功能:
这些方法完善了许多功能,以确保服务能够按预期工作。对Apple的更新(使用@put和/或@Patch),以及终点,逻辑和管理持久性的更多功能操作,读者进行练习。
当我们再次构建和部署时,我们会发现(如果使用Maven或Tomcat进行设置,请参见上面的“构建和部署”),我们现在可以在服务中创建,检索和删除Apple。即使您不在服务器上进行任何重新配置,也可以在XML和JSON之间进行选择性调用。
用“应用程序/JSON”内容类型和JSON主体创建一个Apple,如下图所示:
这是另一个示例:使用“应用程序/XML”内容类型和XML主体创建一个Apple。
在XML中搜索所有Apple数据:
从JSON中的ID检索Apple 2的数据:
按ID删除Apple 1的数据:
在JSON中搜索Apple的所有数据:
概括
在这里,我们探索了Resteasy Architecture如何无缝支持Java Web服务中的XML和JSON数据传输格式。我还解释了REST,媒体类型(数据传输格式),Web服务和微服务之间的技术差异,因为这些术语中有许多令人困惑的灰色区域。
我在这里列出的示例可能有点不情愿。在我的生活中,我从不真正需要与水果相关的数据,而且我从未在食品行业工作。我以水果为例的原因是因为我认为这种“规模”可以帮助每个人都了解微服务的概念。您还可以想象其他微服务(例如蔬菜,罐头食品或海鲜)如何形成食品分配系统。在现实世界中,杂货店的食品分配系统实际上非常复杂,它必须考虑到包括销售,优惠券,过期日期,营养信息等问题。
当然,您可以选择其他方法来细分系统,但是当您需要快速,高效,轻巧的工具来支持多种数据格式时,Resteasy确实是一个不错的选择。
原始链接:
参考地址:
[1],[2],[6]
[3]
[4]
[5]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
[16]
[17]
[18]
[19]
关于作者:Lyndsey是密苏里州堪萨斯城的著名软件工程师和演讲者。尽管她过去一直活跃于Java领域,但她目前一直专注于Node JS+ES6的工作。她已经在一些大型企业和初创公司工作了很长时间,并且在软件和网络开发方面积累了十多年的丰富实用经验,并且擅长设计可维护和直观的恢复API网络接口。她的演讲和博客都是关于微服务,git概念和工作流以及各种软件技能的。 Lyndsey积极参与相关的地方组织,并鼓励各个年龄段的妇女积极探索数学和科学领域的职业问题。有关她的更多信息,请访问lyndseypadget.com。