背景
某项目接口采用jersey开发,部署在weblogic上,升级JDK至jdk1.7.0_191后所有接口无法访问,返回404 Not Found。
测试
首先怀疑是JDK升级后部分java类初始化失败导致404,但重启服务器,重启应用,重新部署应用都未报错,说明应用启动是成功的,一开始也没怀疑到jersey框架上,因此走了很多弯路,这里就不赘述。到后面所有的猜测都验证后,还是没有解决,于是决定本地用jersey写一个最简单的接口部署上去看下结果。
- 接口部署到线上weblogic访问404
- 接口部署到本地tomcat,访问正常
- 接口部署到其他weblogic环境,访问正常,jdk版本为
jdk1.7.0_80
- 正常的环境将jdk切换至
jdk1.8.0_171
,访问404
基于以上实验可以看出,jersey在weblogic环境下,高版本的jdk会报404,tomcat没有这问题,确定了问题后排查方向就比较清楚,就是搞清楚为什么高版本的jdk会报错。
解决
以下是jersey的web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.xx.poc</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
jersey工作原理很简单,所有的请求(/*)都由统一的servlet(org.glassfish.jersey.servlet.ServletContainer)进行处理,jersey会扫描jersey.config.server.provider.packages
下所有的类,ServletContainer根据url进行分发请求处理。所以首先从ServletContainer入手,通过查看jersey源码找到以下代码(位于org.glassfish.jersey.server.ServerRuntime.process)
final Ref<Endpoint> endpointRef = Refs.emptyRef();
final RequestProcessingContext data = Stages.process(context, requestProcessingRoot, endpointRef);
final Endpoint endpoint = endpointRef.get();
if (endpoint == null) {
// not found
throw new NotFoundException();
}
看来就是在这里导致了404,进一步追踪,以下代码set了一个空,也就是Stages.extractInflector(lastStage)返回了空
inflectorRef.set(Stages.extractInflector(lastStage));
使用arthas进行调试证实了猜想
watch org.glassfish.jersey.process.internal.Stages extractInflector "{returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 69 ms.
ts=2020-11-21 13:14:36; [cost=0.652066ms] result=@ArrayList[
null,
]
原因应该是web.xml中jersey.config.server.provider.packages
不生效,rest相关类没有被扫描到,这里也找到了扫描的代码位于org.glassfish.jersey.server.ResourceConfig.scanClasses
中
final String[] classNames = parsePropertyValue(ServerProperties.PROVIDER_CLASSNAMES);
if (classNames != null) {
for (final String className : classNames) {
try {
result.add(_state.getClassLoader().loadClass(className));
} catch (final ClassNotFoundException e) {
LOGGER.log(Level.CONFIG, LocalizationMessages.UNABLE_TO_LOAD_CLASS(className));
}
}
}
final String[] packageNames = parsePropertyValue(ServerProperties.PROVIDER_PACKAGES);
if (packageNames != null) {
final Object p = getProperty(ServerProperties.PROVIDER_SCANNING_RECURSIVE);
final boolean recursive = p == null || PropertiesHelper.isProperty(p);
rfs.add(new PackageNamesScanner(packageNames, recursive));
}
从代码上看,jersey支持两种定义扫描类方式
- 直接定义类名
- 定义包路径,扫描该路径下所有类
包路径扫描由org.glassfish.jersey.server.internal.scanning.PackageNamesScanner
负责,遗憾的是最终还是未找到未扫描到的原因,因为扫描过程程序启动时就完成了,很难使用arthas进行跟踪调试。但我们也从代码中发现了另外一种方法,就是直接指定rest类。修改web.xml添加ServerProperties.PROVIDER_CLASSNAMES配置,也就是jersey.config.server.provider.classnames
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>com.fuyao.poc.JerseyService</param-value>
</init-param>
重新部署,问题解决
总结
对jersey的印象一直就不大好,兼容性差,依赖多,很容易出问题,这次更是验证了jersey确实不是一个成熟的框架,最终也没找到根本原因算是一个遗憾,另一方面,weblogic的兼容性也是出名的差,很多应用在tomcat上跑的好好的,在weblogic就是有问题,还要依靠各种奇技淫巧解决,比如spring boot。很大一部分原因是weblogic并不像tomcat那样只提供环境,weblogic本身自带另很多库,导致和应用的jar包冲突,所以在部署weblogic应用时,最好在weblogic.xml上加上以下配置,减少冲突。
<?xml version='1.0' encoding='UTF-8'?>
<wls:weblogic-web-app
xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
http://xmlns.oracle.com/weblogic/weblogic-web-app
http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
<wls:container-descriptor>
<wls:prefer-web-inf-classes>true</wls:prefer-web-inf-classes>
<wls:show-archived-real-path-enabled>true</wls:show-archived-real-path-enabled>
</wls:container-descriptor>
<wls:context-root>appContextRoot</wls:context-root>
</wls:weblogic-web-app>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。