jersey+jetty+jdk8实现restful接口

Posted tower888

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jersey+jetty+jdk8实现restful接口相关的知识,希望对你有一定的参考价值。

jersey+jetty+jdk8实现restful接口, 嵌入式, 无需tomcat部署,  比springboot轻量.

支持统一异常处理;

支持Date日期序列化,  响应时自动将对象中的日期字段转为自定义格式的时间格式字符串 返回给客户端

 

 

目录结构:

 

build.gradle:

import org.apache.tools.ant.filters.FixCrLfFilter;
import org.apache.tools.ant.filters.ReplaceTokens;

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'eclipse'

project.group = 'com.soft'
project.archivesBaseName = 'jersey-jetty'
project.version = ''

tasks.withType(JavaCompile)  
    sourceCompatibility = "1.8"
    targetCompatibility = "1.8"
    options.encoding="UTF-8"


javadoc  options.encoding = "UTF-8" 

repositories 
        mavenLocal()
        maven url "http://maven.aliyun.com/nexus/content/groups/public" 


dependencies 
        
        compile 'cn.hutool:hutool-all:5.3.5'
        
        compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.34'
        compile 'org.glassfish.jersey.containers:jersey-container-servlet-core:2.34'
        compile 'org.glassfish.jersey.inject:jersey-hk2:2.34'
        compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.34'
        //compile 'org.eclipse.jetty:jetty-server:9.4.31.v20200723'
        //compile 'org.eclipse.jetty:jetty-servlet:9.4.31.v20200723'
        //compile 'org.eclipse.jetty:jetty-util:9.4.31.v20200723'

        compile 'org.eclipse.jetty:jetty-server:9.4.41.v20210516'
        compile 'org.eclipse.jetty:jetty-servlet:9.4.41.v20210516'
        compile 'org.eclipse.jetty:jetty-util:9.4.41.v20210516'
        
        compile 'org.slf4j:slf4j-api:2.0.0-alpha1'
        runtime 'org.slf4j:jcl-over-slf4j:2.0.0-alpha1'
        runtime 'ch.qos.logback:logback-classic:1.3.0-alpha5'
      
        compile group: 'org.mybatis', name: 'mybatis', version: '3.5.4'
        compile group: 'com.zaxxer', name: 'HikariCP', version: '3.4.5'
        runtime 'com.oracle:ojdbc6:11.2.0.3'
        
        compileOnly 'org.projectlombok:lombok:1.18.12'
        annotationProcessor 'org.projectlombok:lombok:1.18.12'    


task libs(type: Copy) 
    from configurations.runtime
    into "$buildDir/libs"


task makeJar(type:org.gradle.api.tasks.bundling.Jar) 
    from('build/classes/java/main')
    exclude('*.properties', '*.xml','*.setting')
 

processResources()
    filter(ReplaceTokens,tokens: loadEnv());
    filter ReplaceTokens, tokens: [
        "version": version
    ]
    
    filter(FixCrLfFilter,
        eol: FixCrLfFilter.CrLf.newInstance('lf'),
        tab: FixCrLfFilter.AddAsisRemove.newInstance('asis'),
        tablength: 4,
        eof: FixCrLfFilter.AddAsisRemove.newInstance('remove'),
        fixlast: true)


task processShell(type: Copy) 
    description = " copy shell scripts to buildDir/shell"
    from 'src/main/sh'
    into "$buildDir/sh"
    filter(ReplaceTokens,tokens: loadEnv());
    filter(FixCrLfFilter,
        eol: FixCrLfFilter.CrLf.newInstance('lf'),
        tab: FixCrLfFilter.AddAsisRemove.newInstance('asis'),
        tablength: 4,
        eof: FixCrLfFilter.AddAsisRemove.newInstance('remove'),
        fixlast: true)


test 
    systemProperties 'property': 'value'


task deploy(type: Copy) 
    description = ' deploy all the binary and config files to destination(prefix) '
    def destFold=file("$prefix")
    into(destFold)
    from("$buildDir/libs")
        into("lib")
    
    from("$buildDir/sh")
        into("sh")
    
    from("$buildDir/resources/main")
        into("conf")
    
    
    doFirst
        logger.lifecycle("deploy files to : $destFold")
    
    doLast
        logger.lifecycle("deploy success!")
    


task preDeploy()
    description = ' pre actions for task deploy'
    def logsdir=file("$prefix/log")
    if(!logsdir.exists())
        logsdir.mkdir();
    
    def libs=fileTree("$prefix/lib")
    libs.each jar ->
        jar.delete()
    
    def configs=fileTree("$prefix/conf")
    configs.each conffile ->
        conffile.delete()
    



libs.dependsOn(makeJar)
build.dependsOn(['libs','processShell','processResources'])

deploy.dependsOn preDeploy
deploy.dependsOn build

env/config.groovy:

prefix='/output'

environments 
    
    dev 
        sh
            WORK_HOME='/home/xxx/newitf/jersey-jetty';
            PNAME='jersey-jetty';
            LANG='zh_CN.utf8';
            JAVA_HOME='/home/xxx/app/jdk/jdk8';
        
    
    

JettyStart  

package com.soft;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.soft.server.JettyServer;

public class JettyStart 
	private static final Logger log = LoggerFactory.getLogger(JettyStart.class);

	public static void main(String... args) 
		new JettyStart().start();
	

	private JettyServer server;

	public JettyStart() 
		server = new JettyServer();
	

	public void start() 
		try 
			server.start();
			server.join();
		 catch (Exception e) 
			log.error("", e);
		 finally 
			server.destroy();
		
	


JerseyStart  (此类与JettyStart  2个都可以启动服务)

package com.soft;

import java.net.URI;

import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JerseyStart 

	private static final Logger log = LoggerFactory.getLogger(JerseyStart.class);

	public static void main(String[] args) 
		try 
			// uri只有第一个路径段将用作上下文路径,其余部分将被忽略
			JettyHttpContainerFactory.createServer(URI.create("http://localhost:8080/"), new RestApplication());
		 catch (Exception e) 
			log.error("", e);
		
	

 

JettyServer

package com.soft.server;

import com.soft.RestApplication;
import com.soft.config.ServerConf;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JettyServer 
	private static final Logger log = LoggerFactory.getLogger(JettyServer.class);

	private static final int port = ServerConf.VO.getPort();
	private static final String contextPath = ServerConf.VO.getContextPath();
	private static final String resourceBase = ServerConf.VO.getResourceBase();
	private static final int maxThreads = ServerConf.VO.getMaxThreads();
	private static final String ipWhiteList = ServerConf.VO.getIpWhiteList();

	private Server server;

	public JettyServer() 
	

	public void start() throws Exception 
		server = new Server(createThreadPool());
		server.addConnector(createConnector());
		server.setHandler(createHandlers());
		server.setStopAtShutdown(true);
		server.start();
	

	public void join() throws InterruptedException 
		server.join();
	

	public void destroy() 
		server.destroy();
	

	private ThreadPool createThreadPool() 
		QueuedThreadPool threadPool = new QueuedThreadPool();
		threadPool.setName("embed-jetty-http");
		threadPool.setMinThreads(1);
		threadPool.setMaxThreads(maxThreads);
		return threadPool;
	

	private ServerConnector createConnector() 
		ServerConnector connector = new ServerConnector(server);
		connector.setPort(port);
		log.info("App start at port: ", port);
		return connector;
	

	private HandlerCollection createHandlers() 
		ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS);
		handler.setContextPath(contextPath);
		handler.setResourceBase(resourceBase);
		ServletHolder holder = handler.addServlet(ServletContainer.class, "/*");
		holder.setInitOrder(1);

		holder.setInitParameter("javax.ws.rs.Application", RestApplication.class.getName());

		HandlerCollection handlerCollection = new HandlerCollection();
		handlerCollection.setHandlers(new Handler[]handler);
		return handlerCollection;
	

//	private static InetAccessHandler getFireWallHandler() 
//		InetAccessHandler ipHandler = new InetAccessHandler();
//		if (StrUtil.isBlank(ipWhiteList)) // 空,不配置,则不校验
//			return ipHandler;
//		
//
//		String[] ipList = ipWhiteList.split(",");
//		for (String ip : ipList) 
//			ipHandler.include(ip);
//		
//		return ipHandler;
//	


UserController:

package com.soft.controller;

import java.util.Date;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.soft.po.User;
import com.soft.util.Result;

@Path("/abc")
public class UserController 
	private static final Logger log = LoggerFactory.getLogger(UserController.class);

	@GET
	@Path("/hello")
	@Produces(MediaType.TEXT_PLAIN)
	public String hi() 
		return "hello jersey";
	

	@GET
	@Path("/username/age")
	@Produces(MediaType.APPLICATION_JSON)
	public Result findByKey(@PathParam("username") String username, @PathParam("age") int age) 
		log.info("接收到请求" + username);
		User user = new User();
		user.setUsername(username);
		user.setAge(age);
		user.setStatus(20);
		user.setModdate(new Date());
		return Result.ok(user);

	

Result:

package com.soft.util;

import com.soft.exception.ErrorType;

import lombok.Data;

@Data
public class Result 
	private String code;
	private String msg;
	private Object body;

	public Result() 
	

	public Result(ErrorType type) 
		this.code = type.getCode();
		this.msg = type.getMsg();
	

	public Result(String code, String msg) 
		this.code = code;
		this.msg = msg;
	

	public Result(String code, String msg, Object body) 
		this.code = code;
		this.msg = msg;
		this.body = body;
	

	public static Result gen(String code, String msg, Object data) 
		return new Result(code, msg, data);
	

	public static Result gen(ErrorType type, Object data) 
		return new Result(type.getCode(), type.getMsg(), data);
	

	public static Result ok() 
		return gen(ErrorType.Succ, null);
	

	public static Result ok(Object data) 
		return gen(ErrorType.Succ, data);
	

	public static Result fail(String msg) 
		return new Result(ErrorType.Fail.getCode(), msg, null);
	

ErrorType

package com.soft.exception;


public enum ErrorType 
    Succ("0","操作成功"),
    Fail("1","操作失败"),
    ReqParamMissing("0001","请求参数缺少"),
    DataNotExist("1000","数据不存在"),
    DataExist("1001","数据已存在"),
    InvalidQuery("1002","无效的查询"),
    InvalidMod("1003","无效的修改"),
    SysErr("9999","系统错误");

    private String code;
    private String msg;

    ErrorType(String code,String msg) 
        this.code=code;
        this.msg = msg;
    

    public String getCode() 
        return code;
    

    public String getMsg() 
        return msg;
    

ServerConf

package com.soft.config;

import cn.hutool.setting.Setting;

public class ServerConf 
    private static final Setting SETTING = new Setting("server.setting");
    public static final ServerVo VO = SETTING.toBean(ServerVo.class);

    private ServerConf() 
    


ServerVo:

package com.soft.config;

import lombok.Data;

@Data
public class ServerVo 
    private int port;
    private String contextPath;
    private String resourceBase;
	private int maxThreads;
	private String ipWhiteList;// 逗号分隔ip

BUserController 

package com.soft.controller;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.soft.dao.BBroadbandUserDAO;
import com.soft.exception.AppException;
import com.soft.exception.ErrorType;
import com.soft.exception.ReqException;
import com.soft.po.BBroadbandUser;
import com.soft.util.Result;

import cn.hutool.core.util.StrUtil;

@Path("/buser")
public class BUserController 
	@POST
	@Path("/find")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Result findByKey(BBroadbandUser req) 
		String username = req.getUsername();
		if (StrUtil.isBlank(username)) 
			throw new ReqException(ErrorType.ReqParamMissing);
		
		BBroadbandUser po = new BBroadbandUser();
		po.setUsername(username);
		BBroadbandUserDAO udao = new BBroadbandUserDAO();
		BBroadbandUser user = udao.queryByPK(po);
		if (user==null) 
			throw new AppException(ErrorType.DataNotExist);
		
		return Result.ok(user);
	

User:

package com.soft.po;

import java.util.Date;

import lombok.Data;

@Data
public class User 
    private String username;
	private int age;
    private int status;
	private Date moddate;


 

package com.soft.controller;

import com.soft.exception.BaseException;
import com.soft.exception.ErrorType;
import com.soft.util.Result;
import org.eclipse.jetty.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * 异常捕获, 此类必须放在此controller包下,放在其他目录,客户端会抛异常
 */
@Provider
public class ExceptionMappingResource implements ExceptionMapper<Throwable> 

	private static final Logger log = LoggerFactory.getLogger(ExceptionMappingResource.class);

	@Override
	public Response toResponse(Throwable t) 
		Result re = null;
		if (t instanceof BaseException) 
			BaseException e = (BaseException) t;
			re = new Result(e.getCode(), e.getMessage());
			log.info("", re);
		 else 
			re = new Result(ErrorType.Fail.getCode(), t.getMessage());
			log.error("", t);
		
		return Response.status(HttpStatus.INTERNAL_SERVER_ERROR_500).entity(re).build();
	


package com.soft.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;

@Provider
@Produces(MediaType.APPLICATION_JSON)  //必须增加@Produces注解
public class JacksonConf extends JacksonJsonProvider 

	private static final Logger log = LoggerFactory.getLogger(JacksonConf.class);

	@Override
	public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
	                    MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException 
		//log.info("------------date format convert-------------");
		// 将写给客户端的数据中的Date类型字段转换为特定格式
		ObjectMapper mapper = new ObjectMapper();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		mapper.setDateFormat(sdf);

		// 必须设置, 写给客户端的json数据中,Date类型字段才能转为自定义的格式
		mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
		mapper.writeValue(entityStream, value);// 结果写给客户端
	

 

package com.soft;

import com.soft.controller.JacksonConf;
import org.glassfish.jersey.server.ResourceConfig;

import javax.ws.rs.ApplicationPath;

//@ApplicationPath("/abc") //注解无效
public class RestApplication extends ResourceConfig 
	public RestApplication() 
		System.out.println("---------ResourceConfig---------");
		// 服务类所在的包路径
		packages("com.soft.controller");
		register(JacksonConf.class);

		// register(JacksonJsonProvider.class);
//		register(ValidationFeature.class);
	

 

server.setting:

port=8080
contextPath=/
resourceBase=@sh.WORK_HOME@/conf
maxThreads=30
ipWhiteList=

 

说明:

- ExceptionMappingResource必须放在controller目录下,在其他目录,controller代码中抛出自定义异常时,会导致客户端抛异常
- 使用JerseyStart,jetty-server:9.4.40.v20210413版本 启动时, JacksonConf类必须增加注解@Produces(MediaType.APPLICATION_JSON),才能Date日期序列化生效
- 使用JettyStart,jetty-server:9.4.31.v20200723版本 启动时, JacksonConf类无需增加注解@Produces(MediaType.APPLICATION_JSON),也能Date日期序列化生效
- 必须使用jetty 9.4.31.v20200723版本 ,RestApplication配置的Date类型序列化才会成功;
  稍高版本  jetty 9.4.41.v20210516 也无效,无法转换Date格式.
  解决: 这是因为未增加注解@Produces(MediaType.APPLICATION_JSON), 增加后可使用jetty9高版本.

以上是关于jersey+jetty+jdk8实现restful接口的主要内容,如果未能解决你的问题,请参考以下文章

jersey+jetty+jdk8实现restful接口

jersey+jetty实现文件上传

将 Jetty 与 JAX-RS-Jersey 集成

带有 XML 参数的 REST 服务操作上的 HTTP 错误 415 不受支持的媒体类型(Jersey + Jetty)

idea+Spring+Mybatis+jersey+jetty构建一个简单的web项目

如何在 JDK8 上使用 SPDY 运行 Jetty?