本文主要是介绍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.runtimeinto "$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);@Overridepublic 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);@Overridepublic 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接口的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!