Why?
技术变革的车轮不断向前,伴随着k8s、docker等新技术的落地,陈旧的技术栈,千疮百孔的工程,已经逐步成为技术负债的黑洞。秉承着“前人挖坑后人骂,后人填坑苦哈哈”的精神,我来吧,苦哈哈。
What?
-
Jersey2.x 多Servlet-Mapping
Spring 3 映射Mappings,基于web.xml 映射Servlet,如以下代码示例,目的是为了实现接口的默认前缀并控制接口版本,以映射不同的鉴权版本、拦截器等
<servlet>
<servlet-name>Jersey2</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey2</servlet-name>
<url-pattern>/rest/v1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Jersey2</servlet-name>
<url-pattern>/rest/v2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Jersey2</servlet-name>
<url-pattern>/rest/v3</url-pattern>
</servlet-mapping>
基于springboot想实现接口的默认前缀,存在以下几种方式
每一个接口添加前缀 或多个接口实现(当我没说)- @ApplicationPath
然而无法实现多个path共存的想法,因此貌似走到了怪圈里。。走了好久好久好久。。
直到发现了以下的文档:
Springboot 中Jersey 的multiple Application实现
核心思想:即对Servlet扫描规则进行了拓展,完美的解决了以上问题。
-
SpringBoot Jar包运行,提示FileNotFound
原本的配置是这个样子的,基于IDE调试的过程中一直很顺利,直到基于jar部署的时候,凉凉了
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
//扫描Jersey实例所在包
packages("com.xx.xx");
// 注册JacksonFeature
register(JacksonFeature.class);
}
}
貌似又走到了怪圈里。。走了好久好久好久。。
直到发现了以下的文档:
jersey在 spring boot 添加 packages 扫描路径支持
核心思想:springboot打包成jar之后,jersey packages扫描不到相关类,故使用Spring的扫描机制进行处理
此处发现可以优化的点,之前是进行包下的所有类扫描并注册,之后启动后会提示一些警告,故可以在之中判断class是有@Path的引用,再进行注册
-
多Application实例,注册不成功
原以为逃离了怪圈。。好远好远好远。。基于Jar部署后,启动成功了,然而调用接口后,404!!
又又又凉凉凉了。。
貌似又又走到了怪圈里。。走了好久好久。。
之后毅然决然的查看了Application的相关源码,发现了事情的真相。
public class Application {
public Set<Class<?>> getClasses() {
return Collections.emptySet();
}
...省略
}
getClasses() 方法即为基于以上实例扫描到的Jersey实例,通过debug发现以下为空,瞬间懂了,将上文扫描到的classes,返回即可。
希望这回真的逃离了怪圈。。好远好远好远好远。。
-
@ Produces 不指定类型或者干脆没有这个注解
基于以上的一通儿操作,我以为我今天一定可以霸屏"填坑走多远排行版"第一把交椅,然而又又又又TM的出现了,已经一个正常的请求,后台提示 text/html 没有解析器。
!!!这是一个应该响应成JSON的请求!!!
貌似又又又又又又走到了怪圈里。。有点走走走走走走不动了。。
冷静了许久。。想到了两个解决方案:
全局搜索,然后一个一个补全,之后打死哪些不写全代码的人- 配置默认的响应MediaType
最终在Jersey包下找到了以下类ContainerResponseFilter,大体的源码如下
package javax.ws.rs.container;
import java.io.IOException;
public interface ContainerResponseFilter {
/**
* Filter method called after a response has been provided for a request
* (either by a {@link ContainerRequestFilter request filter} or by a
* matched resource method.
* <p>
* Filters in the filter chain are ordered according to their {@code javax.annotation.Priority}
* class-level annotation value.
* </p>
*
* @param requestContext request context.
* @param responseContext response context.
* @throws IOException if an I/O exception occurs.
*/
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException;
}
关键的一段英文在下面
Filter method called after a response has been provided for a request
!!!在请求的响应返回时触发 !!!
果断实现接口,对于代码做容错,大体逻辑如下:
没有@Produces 响应默认为MediaType.APPLICATION_JSON
有@Produces 但是没有值,则一样变成MediaType.APPLICATION_JSON