应用Jersey2.x建立REST Application
背景:遗留系统升级。遗留系统是利用JSF+JaveEE在IBM Websphere上构筑的Web应用。希望改进客户体验,将JSF替换为现代的前台开发技术例如(React.js 或者 Angular或者Vue等),同时对后台提出使用REST API与前台交互。
大的前提:对于后台的升级,希望只替换Control层的部分,而保留business以及DB层的部分。
大的挑战:如何应用Jersey这种JAXRS开发框架实现改方案?
具体实施方案:
第一部分: 导入Jersey2.33框架。
-
导入Jersey相关包。
jersey-server,jersey-client,jersey-common,jersey-container-servlet
2. 修改部署文件web.xml。修改Servlet配置。如下:
<servlet>
<servlet-name>RestServlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>配置自定义ResourceConfig
</param-value>
</init-param>
<load-on-startup>-1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>RestServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
3. 追加您需要的REST Root resource class。例如如下:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("helloworld")
public class HelloWorldResource {
@GET
@Produces("text/plain")
public String getHello() {
return "Hello World!";
}
}
4. 追加JSON处理。
追加JSON相关依赖包。
jersey-media-json-jackson,jersey-media-json-processing
Jersey使用Jackson来处理JSON的序列化与反序列化。
- 可以根据需要自定义处理日期字段的序列化与反序列化(Jackson默认是使用时间戳表示)
- 可以过滤掉Null的字段
- 处理Map(KeyObject,ValueObject)的POJO的序列化/反序列化
以上的技术实现,我们可以通过自定义Jackson ObjectMapper来实现。
//设定序列化/反序列化时日期字段的显示格式。对于JDK1.8新的LocalTime的设定需要引入类似//jackson-datatype-jsr310来支持
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//过滤掉Null的字段
objectMpper. setSerializationInclusion(JsonInclude.Include.NON_NULL);
更多的ObjectMapper的设定可以参考下以下的官网
https://github.com/FasterXML/jackson
关于处理Map(KeyObject,ValueObject)POJO的序列化/反序列化
a. 针对KeyObject设定自定义序列化/反序列化类。
利用KeyObject.toString()方法,进行KeyObject的序列化。
public class KeyObjectSerializer extends JsonSerializer<PartKey> {
private final ObjectMapper mapper = new ObjectMapper();
@Override
public void serialize(KeyObject value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, value);
gen.writeFieldName(writer.toString());
}
}
自定义反序列化类
public class KeyObjectDeserializer extends KeyDeserializer {
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
// 利用参数Key调用KeyObject的构造函数
return new KeyObject(key);
}
}
b. 在ObjectMapper中注册自定义的序列化/反序列化类。
SimpleModule simpleModule = new SimpleModule();
simpleModule.addKeyDeserializer(KeyObject.class, new KeyObjectDeserializer());
simpleModule.addKeySerializer(KeyObject.class, new KeyObjectSerializer());
objectMapper.registerModule(simpleModule);
5. 追加上传文件功能。
由于使用的Websphere的容器,该容器默认使用Apache CXF实现文件上传。我们很难直接使用Jersey的jersey-media-multipart来实现文件上传。
我们可以利用与Websphere友好的com.ibm.websphere.appserver.api.jaxrs20来实现文件上传。
可以参考如下的文章来实现。
6. 追加统一异常处理。
追加自定义的Provider,来实现统一异常处理。及在REST里抛出自定义异常,Jersey可以返回事先定义好的返回JSON串。
例如:
//自定义异常继承于RuntimeException。在自己的REST类当中可以自己Throw该异常。
public class MyCustomException extends RuntimeException {
…
}
//自定义统一异常处理Provider
@Provider
public class ExceptionMapperClass implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception e) {
if (e instanceof MyCustomException) {
//自定义异常的场合,自定义JSON串,返回
} else {
//自定以异常以外的场合,定义JSON串返回。
}
7. 追加认证功能。
这部分根据选择的认证方式无关,只是利用认证token与Client端交互认证。
具体的实现请参看以下的URL。
第二部分: 清除JSF框架。
由于遗留系统中各个模块(Web,business,Dao等)分别开发且利用web-fragment.xml等情况,需要对各个模块统一去除和测试。
关于利用Jersey2.33在JavaEE(EJB, CDI),Websphere(部署容器)开发Tips总结。
Tips1:可以参考jersey2.33的官方例子代码(https://github.com/eclipse-ee4j/jersey/tree/master/examples),但是,请注意这些例子代码是建立于部署在glassfish应用容器的基础上的。如果你的部署环境是glassfish以外的情况,需要自己测试这些例子的使用。这一点在Jersey官方文档中也有提到。(https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/index.html中 4.8.4. Java EE Servers)
Tips2: 对于部署环境是Websphere的情况下,导入jersey-media-json-jackson的时候需要在自定义的ResourceConfig
类中,显示注册jackson的Provider类----JacksonJsonProvider。
具体请参看以下Blog。
https://openliberty.io/blog/2020/11/11/byo-jackson.html
Tips3: Websphere(Liberty18.x)中,如果在Server.xml中导入feature ‘jaxrs-2.1’的情况,
Websphere对于Jaxrs2.1的默认实现是Apach CXF。通过在ResourceConfig
类中显示注册Jersey相关资源与provider,已指定应用程序使用Jersey的实现。