Jetty掃盲之實踐(一)
阿新 • • 發佈:2018-12-16
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介面,
如:
接下來要做的則是構建服務並啟動服務,這裡通過程式碼實現
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) {