Jetty作为项目使用的轻量级web容器,使用广泛。
目前的微服务化也是以该技术为基础。熟悉如Spring-boot技术的同学不要嘲笑,本文只是技术学习而已。
基本实现
作为web服务,可以通过web.xml的进行配置,主要对servlet进行配置(本文不通过配置文件而是通过代码直接加载Servlet)
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>JAXRSRestconf</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.zte.sunquan.demo.jersey.config.DemoResourceConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAXRSRestconf</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
或者通过代码直接完成Jetty服务功能
注意我们使用的是org.glassfish.jersey.servlet.ServletContainer,你需要在POM加入相关依赖:
<!--jersey-->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.0</version>
</dependency>
<!-- Jetty -->
<dependency>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all-server</artifactId>
<version>8.1.16.v20140903</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.9.6</version>
</dependency>
DemoResourceConfig参考:
package com.zte.sunquan.demo.jersey.config;
import org.glassfish.jersey.server.ResourceConfig;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import com.zte.sunquan.demo.jersey.bean.User;
/**
* Created by sunquan on 2018/7/16.
*/
public class DemoResourceConfig extends ResourceConfig {
public DemoResourceConfig() {
//自动扫描
packages("com.zte.sunquan.demo.jersey");
register(User.class);
register(JacksonJsonProvider.class);
}
}
jersey提供了十分方便的packages,可以对自动进行扫描相应包,并解析包中类上注释定义的特性,例如在包com.zte.sunquan.demo.jersey.action中定义了类EntranceAction
package com.zte.sunquan.demo.jersey.action;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import com.google.common.collect.Sets;
import com.zte.sunquan.demo.jersey.bean.User;
import com.zte.sunquan.demo.jersey.exception.DeviceException;
import com.zte.sunquan.demo.jersey.exception.ErrorCode;
/**
* Created by sunquan on 2018/7/16.
* http://localhost:28101/demo-jersey/user
*/
@Path("user")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EntranceAction {
public static final String XML = "+xml";
public static final String JSON = "+json";
private AtomicInteger count = new AtomicInteger(0);
private static Map<Long, User> dataStore = new ConcurrentHashMap<>();
static {
dataStore.put(1L, new User(1, "sunquan", 28));
}
@POST
public void createUser(User user) {
System.out.println("add user:" + user);
if (user != null) {
dataStore.put(user.getId(), user);
}
}
@PUT
public void modifyUser(User user) {
System.out.println("modify user:" + user);
if (user != null) {
dataStore.put(user.getId(), user);
}
}
@DELETE
@Path("{id}")
public void deleteUser(@PathParam("id") long id) {
System.out.println("delete user:" + id);
dataStore.remove(id);
}
@GET
@Path("{id}")
public User getUserById(@PathParam("id") long id) throws Exception {
User user = dataStore.get(id);
return user;
}
@GET
@Path("list")
public Set<User> getUsers() throws Exception {
Set<User> users = Sets.newHashSet(dataStore.values());
return users;
}
@GET
@Path("list/by-name/{name}")
//@Produces(MediaType.TEXT_PLAIN)
public User getUserByName(@PathParam("name") String name) throws Exception {
for (User user : dataStore.values()) {
if (user.getName().equals(name))
return user;
}
//不存在该名称用户
throw new DeviceException(ErrorCode.PARAM_INVALID);
}
//通用借助URI信息
@GET
@Path("/find")
public String get(@Context UriInfo ui) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
MultivaluedMap<String, String> pathParams = ui.getPathParameters();
return "success";
}
@POST
@Path("/find2")
@Consumes(MediaType.APPLICATION_JSON)
public String get2(@Context UriInfo ui, User user) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
MultivaluedMap<String, String> pathParams = ui.getPathParameters();
return "success";
}
}
当扫描到EntranceAction后,开始分析其上的注解,熟悉JAX-RS规范知道其是指定了一个可访问的Restfull接口,
如:
http://localhost:{port}/{contextPath}/user/1
接下来要做的则是构建服务并启动服务,这里通过代码实现
package com.zte.sunquan.demo.jersey.main;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;
import com.zte.sunquan.demo.jersey.config.DemoResourceConfig;
/**
* Created by on 2018/7/16.
*/
public class Main {
public static void main(String[] args) throws Exception {
Server server = new Server(28101);
DemoResourceConfig config = new DemoResourceConfig();
ServletContainer servletContainer = new ServletContainer(config);
ServletHolder servlet = new ServletHolder(servletContainer);
ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS);
handler.setContextPath("/demo/jesery");
handler.addServlet(servlet, "/*");
server.setHandler(handler);
server.start();
System.out.println("start...in 28101");
}
}
启动服务并监听28101端口,访问上文的URL,打印:
期待的打印出现,说明以上Jetty的使用流程没有问题。
尝试查询User列表,注意返回的是一个数组
尝试查询id为1的用户
尝试查询用户名为sunquan的用户
尝试查询用户名为sunquan2的用户(用户不存在)
返回这种页面非常不友好。基本这种情况,如果能够将程序异常捕获并返回有用的信息给用户。
幸运的是ExceptionMapper则匹配了上面的需求:
package com.zte.sunquan.demo.jersey.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
/**
* Created by sunquan on 2018/7/16.
*/
@Provider
public class DemoExceptionMapper implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception e) {
Response.ResponseBuilder ResponseBuilder = null;
if (e instanceof DeviceException) {
DeviceException exp = (DeviceException) e;
ErrorEntity entity = new ErrorEntity(exp.getCode(), exp.getMessage());
ResponseBuilder = Response.ok(entity, MediaType.APPLICATION_JSON);
}else if(e instanceof NullPointerException){
String msg=e.getMessage();
System.out.println(msg);//特殊处理...
}
else {
ErrorEntity entity = new ErrorEntity(ErrorCode.OTHER_ERR.getCode(), e.getMessage());
ResponseBuilder = Response.ok(entity, MediaType.APPLICATION_JSON);
}
return ResponseBuilder.build();
}
}
上图针对DeviceException进行了拦截,并统一转化为ErrorEntity
@XmlRootElement
public class ErrorEntity {
private int resp_code;
private String resp_msg;
public ErrorEntity(int resp_code, String resp_msg) {
this.resp_code = resp_code;
this.resp_msg = resp_msg;
}
public ErrorEntity() {
}
public int getResp_code() {
return resp_code;
}
public void setResp_code(int resp_code) {
this.resp_code = resp_code;
}
public String getResp_msg() {
return resp_msg;
}
public void setResp_msg(String resp_msg) {
this.resp_msg = resp_msg;
}
}
再次访问:
这样则用户友好的多。
尝试新增用户wang
最后通过Context注解可以将URL传入后台,借助 MultivaluedMap<String, String> queryParams = ui.getQueryParameters(); MultivaluedMap<String, String> pathParams = ui.getPathParameters(); 该接口可以作很多通用性的接口 public String get(@Context UriInfo ui) { public String get2(@Context UriInfo ui, User user) {