Java Web基础
2022-05-12 15:31:19 1 举报
AI智能生成
Java Web基础,你知道多少?
作者其他创作
大纲/内容
软件系统体系结构
常见软件系统体系结构
B/S
C/S
Web资源
Web资源介绍
静态资源和动态资源区别
访问Web资源
Web服务器介绍
JSP
java server pages
jsp脚本
<% Java语句 %>
JSP 脚本可以包含任意数量的 Java 语句,变量、方法和表达式。
JSP 脚本会把包含的内容插入到 Servlet 的 service() 方法中。 脚本不可以写方法 方法不能套方法
JSP 脚本会把包含的内容插入到 Servlet 的 service() 方法中。 脚本不可以写方法 方法不能套方法
不能声明方法 只能生命变量(局部变量)
脚本写入到servlet中的service方法里
jsp声明
<%! 声明语句 %>
JSP 声明语句用于声明一个或多个变量、方法,以供后面的 Java 代码使用。
您必须先对变量和方法进行声明,才能使用它们
您必须先对变量和方法进行声明,才能使用它们
可能声明方法 变量(成员方法 成员变量)
声明内容添加到servlet类中 方法常量
jsp表达式
<%= 表达式 %>
JSP 表达式可以把变量或者表达式输出到网页上,不需要 out.print() 就能输出数据。
通常用于打印变量和方法的值。
通常用于打印变量和方法的值。
生命周期
编译
解析
翻译jsp--Servlet
编译Servlet
初始化
jspinit()
执行
jspService()
request
get
post
response
销毁
jspDestroy()
JSP指令
<%@ page ... %>
JSP page 指令用来定义当前页面的相关属性。
page 指令可以在 JSP 页面的任意位置编写,
通常放在 JSP 页面的顶部。
page 指令可以在 JSP 页面的任意位置编写,
通常放在 JSP 页面的顶部。
<%@ include ... %>
用于在 JSP 页面引入其它内容,
可以是 JSP 文件、html 文件和文本文件等,
相当于把文件的内容复制到 JSP 页面。引入的文件和 JSP 页面同时编译运行。
可以是 JSP 文件、html 文件和文本文件等,
相当于把文件的内容复制到 JSP 页面。引入的文件和 JSP 页面同时编译运行。
<%@ taglib ... %>
可以使用 taglib 指令声明并引入标签库。
Java API 允许在页面中自定义标签,
标签库就是自定义标签的集合。
Java API 允许在页面中自定义标签,
标签库就是自定义标签的集合。
JSP动作
include动作
<jsp:include page="head.jsp"></jsp:include>
forward动作
<jsp:forward page="index.jsp" />
转发 带参数转发
web地址不变化
param动作
<jsp: param name="param_name" value="param_value" />
useBean动作
<jsp:useBean id = "name" class = "package.class" scope= "page | request | session | application" />
setProperty和getProperty动作
<jsp:setProperty name = "beanName" property = "attributeName" value = "attributeValue"/>
<jsp:getProperty name = "beanName" property = "attributeName">
JSP内置对象
JSP 内置对象只能在脚本和表达式中使用,在声明中不能使用。
JSP 内置对象又称为隐式对象,它们由容器实现和管理。在 JSP 页面中,这些内置对象不需要预先声明,也不需要进行实例化,我们可以直接在脚本和表达式中使用。
由 JSP 规范提供,不用编写者实例化;
通过 Web 容器实现和管理;
所有 JSP 页面均可使用;
只有在脚本元素的表达式或代码段中才能使用。
通过 Web 容器实现和管理;
所有 JSP 页面均可使用;
只有在脚本元素的表达式或代码段中才能使用。
九大内置对象
四大域对象
pageContext
request
session
application
四大域方法
setAttribute
getAttribute
removeAttribute
response
response.sendRedirect("success.jsp");
重定向 web地址发生变化
request参数不传递
out
config
page
exception
cookie
EL表达式
获取参数
${param.a}
等同于request.getParameter("a");
等同于request.getParameter("a");
子主题
JSTL
javabean
规范
提供一个默认的无参构造函数。 ?
需要被序列化并且实现了 Serializable 接口。 ?
,JVM 将类实例转化为字节序列,当类实例被发送到另一台计算机后,
会被重新组装,不用担心因操作系统不同而有所改变,序列化机制可
以弥补网络传输中不同操作系统的差异问题。
会被重新组装,不用担心因操作系统不同而有所改变,序列化机制可
以弥补网络传输中不同操作系统的差异问题。
可能有一系列可读写属性。
可能有一系列的 getter 或 setter 方法。
属性
getPropertyName()
setPropertyName()
与import导入区别
导入并未实例化 需手动创建对象
javabean实例化 对象名为id
实例化javabean<jsp:useBean id="student" class="web.webtest.jsp.jsp07.StudentBean" scope="page">
设置属性:<jsp:setProperty name="student" property="add" value="黑龙江"/>
读取属性:<jsp:getProperty name="student" property="add"/>
读取属性:<jsp:getProperty name="student" property="add"/>
JSP的生命周期
JSP乱码的问题
ME打开Chrome乱码的解决
常见问题
什么是JSP页面?
JSP页面是一种包含了静态数据和JSP元素两种类型的文本的文本文档。静态数据可以用任何基于文本的格式来表示,比如:HTML或者XML。JSP是一种混合了静态内容和动态产生的内容的技术。这里看下JSP的例子。
.JSP请求是如何被处理的?
浏览器首先要请求一个以.jsp扩展名结尾的页面,发起JSP请求,然后,Web服务器读取这个请求,使用JSP编译器把JSP页面转化成一个Servlet类。需要注意的是,只有当第一次请求页面或者是JSP文件发生改变的时候JSP文件才会被编译,然后服务器调用servlet类,处理浏览器的请求。一旦请求执行结束,servlet会把响应发送给客户端。这里看下如何在JSP中获取请求参数。
JSP有什么优点?
JSP页面是被动态编译成Servlet的,因此,开发者可以很容易的更新展现代码。
JSP页面可以被预编译。
JSP页面可以很容易的和静态模板结合,包括:HTML或者XML,也可以很容易的和产生动态内容的代码结合起来。
开发者可以提供让页面设计者以类XML格式来访问的自定义的JSP标签库。
开发者可以在组件层做逻辑上的改变,而不需要编辑单独使用了应用层逻辑的页面。
JSP页面可以被预编译。
JSP页面可以很容易的和静态模板结合,包括:HTML或者XML,也可以很容易的和产生动态内容的代码结合起来。
开发者可以提供让页面设计者以类XML格式来访问的自定义的JSP标签库。
开发者可以在组件层做逻辑上的改变,而不需要编辑单独使用了应用层逻辑的页面。
什么是JSP指令(Directive)?JSP中有哪些不同类型的指令?
Directive是当JSP页面被编译成Servlet的时候,JSP引擎要处理的指令。Directive用来设置页面级别的指令,从外部文件插入数据,指定自定义的标签库。Directive是定义在 <%@ 和 %>之间的。下面列出了不同类型的Directive:
包含指令(Include directive):用来包含文件和合并文件内容到当前的页面。
页面指令(Page directive):用来定义JSP页面中特定的属性,比如错误页面和缓冲区。
Taglib指令: 用来声明页面中使用的自定义的标签库。
包含指令(Include directive):用来包含文件和合并文件内容到当前的页面。
页面指令(Page directive):用来定义JSP页面中特定的属性,比如错误页面和缓冲区。
Taglib指令: 用来声明页面中使用的自定义的标签库。
什么是JSP动作(JSP action)?
JSP动作以XML语法的结构来控制Servlet引擎的行为。当JSP页面被请求的时候,JSP动作会被执行。它们可以被动态的插入到文件中,重用JavaBean组件,转发用户到其他的页面,或者是给Java插件产生HTML代码。下面列出了可用的动作:
jsp:include-当JSP页面被请求的时候包含一个文件。
jsp:useBean-找出或者是初始化Javabean。
jsp:setProperty-设置JavaBean的属性。
jsp:getProperty-获取JavaBean的属性。
jsp:forward-把请求转发到新的页面。
jsp:plugin-产生特定浏览器的代码。
jsp:include-当JSP页面被请求的时候包含一个文件。
jsp:useBean-找出或者是初始化Javabean。
jsp:setProperty-设置JavaBean的属性。
jsp:getProperty-获取JavaBean的属性。
jsp:forward-把请求转发到新的页面。
jsp:plugin-产生特定浏览器的代码。
什么是Scriptlets?
JSP技术中,scriptlet是嵌入在JSP页面中的一段Java代码。scriptlet是位于标签内部的所有的东西,在标签与标签之间,用户可以添加任意有效的scriplet。
声明(Decalaration)在哪里?
声明跟Java中的变量声明很相似,它用来声明随后要被表达式或者scriptlet使用的变量。添加的声明必须要用开始和结束标签包起来。
什么是表达式(Expression)?
【列表很长,可以分上、中、下发布】
JSP表达式是Web服务器把脚本语言表达式的值转化成一个String对象,插入到返回给客户端的数据流中。表达式是在<%=和%>这两个标签之间定义的。
JSP表达式是Web服务器把脚本语言表达式的值转化成一个String对象,插入到返回给客户端的数据流中。表达式是在<%=和%>这两个标签之间定义的。
隐含对象是什么意思?有哪些隐含对象?
JSP隐含对象是页面中的一些Java对象,JSP容器让这些Java对象可以为开发者所使用。开发者不用明确的声明就可以直接使用他们。JSP隐含对象也叫做预定义变量。下面列出了JSP页面中的隐含对象:
application
page
request
response
session
exception
out
config
pageContext
application
page
request
response
session
exception
out
config
pageContext
Servlet
什么是Servlet?
Servlet是用来处理客户端请求并产生动态网页内容的Java类。Servlet主要是用来处理或者是存储HTML表单提交的数据,产生动态内容,在无状态的HTTP协议下管理状态信息。
Servlet的如何实现?
sendRedirect()和forward()方法有什么区别?
sendRedirect()方法会创建一个新的请求,而forward()方法只是把请求转发到一个新的目标上。重定向(redirect)以后,之前请求作用域范围以内的对象就失效了,因为会产生一个新的请求,而转发(forwarding)以后,之前请求作用域范围以内的对象还是能访问的。一般认为sendRedirect()比forward()要慢。
浏览器和Servlet通信使用的是什么协议?
浏览器和Servlet通信使用的是HTTP协议。
如何知道是哪一个客户端的机器正在请求你的Servlet?
ServletRequest类可以找出客户端机器的IP地址或者是主机名。getRemoteAddr()方法获取客户端主机的IP地址,getRemoteHost()可以获取主机名。
什么是Servlet链(Servlet Chaining)?
Servlet链是把一个Servlet的输出发送给另一个Servlet的方法。第二个Servlet的输出可以发送给第三个Servlet,依次类推。链条上最后一个Servlet负责把响应发送给客户端。
什么是服务端包含(Server Side Include)?
服务端包含(SSI)是一种简单的解释型服务端脚本语言,大多数时候仅用在Web上,用servlet标签嵌入进来。SSI最常用的场景把一个或多个文件包含到Web服务器的一个Web页面中。当浏览器访问Web页面的时候,Web服务器会用对应的servlet产生的文本来替换Web页面中的servlet标签。
什么是Web应用程序?
Web应用程序是对Web或者是应用服务器的动态扩展。有两种类型的Web应用:面向表现的和面向服务的。面向表现的Web应用程序会产生包含了很多种标记语言和动态内容的交互的web页面作为对请求的响应。而面向服务的Web应用实现了Web服务的端点(endpoint)。一般来说,一个Web应用可以看成是一组安装在服务器URL名称空间的特定子集下面的Servlet的集合。
doGet()方法和doPost()方法有什么区别?
doGet:GET方法会把名值对追加在请求的URL后面。因为URL对字符数目有限制,进而限制了用在客户端请求的参数值的数目。并且请求中的参数值是可见的,因此,敏感信息不能用这种方式传递。
doPOST:POST方法通过把请求参数值放在请求体中来克服GET方法的限制,因此,可以发送的参数的数目是没有限制的。最后,通过POST请求传递的敏感信息对外部客户端是不可见的。
doPOST:POST方法通过把请求参数值放在请求体中来克服GET方法的限制,因此,可以发送的参数的数目是没有限制的。最后,通过POST请求传递的敏感信息对外部客户端是不可见的。
解释下Servlet的生命周期。
对每一个客户端的请求,Servlet引擎载入Servlet,调用它的init()方法,完成Servlet的初始化。然后,Servlet对象通过为每一个请求单独调用service()方法来处理所有随后来自客户端的请求,最后,调用Servlet(译者注:这里应该是Servlet而不是server)的destroy()方法把Servlet删除掉。
GenericServlet和HttpServlet有什么区别?
GenericServlet是一个通用的协议无关的Servlet,它实现了Servlet和ServletConfig接口。继承自GenericServlet的Servlet应该要覆盖service()方法。最后,为了开发一个能用在网页上服务于使用HTTP协议请求的Servlet,你的Servlet必须要继承自HttpServlet。这里有Servlet的例子。
Applet和Servlet有什么区别?
Applet是运行在客户端主机的浏览器上的客户端Java程序。而Servlet是运行在web服务器上的服务端的组件。applet可以使用用户界面类,而Servlet没有用户界面,相反,Servlet是等待客户端的HTTP请求,然后为请求产生响应。
说一下Servlet的体系结构。
所有的Servlet都必须要实现的核心的接口是javax.servlet.Servlet。每一个Servlet都必须要直接或者是间接实现这个接口,或者是继承javax.servlet.GenericServlet或者javax.servlet.http.HTTPServlet。最后,Servlet使用多线程可以并行的为多个请求服务。
EJB
J2EE的体系结构可以分为四层
J2EE的体系结构可以分为四层
【客户端层】
负责与用户直接交互,J2EE支持多种客户端,所以客户端既可以是WEB浏览器,也可以是专用的Java客户端。
【服务器端组件层】
本层是为了基于WEB的应用服务的,利用J2EE中的JSP与Java Servlet技术,可以响应客户端的请求,并向后访问封装有商业逻辑的组件。
【EJB层】
本层主要封装了商务逻辑,完全企业计算机,提供了事务处理,负载均衡、安全、资源连接等各种基本服务,
程序在编写EJB时可以不关心这些基本的服务,集中注意力于商务逻辑的实现。
【企业信息系统层】
企业信息系统层包括了企业的现有系统(包括数据库系统、文件系统)
J2EE提供了多种技术以访问这些系统,如JDBC访问DBMS。
在J2EE规范中,J2EE平台包括有一整套的服务、应用编程接口和协议,可用于开发一般的多层应用和基于WEB的多层应用,是J2EE的核心和基础。
它还提供了EJB、Java Servlets API、JSP和XML技术的全面支持等。
Struts1
原理
交给Spring代理?
action更重一些?
大致的开发步骤
如何做参数校验?
validatorForm
Struts2
原理
大致的开发步骤
SpringMVC
Spring MVC - https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc
执行流程
图解
图解
图解
1、 用户发送请求至前端控制器DispatcherServlet
Http请求到DispatcherServlet
(1) 客户端请求提交到DispatcherServlet。
2、 HandlerMapping寻找处理器
(2) 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller。
3、 调用处理器Controller
(3) DispatcherServlet将请求提交到Controller。
4、 Controller调用业务逻辑处理后,返回ModelAndView
(4)(5)调用业务处理和返回结果:Controller调用业务逻辑处理后,返回ModelAndView。
5、DispatcherServlet查询ModelAndView
(6)(7)处理视图映射并返回模型:
DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图。
6、 ModelAndView反馈浏览器HTTP
(8) Http响应:视图负责将结果显示到客户端
@Controller
@RestController
HttpServletRequest
HttpServletResponse
@RestController
HttpServletRequest
HttpServletResponse
常用注解
@Compoent
@Controller
@Service
@Repository
在类定义之前添加@Component注解,它会被spring容器识别,并转为bean。
@RequestMapping
@RequestParam
@Autowired
@Resource
图解常用注解
servlet生命周期
1.加载和实例化
2.初始化
3.请求处理
4.服务终止
Spring MVC原理
Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,
这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。
SpringMVC常见问题
SpingMVC如何加过滤器?
SpringMVC如何实现拦截器和过滤器?
SpringMVC的工作原理与大致的过程+1
SpringMVC如何做参数校验?
DTO层面使用使用注解
SpringMVC的常用注解有哪些?
模板引擎
模板引擎是什么?
模板引擎(这里特指用于Web开发的模板引擎)
是为了使用户界面与业务数据(内容)分离而产生的,
它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的HTML代码。
图解模板引擎
图解模板引擎
Thymeleaf
https://www.thymeleaf.org/
Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
常用标签
赋值<span th:text="${book.author.name}">
循环<li th:each="book : ${books}" th:text="${book.title}">En las Orillas del Sar</li>
带URL的链接值<a th:href="@{/order/details(id=${orderId},type=${orderType})}">...</a>
循环<li th:each="book : ${books}" th:text="${book.title}">En las Orillas del Sar</li>
带URL的链接值<a th:href="@{/order/details(id=${orderId},type=${orderType})}">...</a>
If条件判断
<div th:if=" ${userCarSize} lt '3' ">…<div>
gt:great than(大于)>
ge:great equal(大于等于)>=
eq:equal(等于)==
lt:less than(小于)<
le:less equal(小于等于)<=
ne:not equal(不等于)!=
gt:great than(大于)>
ge:great equal(大于等于)>=
eq:equal(等于)==
lt:less than(小于)<
le:less equal(小于等于)<=
ne:not equal(不等于)!=
Switch Case
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
Jersey
是什么?
RESTful 框架
轻量WebService框架
Jersey RESTful 框架是开源的RESTful框架, 实现了JAX-RS (JSR 311 & JSR 339) 规范。
它扩展了JAX-RS 参考实现, 提供了更多的特性和工具, 可以进一步地简化 RESTful service 和 client 开发。
很少一部分人去关注Jersey
尽管相对年轻,它已经是一个产品级的 RESTful service 和 client 框架。
与Struts类似,它同样可以和hibernate,spring框架整合。
由于Struts2+hibernate+spring整合在市场的占有率太高,所以很少一部分人去关注Jersey
所以网上有关于Jersey的介绍很少。但是它确实是一个非常不错的框架。
非常不错的框架
对于请求式服务,对于GET,DELETE请求,你甚至只需要给出一个URI即可完成操作。
优点
优秀的文档和例子
快速
超级容易的路由
平滑的 JUnit 集成
就个人而言, 当开发 RESTful service 时, JAX-RS 实现要好于 MVC 框架。
可以集成到其它库/框架 (Grizzly, Netty). 这也可能是很多产品使用它的原因。
支持异步链接
不喜欢 servlet container? 使用Jersey的时候可以不用它们。
WADL, XML/JSON support
包含在Glassfish中
快速
超级容易的路由
平滑的 JUnit 集成
就个人而言, 当开发 RESTful service 时, JAX-RS 实现要好于 MVC 框架。
可以集成到其它库/框架 (Grizzly, Netty). 这也可能是很多产品使用它的原因。
支持异步链接
不喜欢 servlet container? 使用Jersey的时候可以不用它们。
WADL, XML/JSON support
包含在Glassfish中
缺点
Jersey 2.0+使用了有些复杂的依赖注入实现
可能不是一件坏事。Jersey 1.X 使用较老的 JAX-RS 实现
一大堆第三方库只支持 Jersey 1.X, 在 Jersey 2.X 不可用
可能不是一件坏事。Jersey 1.X 使用较老的 JAX-RS 实现
一大堆第三方库只支持 Jersey 1.X, 在 Jersey 2.X 不可用
举一个简单的使用例子
package org.glassfish.jersey.examples.helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("helloworld")
public class HelloWorldResource {
public static final String CLICHED_MESSAGE = "Hello World!";
@GET
@Produces("text/plain")
public String getHello() {
return CLICHED_MESSAGE;
}
}
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("helloworld")
public class HelloWorldResource {
public static final String CLICHED_MESSAGE = "Hello World!";
@GET
@Produces("text/plain")
public String getHello() {
return CLICHED_MESSAGE;
}
}
同类技术优劣势对比(按评分排序)
摘自[http://www.importnew.com/17138.html]
背景
过去的每一年,涌现出越来越多的Java框架。就像JavaScript,每个人都认为他们知道一个好的框架的功能应该是怎么样的。
连我的老祖母现在也使用 一个我从来没有听说过,而且可能永远不会使用的框架。
玩笑归玩笑,可以做几乎任何事的臃肿的框架市场已经饱和,,但是如何评判呢?
这篇文章旨在提供目前最好的 Java RESTfulful 框架。我只介绍轻量级的产品, 略过了那些臃肿的过度设计的框架。
同时,我只想要他们稳定和成熟,提供简单,轻量级的特点。我只在介绍Play frameworks时打破这条规则,理由稍后给出。
在将来的项目中使用哪个 Java RESTful框架完全取决于你当前的需求。
为了便于你取舍,我将列出最突出的框架功能,希望这能节省一些你的时间。
Jersey
诞生时间: 2012 (Jersey 2.X)
评分: 5/5
评分: 5/5
Jersey 是我的选择, 5颗星。
Dropwizard
诞生时间: 2011
评分: 4.5/5
评分: 4.5/5
是什么?
Dropwizard 提供了稳定成熟的Java库, 并封装成一个简单的轻量级的包。
Dropwizard 介于框架和库之间。它提供了一个开发web应用程序的全部所需。由于内置模块化,一个应用程序可以保持小而精干的特点,减少开发和维护的时间,减少负担。
Dropwizard 使用已有的 Jetty HTTP 库,嵌入到你的项目中,无需外部的server。所有的Dropwizard项目都有一个 main 方法来管理内建的 HTTP server.
Dropwizard 介于框架和库之间。它提供了一个开发web应用程序的全部所需。由于内置模块化,一个应用程序可以保持小而精干的特点,减少开发和维护的时间,减少负担。
Dropwizard 使用已有的 Jetty HTTP 库,嵌入到你的项目中,无需外部的server。所有的Dropwizard项目都有一个 main 方法来管理内建的 HTTP server.
优点
快速的项目构建和启动
模块化
不可思议的快(至少根据内建的 metric 测得的结果)
Jetty for HTTP, Jersey for REST, 以及 Jackson for JSON
也支持其它的库, 比如 Mustache, Logback, JDBI, Hibernate Validator, Guava, …
使用Metrics支持监控
Main方法启动 Jetty server,可以容易地调试和维护
社区力度强
模块化
不可思议的快(至少根据内建的 metric 测得的结果)
Jetty for HTTP, Jersey for REST, 以及 Jackson for JSON
也支持其它的库, 比如 Mustache, Logback, JDBI, Hibernate Validator, Guava, …
使用Metrics支持监控
Main方法启动 Jetty server,可以容易地调试和维护
社区力度强
缺点
Dropwizard文档是主要的知识来源,但谈不上优秀。你可能需要搜索和发掘第三方类库的文档。
因为某些原因错误被当作普通文本, 如果你希望响应结果总是JSON,这可能有问题
确保使用最新的Dropwizard, 一些老版本使用了废弃的第三方库。 而且早期的Dropwizzard也很难升级
因为某些原因错误被当作普通文本, 如果你希望响应结果总是JSON,这可能有问题
确保使用最新的Dropwizard, 一些老版本使用了废弃的第三方库。 而且早期的Dropwizzard也很难升级
例子
package com.example.helloworld;
import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import com.example.helloworld.resources.HelloWorldResource;
import com.example.helloworld.health.TemplateHealthCheck;
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {
public static void main(String[] args) throws Exception {
new HelloWorldApplication().run(args);
}
@Override
public String getName() {
return "hello-world";
}
@Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
// nothing to do yet
}
@Override
public void run(HelloWorldConfiguration configuration,
Environment environment) {
// nothing to do yet
}
}
import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import com.example.helloworld.resources.HelloWorldResource;
import com.example.helloworld.health.TemplateHealthCheck;
public class HelloWorldApplication extends Application<HelloWorldConfiguration> {
public static void main(String[] args) throws Exception {
new HelloWorldApplication().run(args);
}
@Override
public String getName() {
return "hello-world";
}
@Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
// nothing to do yet
}
@Override
public void run(HelloWorldConfiguration configuration,
Environment environment) {
// nothing to do yet
}
}
个人不想为大的项目推荐这个框架。不过如果你想尝试的话,你不会失望的。
主要是这个框架使用最好的现代的Java web 组件,组装成一个简单易用的框架。
不幸的是这也带来了它的问题。 将这些库糅合起来可能导致不可预见的问题。
这也是我为什么给它减掉0.5颗星,而没有评为满分5颗星。
Restlet
诞生时间: 2005
评分: 4.5/5
评分: 4.5/5
是什么?
Restlet 帮助Java程序员建立大规模的快速的符合 RESTful 架构模式的web api。
它提供了强大的路由和 filtering 系统。
统一的client/server Java API.
满足所有主要的平台 (Java SE/EE, Google AppEngine, OSGi, GWT, Android) 以及提供了无数的扩展以满足程序员的需求。
据我说知,它是第一个 java RESTful web 框架。很多公司都在用它,但是你可能从未听说过它,好像它已经不可见了。
优点
强大
企业级的框架
多平台 Java SE, Java EE, Google Web Toolkit, Google AppEngine, Android, OSGi environments
支持JAX-RS (就像 Jersey)
大部分高级 RESTful 支持
模块化
支持其它库
开发一直活跃
智能的url绑定, 全功能的 URI 路由
有相关的书籍
企业级的框架
多平台 Java SE, Java EE, Google Web Toolkit, Google AppEngine, Android, OSGi environments
支持JAX-RS (就像 Jersey)
大部分高级 RESTful 支持
模块化
支持其它库
开发一直活跃
智能的url绑定, 全功能的 URI 路由
有相关的书籍
缺点
非常陡峭的学习曲线
关闭的社区,尽管 StackOverflow 上还是开放的
不再流行,更多的是因为 Play Framework 和 Jersey
关闭的社区,尽管 StackOverflow 上还是开放的
不再流行,更多的是因为 Play Framework 和 Jersey
例子
public class Part03 extends ServerResource {
public static void main(String[] args) throws Exception {
// Create the HTTP server and listen on port 8182
new Server(Protocol.HTTP, 8182, Part03.class).start();
}
@Get("txt")
public String toString() {
return "hello, world";
}
}
public static void main(String[] args) throws Exception {
// Create the HTTP server and listen on port 8182
new Server(Protocol.HTTP, 8182, Part03.class).start();
}
@Get("txt")
public String toString() {
return "hello, world";
}
}
尽管这个框架还一直流行,考虑到它的年纪和当前的完成度,我不能给它5颗星。
Play Framework
诞生时间: 2011
评分: 4/5
评分: 4/5
是什么?
使用Play Framework 很容易地创建,构建和发布 web 应用程序,支持 Java & Scala。
它使用Akka, 基于一个轻量级的无状态的架构。
它应该应用于大规模地低CPU和内存消耗的应用。
优点
易于开发
快,但是没有其它的一些框架快
基于 Netty, 支持非阻塞的 I/O. 并行处理远程调用的时候很优秀
社区很大
快速的项目构建和启动
模块化
MVC
REST, JSON/XML, Web Sockets, non-blocking I/O
只需刷新浏览器就可以看到最新的改变
支持Async
有出版的书
快,但是没有其它的一些框架快
基于 Netty, 支持非阻塞的 I/O. 并行处理远程调用的时候很优秀
社区很大
快速的项目构建和启动
模块化
MVC
REST, JSON/XML, Web Sockets, non-blocking I/O
只需刷新浏览器就可以看到最新的改变
支持Async
有出版的书
缺点
版本2.0 是最有争议的Java框架。 切换至Switch to Scala made some Java developers outraged.
不向后兼容; Play 2.X 重写了
号称轻量级,但有些臃肿
SBT构建工具. 号称 Maven 杀手, 但是从没有优秀到替换它。难以学习和配置
非 servlet
Breaking changes across releases
不向后兼容; Play 2.X 重写了
号称轻量级,但有些臃肿
SBT构建工具. 号称 Maven 杀手, 但是从没有优秀到替换它。难以学习和配置
非 servlet
Breaking changes across releases
例子
package controllers
import play.api._
import play.api.mvc._
class Application extends Controller {
def hello(name: String) = Action {
Ok("Hello " + name + "!")
}
}
import play.api._
import play.api.mvc._
class Application extends Controller {
def hello(name: String) = Action {
Ok("Hello " + name + "!")
}
}
抱怨归抱怨,我还是一直喜欢和首选这个框架。
不幸的是,我只能给它4颗星。我坚信 基于JAX-RS的框架更适合 RESTful web services.
Restx
诞生时间: 2013
评分: 3.5/5
评分: 3.5/5
是什么?
Restx 是一个轻量级的,模块化的,特性众多的,超快的开源 Java REST 框架。
优点
快速,轻量级
容易搭建
真正的微框架
模块化
支持其它库
支持MongoDB
容易搭建
真正的微框架
模块化
支持其它库
支持MongoDB
缺点
不友好的令人迷惑的文档。对于这类框架我期望能有好一点的文档
太年轻
目前还不支持异步Async
太年轻
目前还不支持异步Async
例子
@POST("/message/{id}")
public Message sayHello(String id, // path param
Message msg // body param
) {
return msg.setMessage(String.format(
"%s @ %s",
msg.getMessage(), DateTime.now().toString("HH:mm:ss")));
}
@GET("/message/{id}")
public Message sayHello(String id, // path param
String who // query param
) {
return new Message().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
public Message sayHello(String id, // path param
String who // query param
) {
return new Message().setMessage(String.format(
"hello %s, it's %s",
who, DateTime.now().toString("HH:mm:ss")));
}
@POST("/message/{id}")
public Message sayHello(String id, // path param
Message msg // body param
) {
return msg.setMessage(String.format(
"%s @ %s",
msg.getMessage(), DateTime.now().toString("HH:mm:ss")));
}
真心来讲我没有在这个框架上花费太多时间。
不值得在另一个框架上花费太多精力,Java框架市场已经越来越碎片化了,就像 JavaScript 市场,应该停止这种趋势了。
Spark Framework
诞生时间: 2011
评分: 3.5/5
评分: 3.5/5
是什么?
不要和 Apache 的大数据框架 Spark 弄混,
这里的 Spark 框架是一个轻量级的 Java web 框架,用来进行快速的开发(50% Spark用户使用 Spark 创建 REST APIs)。
它受 Ruby 框架 Sinatra 启发。
它有一个不到1M的最小化的内核, 提供了所有基本的特性, 用来构建 RESTful 或者传统的 web 应用程序。
优点
快,轻量级
优秀的快速原型
易于搭建
经常和AngularJS搭配使用
真正的微框架
使用 Jetty
可以用在容器中或者独立运行
优秀的快速原型
易于搭建
经常和AngularJS搭配使用
真正的微框架
使用 Jetty
可以用在容器中或者独立运行
缺点
文档可以更好,它不适合初学者
不适合大型项目
社区小
不适合大型项目
社区小
例子
import static spark.Spark.*;
public class HelloWorld {
public static void main(String[] args) {
get("/hello", (req, res) -> "Hello World");
}
}
public class HelloWorld {
public static void main(String[] args) {
get("/hello", (req, res) -> "Hello World");
}
}
这个框架适合初始开发。主要用作小小项目或者原型。
Ninja Web Framework
诞生时间: 2012
评分: 3.5/5
评分: 3.5/5
是什么?
Ninja Web Framework是全栈的 java web framework。稳定, 快速, 可靠, 产品级.
它提供了开发,测试,发布,维护 RESTful web应用的一切(Servlets, Guice, JPA, Flyway migrations, Maven, etc.).
就像 DropWizzard, Ninja Web Framework 是一个集成的软件栈。
你不必建立你自己的,只需使用 Maven archetype生成一个新的项目,导入到IDE中就可以开始编码了。
优点
快
快速的项目构建和启动
模块化
XML, HTML, JSON渲染
也支持其它的库 (如 Guice, Logback, Guava, etc.)
很好的数据持久化和缓存
不喜欢 servlet container? U可以选择你喜欢的容器
如果根本就不喜欢容器,可以使用 standalone 模式, 使用Jetty作为一个自执行的jar
快速的项目构建和启动
模块化
XML, HTML, JSON渲染
也支持其它的库 (如 Guice, Logback, Guava, etc.)
很好的数据持久化和缓存
不喜欢 servlet container? U可以选择你喜欢的容器
如果根本就不喜欢容器,可以使用 standalone 模式, 使用Jetty作为一个自执行的jar
缺点
同样,就像DropWizzard, 文档有但是不够好。我花了好长时间去了解它。
这个框架也依赖很多其它的库,有时候想得到所需的信息很麻烦。
不怎么出名,社区小。 有谣言说这个框架是由那些切换到Scala的 Play 2.X 用户创建的
例子
package controllers;
public class ApplicationController {
public Result index() {
Person person = new Person();
person.name = "John Johnson";
return Results.json().render(person);
}
}
public class ApplicationController {
public Result index() {
Person person = new Person();
person.name = "John Johnson";
return Results.json().render(person);
}
}
看起来不错,但是在它成熟之前我还是把它丢在一边吧。
RestExpress
诞生时间: 2009
评分: 3/5
评分: 3/5
是什么?
RestExpress是一个非容器的轻量级的 Netty HTTP栈的包装, 以便更容易地创建 Java RESTful services.
RestExpress 目标是支持最好的 RESTful 实践。
优点
真正的微框架
顶级的性能,快,可靠
XML/JSON
最老的也是最稳定的 RESTful 框架之一
顶级的性能,快,可靠
XML/JSON
最老的也是最稳定的 RESTful 框架之一
缺点
没有文档
几乎没有支持
很小的社区
几乎没有支持
很小的社区
例子
package com.example;
import java.io.IOException;
import io.netty.handler.codec.http.HttpMethod;
import org.restexpress.RestExpress;
public class Main{
public static RestExpress startServer(String[] args) throws IOException {
RestExpress server = new RestExpress();
MyResource r = new MyResource();
server.uri("/myapp/myresource", r)
.method(HttpMethod.GET)
.noSerialization();
server.uri("/myapp/myresource", r)
.method(HttpMethod.POST);
server.bind(8080);
return server;
}
public static void main(String[] args) throws Exception{
RestExpress server = startServer(args);
System.out.println("Hit enter to stop it...");
System.in.read();
server.shutdown();
}
}
import java.io.IOException;
import io.netty.handler.codec.http.HttpMethod;
import org.restexpress.RestExpress;
public class Main{
public static RestExpress startServer(String[] args) throws IOException {
RestExpress server = new RestExpress();
MyResource r = new MyResource();
server.uri("/myapp/myresource", r)
.method(HttpMethod.GET)
.noSerialization();
server.uri("/myapp/myresource", r)
.method(HttpMethod.POST);
server.bind(8080);
return server;
}
public static void main(String[] args) throws Exception{
RestExpress server = startServer(args);
System.out.println("Hit enter to stop it...");
System.in.read();
server.shutdown();
}
}
尽管这个框架超级快,我也不想推荐它。文档缺乏以及没有支持使它成为一个欠佳的框架。看在速度的份上给它3颗星。
举个简单的例子
如果你想获得服务器数据库中的所有数据;那么你可以在浏览器或者利用Ajax的GET方法,将路径设置好;
例如:localhost:8080/Student(项目名称)/studentinfo(项目服务总体前缀)/student(处理student对象的签注)/getStudentInfo(最后前缀)。
这样就可以获取所有学生信息。你可以选择GET获取的数据的返回类型:JSON,XML,TEXT_HTML(String)..
获取之后,你可以通过JS将这些数据塞到html或者jsp页面上。
(摘自百度百科:https://baike.baidu.com/item/Jersey/17577306?fr=aladdin)
基本组成要素
注解:
也是核心要素,用于指明相关资源的请求路径,方法,参数及其类型等;
Filter和Interceptors:
类似Servlet的对应类,主要用于资源提前过滤,权限管控,压缩/解压等;
媒体类型:
支持JSON/XML/Multipart等类型;、
容器或运行环境:
主要用于支撑Jersey运行
主要有原生的JDK Http Server/ Grizzly HTTP Server等
当然也可以直接放在如jetty这样的Web容器中运行;
服务端和客户端:
主要作为数据收发的窗口
Jersey主要组件
Table 2.1. Jersey Core
Table 2.1. Jersey Core
Table 2.2. Jersey Containers
Table 2.2. Jersey Containers
Table 2.3. Jersey Connectors
Table 2.3. Jersey Connectors
Table 2.4. Jersey Media
Table 2.4. Jersey Media
Table 2.6. Jersey Test Framework
子主题
Table 2.7. Jersey Test Framework Providers
子主题
工作原理说明
REST 中最重要的概念是资源(resources),使用全球 ID(通常使用 URI)标识。
客户端应用程序使用 HTTP 方法(GET/ POST/ PUT/ DELETE)操作资源或资源集。
RESTful Web 服务是使用 HTTP 和 REST 原理实现的Web 服务。
通常,RESTful Web 服务应该定义以下方面:
Web 服务的基/根 URI,比如 http://host/<appcontext>/resources。
支持 MIME 类型的响应数据,包括 JSON/XML/ATOM 等等。
服务支持的操作集合(例如 POST、GET、PUT 或 DELETE)
Web 服务的基/根 URI,比如 http://host/<appcontext>/resources。
支持 MIME 类型的响应数据,包括 JSON/XML/ATOM 等等。
服务支持的操作集合(例如 POST、GET、PUT 或 DELETE)
使用指导
通过Maven构建模板创建示例项目示例
增加如下仓库定义到pom.xml:
<repository>
<id>snapshot-repository.java.net</id>
<name>Java.net Snapshot Repository for Maven</name>
<url>https://maven.java.net/content/repositories/snapshots/</url>
<layout>default</layout>
</repository>
<id>snapshot-repository.java.net</id>
<name>Java.net Snapshot Repository for Maven</name>
<url>https://maven.java.net/content/repositories/snapshots/</url>
<layout>default</layout>
</repository>
使用如下命令即可完成相关demo程序的下载,并自动完成项目构建,并可以导入到eclipse进行开发测试:
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example \
-DarchetypeVersion=2.26
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example \
-DarchetypeVersion=2.26
通过如下命令清理并编译启动main函数:
mvn clean test
mvn exec:java
mvn exec:java
测试验证1
在浏览器中,可以通过如下链接进行访问:
http://localhost:8080/myapp/myresource
或者通过如下指令获取:
curl -i http://localhost:8080/myapp/myresource
返回结果为:
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Sun, 26 May 2013 18:27:19 GMT
Content-Length: 7
Got it!
Content-Type: text/plain
Date: Sun, 26 May 2013 18:27:19 GMT
Content-Length: 7
Got it!
测试验证2
通过如下程序启动服务:
public class Main {
// Base URI the Grizzly HTTP server will listen on
public static final String BASE_URI = "http://localhost:8080/myapp/";
/**
* Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
* @return Grizzly HTTP server.
*/
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in com.example package
final ResourceConfig rc = new ResourceConfig().packages("com.example");
// packages("org.foo.rest;org.bar.rest");
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
/**
* Main method.
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
System.in.read();
server.stop();
}
}
// Base URI the Grizzly HTTP server will listen on
public static final String BASE_URI = "http://localhost:8080/myapp/";
/**
* Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
* @return Grizzly HTTP server.
*/
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in com.example package
final ResourceConfig rc = new ResourceConfig().packages("com.example");
// packages("org.foo.rest;org.bar.rest");
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
/**
* Main method.
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
System.in.read();
server.stop();
}
}
可以看出是使用GrizzlyHttpServerFactory创建的一个HttpServer实例类。
那么具体的服务是由谁提供的呢?如下展示了对应服务类:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* Root resource (exposed at "myresource" path)
*/
@Path("myresource")
public class MyResource {
/**
* Method handling HTTP GET requests. The returned object will be sent
* to the client as "text/plain" media type.
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getIt() {
return "Got it!";
}
}
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* Root resource (exposed at "myresource" path)
*/
@Path("myresource")
public class MyResource {
/**
* Method handling HTTP GET requests. The returned object will be sent
* to the client as "text/plain" media type.
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getIt() {
return "Got it!";
}
}
其中@Path(“myresource”)中的myresource字段就是刚才使用的访问链接所使用的sub-path.
由于我们是在地址栏输入访问链接,自然使用http协议的GET方法,由@GET注释标注匹配的调用
所以自动会适配调用getIt函数,并返回文本格式,返回格式是由@Produces指定,
MediaType.TEXT_PLAIN表示为超文本格式。
返回结果为:
$ curl -v http://localhost:8080/myapp/myresource
* About to connect() to localhost port 8080 (#0)
* Trying ::1...
* Connection refused
* Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /myapp/myresource HTTP/1.1
> User-Agent: curl/7.25.0 (x86_64-apple-darwin11.3.0) libcurl/7.25.0 OpenSSL/1.0.1e zlib/1.2.7 libidn/1.22
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Date: Sun, 26 May 2013 18:29:18 GMT
< Content-Length: 7
<
* Connection #0 to host localhost left intact
Got it!* Closing connection #0
* About to connect() to localhost port 8080 (#0)
* Trying ::1...
* Connection refused
* Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /myapp/myresource HTTP/1.1
> User-Agent: curl/7.25.0 (x86_64-apple-darwin11.3.0) libcurl/7.25.0 OpenSSL/1.0.1e zlib/1.2.7 libidn/1.22
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Date: Sun, 26 May 2013 18:29:18 GMT
< Content-Length: 7
<
* Connection #0 to host localhost left intact
Got it!* Closing connection #0
Jersey常用注解解释
增删改查类
@GET
查询请求
相当于数据库的查询数据操作
@PUT
插入请求
相当于数据库的插入数据操作
@POST
更新请求
相当于数据库的更新数据操作
@DELETE
删除请求
相当于数据的删除数据操作
路径类
@Path
uri路径
定义资源的访问路径,client通过这个路径访问资源。
比如:@Path("user")
@PathParam
uri路径参数
写在方法的参数中,获得请求路径参数。
比如:@PathParam("username") String userName
参数注解类(@*Param)
@QueryParam
uri路径请求参数
写在方法的参数中,获得请求路径附带的参数。
比如:@QueryParam("desc") String desc
@DefaultValue
设置@QueryParam参数的默认值
如果@QueryParam没有接收到值,就使用默认值。
比如:@DefaultValue("description") @QueryParam("desc") String desc
@FormParam
form传递的参数
接受form传递过来的参数。
比如:@FormParam("name") String userName
@BeanParam
通过Bean的形式传递参数
接受client传递的bean类型的参数,同时这个bean可以在属性上配置@FormParam用以解决client的属性名称和bean的属性名称不一致的问题。
比如:@BeanParam User user
生产者与消费者
@Produces
指定返回MIME格式
资源按照那种数据格式返回
可取的值有:MediaType.APPLICATION_XXX。
比如:@Produces(MediaType.APPLICATION_XML)
@Consumes
接受指定的MIME格式
只有符合这个参数设置的请求再能访问到这个资源。
比如@Consumes("application/x-www-form-urlencoded")
XML类
@XmlRootElement
将bean转换为xml
将bean以xml或json的格式返回,必须要这个注解。
比如:
@XmlRootElement
public class User{...}
@XmlRootElement
public class User{...}
@XmlElements+@XmlElement
@XmlElements({
@XmlElement(name = "first", type = First.class),
@XmlElement(name = "second", type = Second.class),
@XmlElement(name = "third", type = Third.class),
@XmlElement(name = "fourth", type = Fourth.class),
@XmlElement(name = "fifth", type = Fifth.class),
@XmlElement(name = "sixth", type = Sixth.class),
@XmlElement(name = "seventh", type = Seventh.class)})
private List<Dimension> dimensions = new ArrayList<>();
@XmlElement(name = "first", type = First.class),
@XmlElement(name = "second", type = Second.class),
@XmlElement(name = "third", type = Third.class),
@XmlElement(name = "fourth", type = Fourth.class),
@XmlElement(name = "fifth", type = Fifth.class),
@XmlElement(name = "sixth", type = Sixth.class),
@XmlElement(name = "seventh", type = Seventh.class)})
private List<Dimension> dimensions = new ArrayList<>();
其他类
@Context
获得一些系统环境信息
通过@Context可以获得以下信息:UriInfo、ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse和HttpHeaders等
常用注释的使用方法指导
增删改查类
@PUT
public Response putContainer() {
System.out.println("PUT CONTAINER " + container);
URI uri = uriInfo.getAbsolutePath();
Container c = new Container(container, uri.toString());
Response r;
if (!MemoryStore.MS.hasContainer(c)) {
r = Response.created(uri).build();
} else {
r = Response.noContent().build();
}
MemoryStore.MS.createContainer(c);
return r;
}
public Response putContainer() {
System.out.println("PUT CONTAINER " + container);
URI uri = uriInfo.getAbsolutePath();
Container c = new Container(container, uri.toString());
Response r;
if (!MemoryStore.MS.hasContainer(c)) {
r = Response.created(uri).build();
} else {
r = Response.noContent().build();
}
MemoryStore.MS.createContainer(c);
return r;
}
@GET, @PUT, @POST, @DELETE, ... (HTTP Methods)
@path:
@Path("/users/{username}")
public class UserResource {
@GET
@Produces("text/xml")
public String getUser(@PathParam("username") String userName) {
...
}
}
public class UserResource {
@GET
@Produces("text/xml")
public String getUser(@PathParam("username") String userName) {
...
}
}
可以加上通配符:
@Path("users/{username: [a-zA-Z][a-zA-Z_0-9]*}")
@Produces
@Path("/myResource")
@Produces("text/plain")
public class SomeResource {
@GET
public String doGetAsPlainText() {
...
}
@GET
@Produces("text/html")
public String doGetAsHtml() {
...
}
}
@Produces("text/plain")
public class SomeResource {
@GET
public String doGetAsPlainText() {
...
}
@GET
@Produces("text/html")
public String doGetAsHtml() {
...
}
}
@GET
@Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
...
}
@Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
...
}
甚至可以设置权重
@GET
@Produces({"application/xml; qs=0.9", "application/json"})
public String doGetAsXmlOrJson() {
...
}
@Produces({"application/xml; qs=0.9", "application/json"})
public String doGetAsXmlOrJson() {
...
}
@Consumes
@POST
@Consumes("text/plain")
public void postClichedMessage(String message) {
// Store the message
}
@Consumes("text/plain")
public void postClichedMessage(String message) {
// Store the message
}
@POST
@Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
// Store the message
}
@Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
// Store the message
}
Parameter Annotations (@*Param)
@Path("smooth")
@GET
public Response smooth(
@DefaultValue("2") @QueryParam("step") int step,
@DefaultValue("true") @QueryParam("min-m") boolean hasMin,
@DefaultValue("true") @QueryParam("max-m") boolean hasMax,
@DefaultValue("true") @QueryParam("last-m") boolean hasLast,
@DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
@DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
@DefaultValue("red") @QueryParam("last-color") ColorParam lastColor) {
...
}
@GET
public Response smooth(
@DefaultValue("2") @QueryParam("step") int step,
@DefaultValue("true") @QueryParam("min-m") boolean hasMin,
@DefaultValue("true") @QueryParam("max-m") boolean hasMax,
@DefaultValue("true") @QueryParam("last-m") boolean hasLast,
@DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
@DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
@DefaultValue("red") @QueryParam("last-color") ColorParam lastColor) {
...
}
其中实体类定义如下:
public class ColorParam extends Color {
public ColorParam(String s) {
super(getRGB(s));
}
private static int getRGB(String s) {
if (s.charAt(0) == '#') {
try {
Color c = Color.decode("0x" + s.substring(1));
return c.getRGB();
} catch (NumberFormatException e) {
throw new WebApplicationException(400);
}
} else {
try {
Field f = Color.class.getField(s);
return ((Color)f.get(null)).getRGB();
} catch (Exception e) {
throw new WebApplicationException(400);
}
}
}
}
public ColorParam(String s) {
super(getRGB(s));
}
private static int getRGB(String s) {
if (s.charAt(0) == '#') {
try {
Color c = Color.decode("0x" + s.substring(1));
return c.getRGB();
} catch (NumberFormatException e) {
throw new WebApplicationException(400);
}
} else {
try {
Field f = Color.class.getField(s);
return ((Color)f.get(null)).getRGB();
} catch (Exception e) {
throw new WebApplicationException(400);
}
}
}
}
类似实体类需要满足入校条件:
1. Be a primitive type;
2. Have a constructor that accepts a single String argument;
3. Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String) and java.util.UUID.fromString(String));
4. Have a registered implementation of javax.ws.rs.ext.ParamConverterProvider JAX-RS extension SPI that returns a javax.ws.rs.ext.ParamConverter instance capable of a "from string" conversion for the type. or
5. Be List<T>, Set<T> or SortedSet<T>, where T satisfies 2 or 3 above. The resulting collection is read-only.
2. Have a constructor that accepts a single String argument;
3. Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String) and java.util.UUID.fromString(String));
4. Have a registered implementation of javax.ws.rs.ext.ParamConverterProvider JAX-RS extension SPI that returns a javax.ws.rs.ext.ParamConverter instance capable of a "from string" conversion for the type. or
5. Be List<T>, Set<T> or SortedSet<T>, where T satisfies 2 or 3 above. The resulting collection is read-only.
Parameter Annotations (@*Param)
@PathParam
@MatrixParam
@HeaderParam
@CookieParam
@FormParam
参数注解都遵循@QueryParam一样的规则。
其中需要特备说明的@FormParam的使用,在获取"application/x-www-form-urlencoded" MINE媒体类型时,非常有用:
@POST
@Consumes("application/x-www-form-urlencoded")
public void post(@FormParam("name") String name) {
// Store the message
}
@Consumes("application/x-www-form-urlencoded")
public void post(@FormParam("name") String name) {
// Store the message
}
@Context
如果需要获取一些特殊的参数,可通过如下方法获取:
//获取URI或者查询参数
@GET
public String get(@Context UriInfo ui) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
MultivaluedMap<String, String> pathParams = ui.getPathParameters();
}
public String get(@Context UriInfo ui) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
MultivaluedMap<String, String> pathParams = ui.getPathParameters();
}
//获取请求头
@GET
public String get(@Context HttpHeaders hh) {
MultivaluedMap<String, String> headerParams = hh.getRequestHeaders();
Map<String, Cookie> pathParams = hh.getCookies();
}
public String get(@Context HttpHeaders hh) {
MultivaluedMap<String, String> headerParams = hh.getRequestHeaders();
Map<String, Cookie> pathParams = hh.getCookies();
}
@BeanParam
public class MyBeanParam {
@PathParam("p")
private String pathParam;
@MatrixParam("m")
@Encoded
@DefaultValue("default")
private String matrixParam;
@HeaderParam("header")
private String headerParam;
private String queryParam;
public MyBeanParam(@QueryParam("q") String queryParam) {
this.queryParam = queryParam;
}
public String getPathParam() {
return pathParam;
}
...
}
@PathParam("p")
private String pathParam;
@MatrixParam("m")
@Encoded
@DefaultValue("default")
private String matrixParam;
@HeaderParam("header")
private String headerParam;
private String queryParam;
public MyBeanParam(@QueryParam("q") String queryParam) {
this.queryParam = queryParam;
}
public String getPathParam() {
return pathParam;
}
...
}
使用方式
@POST
public void post(@BeanParam MyBeanParam beanParam, @BeanParam AnotherBean anotherBean, @PathParam("p") pathParam,
String entity) {
// beanParam.getPathParam() == pathParam
...
}
public void post(@BeanParam MyBeanParam beanParam, @BeanParam AnotherBean anotherBean, @PathParam("p") pathParam,
String entity) {
// beanParam.getPathParam() == pathParam
...
}
子资源创建
@Singleton
@Path("/printers")
public class PrintersResource {
@GET
@Produces({"application/json", "application/xml"})
public WebResourceList getMyResources() { ... }
@GET @Path("/list")
@Produces({"application/json", "application/xml"})
public WebResourceList getListOfPrinters() { ... }
@GET @Path("/jMakiTable")
@Produces("application/json")
public PrinterTableModel getTable() { ... }
@GET @Path("/jMakiTree")
@Produces("application/json")
public TreeModel getTree() { ... }
@GET @Path("/ids/{printerid}")
@Produces({"application/json", "application/xml"})
public Printer getPrinter(@PathParam("printerid") String printerId) { ... }
@PUT @Path("/ids/{printerid}")
@Consumes({"application/json", "application/xml"})
public void putPrinter(@PathParam("printerid") String printerId, Printer printer) { ... }
@DELETE @Path("/ids/{printerid}")
public void deletePrinter(@PathParam("printerid") String printerId) { ... }
}
@Path("/printers")
public class PrintersResource {
@GET
@Produces({"application/json", "application/xml"})
public WebResourceList getMyResources() { ... }
@GET @Path("/list")
@Produces({"application/json", "application/xml"})
public WebResourceList getListOfPrinters() { ... }
@GET @Path("/jMakiTable")
@Produces("application/json")
public PrinterTableModel getTable() { ... }
@GET @Path("/jMakiTree")
@Produces("application/json")
public TreeModel getTree() { ... }
@GET @Path("/ids/{printerid}")
@Produces({"application/json", "application/xml"})
public Printer getPrinter(@PathParam("printerid") String printerId) { ... }
@PUT @Path("/ids/{printerid}")
@Consumes({"application/json", "application/xml"})
public void putPrinter(@PathParam("printerid") String printerId, Printer printer) { ... }
@DELETE @Path("/ids/{printerid}")
public void deletePrinter(@PathParam("printerid") String printerId) { ... }
}
子类实现方式
@Path("/item")
public class ItemResource {
@Context UriInfo uriInfo;
@Path("content")
public ItemContentResource getItemContentResource() {
return new ItemContentResource();
}
@GET
@Produces("application/xml")
public Item get() { ... }
}
}
public class ItemContentResource {
@GET
public Response get() { ... }
@PUT
@Path("{version}")
public void put(@PathParam("version") int version,
@Context HttpHeaders headers,
byte[] in) {
...
}
}
public class ItemResource {
@Context UriInfo uriInfo;
@Path("content")
public ItemContentResource getItemContentResource() {
return new ItemContentResource();
}
@GET
@Produces("application/xml")
public Item get() { ... }
}
}
public class ItemContentResource {
@GET
public Response get() { ... }
@PUT
@Path("{version}")
public void put(@PathParam("version") int version,
@Context HttpHeaders headers,
byte[] in) {
...
}
}
资源(Resource)的生命周期
Table 3.1. Resource scopes
子主题
展示一个比较全的可注入的使用方式:
@Path("resource")
public static class SummaryOfInjectionsResource {
@QueryParam("query")
String param; // injection into a class field
@GET
public String get(@QueryParam("query") String methodQueryParam) {
// injection into a resource method parameter
return "query param: " + param;
}
@Path("sub-resource-locator")
public Class<SubResource> subResourceLocator(@QueryParam("query") String subResourceQueryParam) {
// injection into a sub resource locator parameter
return SubResource.class;
}
public SummaryOfInjectionsResource(@QueryParam("query") String constructorQueryParam) {
// injection into a constructor parameter
}
@Context
public void setRequest(Request request) {
// injection into a setter method
System.out.println(request != null);
}
}
public static class SubResource {
@GET
public String get() {
return "sub resource";
}
}
public static class SummaryOfInjectionsResource {
@QueryParam("query")
String param; // injection into a class field
@GET
public String get(@QueryParam("query") String methodQueryParam) {
// injection into a resource method parameter
return "query param: " + param;
}
@Path("sub-resource-locator")
public Class<SubResource> subResourceLocator(@QueryParam("query") String subResourceQueryParam) {
// injection into a sub resource locator parameter
return SubResource.class;
}
public SummaryOfInjectionsResource(@QueryParam("query") String constructorQueryParam) {
// injection into a constructor parameter
}
@Context
public void setRequest(Request request) {
// injection into a setter method
System.out.println(request != null);
}
}
public static class SubResource {
@GET
public String get() {
return "sub resource";
}
}
服务端(JavaSE开发环境)
HTTP server
Simple server
Using Jersey with the Simple framework
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build();
ResourceConfig config = new ResourceConfig(MyResource.class);
SimpleContainer server = SimpleContainerFactory.create(baseUri, config);
ResourceConfig config = new ResourceConfig(MyResource.class);
SimpleContainer server = SimpleContainerFactory.create(baseUri, config);
The necessary container extension module dependency in this case is:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-simple-http</artifactId>
<version>2.26</version>
</dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-simple-http</artifactId>
<version>2.26</version>
</dependency>
JDK Http Server
Example 4.5. Using Jersey with JDK HTTP Server;
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build();
ResourceConfig config = new ResourceConfig(MyResource.class);
HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config)
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build();
ResourceConfig config = new ResourceConfig(MyResource.class);
HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config)
A JDK HTTP Container dependency needs to be added:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jdk-http</artifactId>
<version>2.26</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jdk-http</artifactId>
<version>2.26</version>
</dependency>
Grizzly HTTP Server
Using Jersey with Grizzly HTTP Server
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build();
ResourceConfig config = new ResourceConfig(MyResource.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, config);
ResourceConfig config = new ResourceConfig(MyResource.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, config);
The container extension module dependency to be added is:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>2.26</version>
</dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>2.26</version>
</dependency>
Jetty HTTP Server
Using Jersey with Jetty HTTP Server
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build();
ResourceConfig config = new ResourceConfig(MyResource.class);
Server server = JettyHttpContainerFactory.createServer(baseUri, config);
ResourceConfig config = new ResourceConfig(MyResource.class);
Server server = JettyHttpContainerFactory.createServer(baseUri, config);
And, of course, we add the necessary container extension module dependency:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jetty-http</artifactId>
<version>2.26</version>
</dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jetty-http</artifactId>
<version>2.26</version>
</dependency>
Netty HTTP Server
Using Jersey with Netty HTTP Server
URI baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build();
ResourceConfig resourceConfig = new ResourceConfig(HelloWorldResource.class);
Channel server = NettyHttpContainerProvider.createServer(baseUri, resourceConfig, false);
ResourceConfig resourceConfig = new ResourceConfig(HelloWorldResource.class);
Channel server = NettyHttpContainerProvider.createServer(baseUri, resourceConfig, false);
And, of course, we add the necessary container extension module dependency:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-netty-http</artifactId>
<version>2.26</version>
</dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-netty-http</artifactId>
<version>2.26</version>
</dependency>
Servlet-based Deployment
Hooking up Jersey as a Servlet
<web-app>
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
...
</init-param>
</servlet>
...
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/myApp/*</url-pattern>
</servlet-mapping>
...
</web-app>
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
...
</init-param>
</servlet>
...
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/myApp/*</url-pattern>
</servlet-mapping>
...
</web-app>
Hooking up Jersey as a Servlet Filter
<web-app>
<filter>
<filter-name>MyApplication</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
...
</init-param>
</filter>
...
<filter-mapping>
<filter-name>MyApplication</filter-name>
<url-pattern>/myApp/*</url-pattern>
</filter-mapping>
...
</web-app>
<filter>
<filter-name>MyApplication</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
...
</init-param>
</filter>
...
<filter-mapping>
<filter-name>MyApplication</filter-name>
<url-pattern>/myApp/*</url-pattern>
</filter-mapping>
...
</web-app>
Custom Application subclass
Configuring Jersey container Servlet or Filter to use custom Application subclass
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.foo.MyApplication</param-value>
</init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.foo.MyApplication</param-value>
</init-param>
Configuring Jersey container Servlet or Filter to use package scanning
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
org.foo.myresources,org.bar.otherresources
</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.scanning.recursive</param-name>
<param-value>false</param-value>
</init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
org.foo.myresources,org.bar.otherresources
</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.scanning.recursive</param-name>
<param-value>false</param-value>
</init-param>
Configuring Jersey container Servlet or Filter to use a list of classes
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.foo.myresources.MyDogResource,
org.bar.otherresources.MyCatResource
</param-value>
</init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.foo.myresources.MyDogResource,
org.bar.otherresources.MyCatResource
</param-value>
</init-param>
Servlet 3.x Container
Deployment of a JAX-RS application using @ApplicationPath with Servlet 3.0
@ApplicationPath("resources")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.rest;org.bar.rest");
}
}
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.rest;org.bar.rest");
}
}
Configuration of maven-war-plugin to ignore missing web.xml
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
...
</plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
...
</plugins>
客户端
创建方式:
Client client = ClientBuilder.newClient();
或者带入配置参数:
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(MyClientResponseFilter.class);
clientConfig.register(new AnotherClientFilter());
Client client = ClientBuilder.newClient(clientConfig);
clientConfig.register(MyClientResponseFilter.class);
clientConfig.register(new AnotherClientFilter());
Client client = ClientBuilder.newClient(clientConfig);
或者使用连写:
Client client = ClientBuilder.newClient(new ClientConfig()
.register(MyClientResponseFilter.class)
.register(new AnotherClientFilter());
.register(MyClientResponseFilter.class)
.register(new AnotherClientFilter());
基本使用
//在client创建后,也可以给client配置相关信息:
client.register(FilterA.class)
.register(new FilterB())
.property("my-property", true);
//创建Target实例:
WebTarget webTarget = client.target("http://example.com/rest");
webTarget.register(FilterForExampleCom.class);//此配置仅对此Target实例有效
//制定子资源路径:
WebTarget resourceWebTarget = webTarget.path("resource");
WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld");
WebTarget helloworldWebTargetWithQueryParam=helloworldWebTarget.queryParam("greeting", "Hi World!");
//执行一次访问请求:
Invocation.Builder invocationBuilder =
helloworldWebTargetWithQueryParam.request(MediaType.TEXT_PLAIN_TYPE);
invocationBuilder.header("some-header", "true");
Response response = invocationBuilder.get();//调用GET函数
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
client.register(FilterA.class)
.register(new FilterB())
.property("my-property", true);
//创建Target实例:
WebTarget webTarget = client.target("http://example.com/rest");
webTarget.register(FilterForExampleCom.class);//此配置仅对此Target实例有效
//制定子资源路径:
WebTarget resourceWebTarget = webTarget.path("resource");
WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld");
WebTarget helloworldWebTargetWithQueryParam=helloworldWebTarget.queryParam("greeting", "Hi World!");
//执行一次访问请求:
Invocation.Builder invocationBuilder =
helloworldWebTargetWithQueryParam.request(MediaType.TEXT_PLAIN_TYPE);
invocationBuilder.header("some-header", "true");
Response response = invocationBuilder.get();//调用GET函数
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
上面的函数是使用GET函数,如果是需要调用POST函数,可以使用如下方法:
Response postResponse =
helloworldWebTarget.request(MediaType.TEXT_PLAIN_TYPE)
.post(Entity.entity("A string entity to be POSTed", MediaType.TEXT_PLAIN));
helloworldWebTarget.request(MediaType.TEXT_PLAIN_TYPE)
.post(Entity.entity("A string entity to be POSTed", MediaType.TEXT_PLAIN));
Fluent API style如下:
Client client = ClientBuilder.newClient(new ClientConfig()
.register(MyClientResponseFilter.class)
.register(new AnotherClientFilter()));
String entity = client.target("http://example.com/rest")
.register(FilterForExampleCom.class)
.path("resource/helloworld")
.queryParam("greeting", "Hi World!")
.request(MediaType.TEXT_PLAIN_TYPE)
.header("some-header", "true")
.get(String.class);
.register(MyClientResponseFilter.class)
.register(new AnotherClientFilter()));
String entity = client.target("http://example.com/rest")
.register(FilterForExampleCom.class)
.path("resource/helloworld")
.queryParam("greeting", "Hi World!")
.request(MediaType.TEXT_PLAIN_TYPE)
.header("some-header", "true")
.get(String.class);
更加简易的写法如下:
String responseEntity = ClientBuilder.newClient()
.target("http://example.com").path("resource/rest")
.request().get(String.class);
.target("http://example.com").path("resource/rest")
.request().get(String.class);
客户端异步获取资源
为了应对高并发的处理,可以使用线程池的技术,在创建client时,需要额外的线程去执行后续的异步请求,可以使用如下方式进行获取:
ExecutorService myExecutorService = Executors.newCachedThreadPool();
Client client = ClientBuilder.newBuilder().executorService(myExecutorService).build();
Client client = ClientBuilder.newBuilder().executorService(myExecutorService).build();
传输文件示例:
在处理流输入或者上传文件时,可使用如下技术:
InputStream in = response.readEntity(InputStream.class);
... // Read from the stream
in.close();
response.close()//在处理完所有数据后,此处需要手动关闭
... // Read from the stream
in.close();
response.close()//在处理完所有数据后,此处需要手动关闭
File f = ...
...
webTarget.request().post(Entity.entity(f, MediaType.TEXT_PLAIN_TYPE));
...
webTarget.request().post(Entity.entity(f, MediaType.TEXT_PLAIN_TYPE));
子主题
SSL Client创建方式
SslConfigurator sslConfig = SslConfigurator.newInstance()
.trustStoreFile("./truststore_client")
.trustStorePassword("secret-password-for-truststore")
.keyStoreFile("./keystore_client")
.keyPassword("secret-password-for-keystore");
SSLContext sslContext = sslConfig.createSSLContext();
Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();
Response response = client.target("https://example.com/resource").request().get();
.trustStoreFile("./truststore_client")
.trustStorePassword("secret-password-for-truststore")
.keyStoreFile("./keystore_client")
.keyPassword("secret-password-for-keystore");
SSLContext sslContext = sslConfig.createSSLContext();
Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();
Response response = client.target("https://example.com/resource").request().get();
支持简单的身份验证示例
在服务端加入如下Feature:
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("user", "superSecretPassword");
如需创建非抢占模式:
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder()
.nonPreemptive().credentials("user", "superSecretPassword").build();
当然,也可以创建没有任何凭证的Feature:
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().build();
客户端需要使用如下方式进行访问:
Response response = client.target("http://localhost:8080/rest/homer/contact").request()
.property(HTTP_AUTHENTICATION_BASIC_USERNAME, "homer")
.property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745").get();
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("user", "superSecretPassword");
如需创建非抢占模式:
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder()
.nonPreemptive().credentials("user", "superSecretPassword").build();
当然,也可以创建没有任何凭证的Feature:
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().build();
客户端需要使用如下方式进行访问:
Response response = client.target("http://localhost:8080/rest/homer/contact").request()
.property(HTTP_AUTHENTICATION_BASIC_USERNAME, "homer")
.property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745").get();
复杂场景解决方案 - Reactive Approach
为了解决更加复杂的使用场景,一个请求的执行受到上一次或者上几次请求结果的影响,这样就可以使用Reactive Client Extension:
譬如有如下需求:
We also presume that the basic services we need to work with are:
Customers service – provides information about customers of the travel agency.
Destinations service – provides a list of visited and recommended destinations for an authenticated customer.
Weather service – provides weather forecast for a given destination.
Quoting service – provides price calculation for a customer to travel to a recommended destination.
The task is to create a publicly available feature that would, for an authenticated user, display a list of 10 last visited places and also display a list of 10 new recommended destinations including weather forecast and price calculations for the user. Notice that some of the requests (to retrieve data) depend on results of previous requests. E.g. getting recommended destinations depends on obtaining information about the authenticated user first. Obtaining weather forecast depends on destination information, etc. This relationship between some of the requests is an important part of the problem and an area where you can take a real advantage of the reactive programming model.
单纯的实现方法如下:
final WebTarget destination = ...;
final WebTarget forecast = ...;
// Obtain recommended destinations.
List<Destination> recommended = Collections.emptyList();
try {
recommended = destination.path("recommended").request()
// Identify the user.
.header("Rx-User", "Sync")
// Return a list of destinations.
.get(new GenericType<List<Destination>>() {});
} catch (final Throwable throwable) {
errors.offer("Recommended: " + throwable.getMessage());
}
// Forecasts. (depend on recommended destinations)
final Map<String, Forecast> forecasts = new HashMap<>();
for (final Destination dest : recommended) {
try {
forecasts.put(dest.getDestination(),
forecast.resolveTemplate("destination", dest.getDestination()).request().get(Forecast.class));
} catch (final Throwable throwable) {
errors.offer("Forecast: " + throwable.getMessage());
}
}
对应耗费时间如下:
如果使用如下方式,时间将会减少很多:
final WebTarget destination = ...;
final WebTarget forecast = ...;
// Obtain recommended destinations. (does not depend on visited ones)
destination.path("recommended").request()
// Identify the user.
.header("Rx-User", "Async")
// Async invoker.
.async()
// Return a list of destinations.
.get(new InvocationCallback<List<Destination>>() {
@Override
public void completed(final List<Destination> recommended) {
final CountDownLatch innerLatch = new CountDownLatch(recommended.size());
// Forecasts. (depend on recommended destinations)
final Map<String, Forecast> forecasts = Collections.synchronizedMap(new HashMap<>());
for (final Destination dest : recommended) {
forecast.resolveTemplate("destination", dest.getDestination()).request()
.async()
.get(new InvocationCallback<Forecast>() {
@Override
public void completed(final Forecast forecast) {
forecasts.put(dest.getDestination(), forecast);
innerLatch.countDown();
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Forecast: " + throwable.getMessage());
innerLatch.countDown();
}
});
}
// Have to wait here for dependent requests ...
try {
if (!innerLatch.await(10, TimeUnit.SECONDS)) {
errors.offer("Inner: Waiting for requests to complete has timed out.");
}
} catch (final InterruptedException e) {
errors.offer("Inner: Waiting for requests to complete has been interrupted.");
}
// Continue with processing.
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Recommended: " + throwable.getMessage());
}
});
使用如上方法后,调用时间情况如下:
Reactive Approach实现方式:
final WebTarget destination = ...;
final WebTarget forecast = ...;
// Recommended places.
CompletionStage<List<Destination>> recommended =
destination.path("recommended")
.request()
// Identify the user.
.header("Rx-User", "CompletionStage")
// Reactive invoker.
.rx()
// Return a list of destinations.
.get(new GenericType<List<Destination>>() {})
.exceptionally(throwable -> {
errors.offer("Recommended: " + throwable.getMessage());
return Collections.emptyList();
});
// get Forecast for recommended destinations.
return recommended.thenCompose(destinations -> {
List<CompletionStage<Recommendation>> recommendations = destinations.stream().map(destination -> {
// For each destination, obtain a weather forecast ...
final CompletionStage<Forecast> forecastResult =
forecast.resolveTemplate("destination", destination.getDestination())
.request().rx().get(Forecast.class)
.exceptionally(throwable -> {
errors.offer("Forecast: " + throwable.getMessage());
return new Forecast(destination.getDestination(), "N/A");
});
//noinspection unchecked
return CompletableFuture.completedFuture(new Recommendation(destination))
// Set forecast for recommended destination.
.thenCombine(forecastResult, Recommendation::forecast)
}).collect(Collectors.toList());
// Transform List<CompletionStage<Recommendation>> to CompletionStage<List<Recommendation>>
return sequence(recommendations);
});
譬如有如下需求:
We also presume that the basic services we need to work with are:
Customers service – provides information about customers of the travel agency.
Destinations service – provides a list of visited and recommended destinations for an authenticated customer.
Weather service – provides weather forecast for a given destination.
Quoting service – provides price calculation for a customer to travel to a recommended destination.
The task is to create a publicly available feature that would, for an authenticated user, display a list of 10 last visited places and also display a list of 10 new recommended destinations including weather forecast and price calculations for the user. Notice that some of the requests (to retrieve data) depend on results of previous requests. E.g. getting recommended destinations depends on obtaining information about the authenticated user first. Obtaining weather forecast depends on destination information, etc. This relationship between some of the requests is an important part of the problem and an area where you can take a real advantage of the reactive programming model.
单纯的实现方法如下:
final WebTarget destination = ...;
final WebTarget forecast = ...;
// Obtain recommended destinations.
List<Destination> recommended = Collections.emptyList();
try {
recommended = destination.path("recommended").request()
// Identify the user.
.header("Rx-User", "Sync")
// Return a list of destinations.
.get(new GenericType<List<Destination>>() {});
} catch (final Throwable throwable) {
errors.offer("Recommended: " + throwable.getMessage());
}
// Forecasts. (depend on recommended destinations)
final Map<String, Forecast> forecasts = new HashMap<>();
for (final Destination dest : recommended) {
try {
forecasts.put(dest.getDestination(),
forecast.resolveTemplate("destination", dest.getDestination()).request().get(Forecast.class));
} catch (final Throwable throwable) {
errors.offer("Forecast: " + throwable.getMessage());
}
}
对应耗费时间如下:
如果使用如下方式,时间将会减少很多:
final WebTarget destination = ...;
final WebTarget forecast = ...;
// Obtain recommended destinations. (does not depend on visited ones)
destination.path("recommended").request()
// Identify the user.
.header("Rx-User", "Async")
// Async invoker.
.async()
// Return a list of destinations.
.get(new InvocationCallback<List<Destination>>() {
@Override
public void completed(final List<Destination> recommended) {
final CountDownLatch innerLatch = new CountDownLatch(recommended.size());
// Forecasts. (depend on recommended destinations)
final Map<String, Forecast> forecasts = Collections.synchronizedMap(new HashMap<>());
for (final Destination dest : recommended) {
forecast.resolveTemplate("destination", dest.getDestination()).request()
.async()
.get(new InvocationCallback<Forecast>() {
@Override
public void completed(final Forecast forecast) {
forecasts.put(dest.getDestination(), forecast);
innerLatch.countDown();
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Forecast: " + throwable.getMessage());
innerLatch.countDown();
}
});
}
// Have to wait here for dependent requests ...
try {
if (!innerLatch.await(10, TimeUnit.SECONDS)) {
errors.offer("Inner: Waiting for requests to complete has timed out.");
}
} catch (final InterruptedException e) {
errors.offer("Inner: Waiting for requests to complete has been interrupted.");
}
// Continue with processing.
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Recommended: " + throwable.getMessage());
}
});
使用如上方法后,调用时间情况如下:
Reactive Approach实现方式:
final WebTarget destination = ...;
final WebTarget forecast = ...;
// Recommended places.
CompletionStage<List<Destination>> recommended =
destination.path("recommended")
.request()
// Identify the user.
.header("Rx-User", "CompletionStage")
// Reactive invoker.
.rx()
// Return a list of destinations.
.get(new GenericType<List<Destination>>() {})
.exceptionally(throwable -> {
errors.offer("Recommended: " + throwable.getMessage());
return Collections.emptyList();
});
// get Forecast for recommended destinations.
return recommended.thenCompose(destinations -> {
List<CompletionStage<Recommendation>> recommendations = destinations.stream().map(destination -> {
// For each destination, obtain a weather forecast ...
final CompletionStage<Forecast> forecastResult =
forecast.resolveTemplate("destination", destination.getDestination())
.request().rx().get(Forecast.class)
.exceptionally(throwable -> {
errors.offer("Forecast: " + throwable.getMessage());
return new Forecast(destination.getDestination(), "N/A");
});
//noinspection unchecked
return CompletableFuture.completedFuture(new Recommendation(destination))
// Set forecast for recommended destination.
.thenCombine(forecastResult, Recommendation::forecast)
}).collect(Collectors.toList());
// Transform List<CompletionStage<Recommendation>> to CompletionStage<List<Recommendation>>
return sequence(recommendations);
});
支持的Media Type
Representations and Responses:
• All media types (*/*)
o byte[]
o java.lang.String
o java.io.Reader (inbound only)
o java.io.File
o javax.activation.DataSource
o javax.ws.rs.core.StreamingOutput (outbound only)
o java.lang.String
o java.io.Reader (inbound only)
o java.io.File
o javax.activation.DataSource
o javax.ws.rs.core.StreamingOutput (outbound only)
• XML media types (text/xml, application/xml and application/...+xml)
o javax.xml.transform.Source
o javax.xml.bind.JAXBElement
o Application supplied JAXB classes (types annotated with @XmlRootElement or@XmlType)
o javax.xml.bind.JAXBElement
o Application supplied JAXB classes (types annotated with @XmlRootElement or@XmlType)
• Form content (application/x-www-form-urlencoded)
o MultivaluedMap<String,String>
• Plain text (text/plain)
o java.lang.Boolean
o java.lang.Character
o java.lang.Number
o java.lang.Character
o java.lang.Number
异步的实现服务提供
Server端
ChunkedOutput example
@Path("/resource")
public class AsyncResource {
@GET
public ChunkedOutput<String> getChunkedResponse() {
final ChunkedOutput<String> output = new ChunkedOutput<String>(String.class);
new Thread() {
public void run() {
try {
String chunk;
while ((chunk = getNextString()) != null) {
output.write(chunk);
}
} catch (IOException e) {
// IOException thrown when writing the
// chunks of response: should be handled
} finally {
output.close();
// simplified: IOException thrown from
// this close() should be handled here...
}
}
}.start();
// the output will be probably returned even before
// a first chunk is written by the new thread
return output;
}
private String getNextString() {
// ... long running operation that returns
// next string or null if no other string is accessible
}
}
public class AsyncResource {
@GET
public ChunkedOutput<String> getChunkedResponse() {
final ChunkedOutput<String> output = new ChunkedOutput<String>(String.class);
new Thread() {
public void run() {
try {
String chunk;
while ((chunk = getNextString()) != null) {
output.write(chunk);
}
} catch (IOException e) {
// IOException thrown when writing the
// chunks of response: should be handled
} finally {
output.close();
// simplified: IOException thrown from
// this close() should be handled here...
}
}
}.start();
// the output will be probably returned even before
// a first chunk is written by the new thread
return output;
}
private String getNextString() {
// ... long running operation that returns
// next string or null if no other string is accessible
}
}
Client端
异步Client实现方法:
Simple client async invocation
final AsyncInvoker asyncInvoker = target().path("http://example.com/resource/").request().async();
final Future<Response> responseFuture = asyncInvoker.get();
System.out.println("Request is being processed asynchronously.");
final Response response = responseFuture.get();
// get() waits for the response to be ready
System.out.println("Response received.");
final Future<Response> responseFuture = asyncInvoker.get();
System.out.println("Request is being processed asynchronously.");
final Response response = responseFuture.get();
// get() waits for the response to be ready
System.out.println("Response received.");
其中可以加入Callback函数:
final Future<Response> responseFuture = target().path("http://example.com/resource/")
.request().async().get(new InvocationCallback<Response>() {
@Override
public void completed(Response response) {
System.out.println("Response status code "
+ response.getStatus() + " received.");
}
@Override
public void failed(Throwable throwable) {
System.out.println("Invocation failed.");
throwable.printStackTrace();
}
});
.request().async().get(new InvocationCallback<Response>() {
@Override
public void completed(Response response) {
System.out.println("Response status code "
+ response.getStatus() + " received.");
}
@Override
public void failed(Throwable throwable) {
System.out.println("Invocation failed.");
throwable.printStackTrace();
}
});
URI生成方式
final URI uri = UriBuilder.fromUri("http://{host}/{path}?q={param}")
.resolveTemplate("host", "localhost")
.resolveTemplate("path", "myApp")
.resolveTemplate("param", "value").build();
uri.toString(); // returns "http://localhost/myApp?q=value"
.resolveTemplate("host", "localhost")
.resolveTemplate("path", "myApp")
.resolveTemplate("param", "value").build();
uri.toString(); // returns "http://localhost/myApp?q=value"
Java Web的常见问题
HTTP响应的结构是怎么样的?
HTTP响应由三个部分组成:
状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果Servlet没有返回状态码,默认会返回成功的状态码HttpServletResponse.SC_OK。
HTTP头部(HTTP Header):它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在Serlet中检索HTTP的头部看这里。
主体(Body):它包含了响应的内容。它可以包含HTML代码,图片,等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。
状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果Servlet没有返回状态码,默认会返回成功的状态码HttpServletResponse.SC_OK。
HTTP头部(HTTP Header):它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在Serlet中检索HTTP的头部看这里。
主体(Body):它包含了响应的内容。它可以包含HTML代码,图片,等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。
什么是HTTP隧道?
HTTP隧道是一种利用HTTP或者是HTTPS把多种网络协议封装起来进行通信的技术。因此,HTTP协议扮演了一个打通用于通信的网络协议的管道的包装器的角色。把其他协议的请求掩盖成HTTP的请求就是HTTP隧道。
什么是URL编码和URL解码?
URL编码是负责把URL里面的空格和其他的特殊字符替换成对应的十六进制表示,反之就是解码。
什么是cookie?session和cookie有什么区别?
cookie是Web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。下面列出了session和cookie的区别:
无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用cookie,但是,session仍然是能够工作的,因为客户端无法禁用服务端的session。
在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象,cookie只能存储String类型的对象。
无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用cookie,但是,session仍然是能够工作的,因为客户端无法禁用服务端的session。
在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象,cookie只能存储String类型的对象。
Session和Cookie的区别
SpringMVC的拦截器与过滤器?+2
0 条评论
下一页