Jersey2.x对REST请求处理流程的分析
一个REST请求,始于一个RESTful Web Service资源的地址,终于一个可接受的对资源的表述(比如JSON)。
因此,流程分析的关键点有2个:
- 将请求地址对应到资源类的相应方法上,并触发该方法。
- 将返回值转换成请求所需要的表述,并返回给客户端。
我们使用Eclipse的断点调试服务器端(代码对应本例https://github.com/feuyeux/jax-rs2-guide/),使用cURL脚本作为客户端。
1 cURL测试
curl -H "Accept:application/json" http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/books/book?id=1
该脚本发送了一个REST GET请求,地址是:
http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/books/book?id=1
对应的服务器端方法是:
com.example.resource.BookResource.getBookByQuery(Integer)
要求返回的类型是JSON:
Accept:application/json
2 Jersey2.x流程
2.1 请求地址到REST方法
2.1.1 ServletContainer.service
ServletContainer.service(HttpServletRequest, HttpServletResponse) line: 248
- baseUri http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/
- requestUri http://localhost:8080/simple-service-webapp-spring-jpa-jquery/webapi/books/book?id=1
ServletContainer是HttpServlet的子类,位于Jersey容器包(.m2\repository\org\glassfish\jersey\containers\jersey-container-servlet-core\2.2\jersey-container-servlet-core-2.2.jar)。我们知道,Servlet的service方法是请求的入口,作为子类,HTTP任何方法的请求都要先经过该类的service方法。
断点堆栈中,重要的两个变量是请求地址信息的baseUri和requestUri。
Jersey包依赖关系图
ServletContainer.service(URI, URI, HttpServletRequest, HttpServletResponse) line: 372
- requestContext.header {user-agent=[curl/7.26.0], host=[localhost:8080], accept=[application/json]}
在容器层级,请求上下文变量除了包括请求地址信息外,还包括请求头信息。这里我们关注accept信息。
2.1.2 对应方法
ApplicationHandler.handle(ContainerRequest) line: 982
ServerRuntime.process(ContainerRequest) line: 211 final Runnable task
new Runnable() {
public void run() {
final ContainerRequest data = Stages.process(request, requestProcessingRoot, endpointRef);
final Endpoint endpoint = endpointRef.get();
-
endpoint ResourceMethodInvoker (id=2553)
public com.example.domain.Book com.example.resource.BookResource.getBookByQuery(java.lang.Integer)
从上面的代码片段,可以看到请求被对应到了一个Endpoint对象,该对象是一个资源方法Invoker,从断点堆栈中可以看到,其内容就是我们期待的那个方法,此后的invoke将调用这个对应REST请求的处理方法。
2.1.3 调用方法
ResourceMethodInvoker.invoke(ContainerRequest, Object) line: 353
dispatcher.dispatch(resource, requestContext);JavaResourceMethodDispatcherProvider$TypeOutInvoker(AbstractJavaResourceMethodDispatcher).invoke(Object, Object...) line: 158
invokeMethodAction.run();ResourceMethodInvocationHandlerFactory$1.invoke(Object, Method, Object[]) line: 81
从堆栈中可以轻松定位这个方法:**BookResource.getBookByQuery(Integer) line: 71**
到此,请求地址到方法调用的流程就结束了。在处理业务逻辑方法BookResource.getBookByQuery后,Jersey将开始处理响应信息。
2.2 REST方法到表述
JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(Object, Request) line: 198
- o Book (id=2686)
- Response response = Response.ok().entity(o).build();
REST请求的返回信息应包括表述信息和HTTP响应代码(通常一个成功的请求应该得到200 OK)。这就是上面代码所做的事情。在设置好response的entity后,需要将该entity对象转化成请求所接受的表述,流程如下。
2.2.1 写向response
ServerRuntime$Responder.process(ContainerResponse) line: 351ServerRuntime$Responder.writeResponse(ContainerResponse) line: 486
- entity Book (id=7076)
- executor WriterInterceptorExecutor (id=7125)
从上面的堆栈中可以看到,这个阶段重点的两个对象是返回对象和写处理对象,就是使用后者将前者恰当地写入response中。
2.2.2 JSON表述
REST的表述不局限于JSON,这里我们以JSON作为常用的表述类型为例,来讨论Jersey的表述处理。 Jersey对JSON的支持有4种方式,在第3章会有详细的讲解,本例使用的是EclipseLink项目的MOXy。其底层依赖是JPA的实现之一(另一个著名的JPA实现是JBoss项目下大名鼎鼎的Hibernate)。
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
MOXy依赖关系图
在下面的堆栈中,可以看到JAXB的身影,因为MOXy同样实现了JAXB,其内部实现是以XML的Marshal方式处理对象(OXM:Object-XML-Mapping),然后转化为JSON数据(XML-2-JSON)。
JsonWithPaddingInterceptor.aroundWriteTo(WriterInterceptorContext) line: 91
WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorContext) line: 188
ConfigurableMoxyJsonProvider(MOXyJsonProvider).**writeTo**(Object, Class<?>, Type, Annotation[], MediaType, MultivaluedMap<string,object style="margin: 0px; padding: 0px;">, OutputStream) line: 782
- object Book (id=7076)
- type Class (com.example.domain.Book) (id=568)
- genericType Class (com.example.domain.Book) (id=568)
- annotations Annotation3
- mediaType AcceptableMediaType (id=7120)
- httpHeaders StringKeyIgnoreCaseMultivaluedMap (id=7121)
- entityStream CommittingOutputStream (id=7124)
开始从对象转化为JSON:
JAXBMarshaller.marshal(Object, OutputStream) line: 395
- object Book (id=7076)
- outputStream CommittingOutputStream (id=7124)
XMLMarshaller(XMLMarshaller<abstract_session,context,descriptor,media_type,namespace_prefix_mapper,object_builder style="margin: 0px; padding: 0px;">).marshal(Object, OutputStream, ABSTRACT_SESSION, DESCRIPTOR) line: 852
XMLMarshaller(XMLMarshaller<abstract_session,context,descriptor,media_type,namespace_prefix_mapper,object_builder style="margin: 0px; padding: 0px;">).marshal(Object, Writer, ABSTRACT_SESSION, DESCRIPTOR) line: 1031
- object Book (id=7305)
-
writer OutputStreamWriter (id=7310)
-> BufferedWriter (id=7333)
XMLMarshaller(XMLMarshaller<abstract_session,context,descriptor,media_type,namespace_prefix_mapper,object_builder style="margin: 0px; padding: 0px;">).marshal(Object, MarshalRecord, ABSTRACT_SESSION, DESCRIPTOR, boolean) line: 583
- object Book (id=7305)
- marshalRecord JSONWriterRecord (id=7342)
- session DatabaseSessionImpl (id=7351)
- descriptor XMLDescriptor (id=7353)
- isXMLRoot false
几经辗转,写入对象变成了MarshalRecord。
JSONWriterRecord.startDocument(String, String) line: 171
TreeObjectBuilder.marshalAttributes(MarshalRecord, Object, CoreAbstractSession) line: 122
XPathObjectBuilder.marshalAttributes(MarshalRecord, Object, CoreAbstractSession) line: 552
这里是可以想见的流程...
ServerRuntime$Responder.processResponse(ContainerResponse) line: 362
最后是将response返回并释放。流程到此结束。
本文收录在我的新书《Java Restful Web Service使用指南》中:
请留意写作动态
https://code.csdn.net/delva/jax-rs2-guide/
相关推荐
总结起来,"phonecat-parent:使用 JAX RS - Jersey 2.x 和 Spring 4.x 的 PhoneCat App API"项目是一个全面展示Java RESTful服务开发的实例,涵盖了从服务设计、实现到测试的整个流程,涉及的技术包括JAX-RS、Jersey...
### Java Jersey REST Web Services开发详解 #### 一、名词解释 **1. REST (Representational State Transfer)** - **定义**: REST是一种软件架构风格,主要用于设计网络应用的API。其核心理念是“资源”...
6. **MVC 风格的支持**:Jersey 2.x 引入了 MVC 风格的编程模型,允许开发者使用类似于 Spring MVC 的方式编写 REST 服务。 7. **模块化**:Jersey 可以与其他 JAX-RS 实现互换,因为它遵循标准。此外,可以通过...
【标题】"upickle_2.10-0.1.1...通过这些知识点,开发者不仅可以学习如何在Scala中处理JSON数据,还可以掌握如何在DropWizard框架下构建微服务,以及如何参与到开源项目中,提升个人的编程技能和对软件开发流程的理解。
chromedriver-win64-136.0.7059.0.zip
python学习一些项目和资源
python学习资源
python学习资源
python学习教程
python学习教程
【毕业设计】java-springboot+vue会议管理系统实现源码(完整前后端+mysql+说明文档+LunW).zip
内有各个系统的版本全了
分数阶模型辨识,分数阶模型辨识
大数据基于python的电影天堂数据可视化(源码+配套文档) 系统功能: 登录 、首页 、电影数据管理 、我的信息 关键技术:Python、Django、Mysql、Hadoop、Scrapy、Vue、B/S 技术支持:已测试可正常运行,调试问题可联系客服有偿解决。 更多项目:3000+优质源码,支持【定制】、修改、部署、讲解和文档。
【毕业设计】java-springboot+vue疾病防控综合系统的设计与实现源码(完整前后端+mysql+说明文档+LunW).zip
【毕业设计】java-springboot-vue家具销售电商平台实现源码(完整前后端+mysql+说明文档+LunW).zip
134dfffffffffffffffffffffffffffffff
代码说明: 设置结束时间:通过new Date().getTime()获取当前时间戳,并加上10分钟的毫秒数(10 * 60 * 1000),得到倒计时的结束时间。 更新倒计时:updateCountdown函数计算当前时间与结束时间的差值,并将其转换为分钟和秒数。 显示倒计时:通过console.log输出剩余时间,格式为“剩余时间:X分Y秒”。 停止倒计时:当剩余时间小于或等于0时,清除定时器并输出“时间到!”。 定时器:使用setInterval每秒调用一次updateCountdown函数,实现倒计时的动态更新。 扩展说明: 应用场景:倒计时功能常用于限时抢购、考试计时、活动倒计时等场景。 优化建议:可以将倒计时显示在网页的某个元素中,而不是控制台。例如,使用document.getElementById获取DOM元素并更新其内容。 兼容性:该代码在现代浏览器中均可运行,如果需要兼容旧版浏览器,可以使用var代替const和let。 扩展功能:可以添加声音提示、动画效果等,提升用户体验。
该项目是一个大学生校园兼职平台。该平台使用Java语言开发后台业务逻辑,运用了SpringMVC+Spring+MyBatis框架进行搭建,前台使用jQuery、layUI框架,数据库服务器采用MySQL5.6+对数据进行持久化。其主要功能有:兼职招聘、论坛交流、在线聊天、个人中心、信箱留言、登录注册等功能。
图解AUTOSAR-CP-CommunicationStackTypes逻辑图打包