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?