Jetty扫盲之实践(一)

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) {

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值