一个java空指针异常的解决过程
背景
上一篇讲了我们从另外一个部门迁移了一个线上系统回来,迁回来是为啥呢,因为这个好几年没新需求的系统,突然有新需求要开发,然后我就开发呗,其实就是在某个服务里加点表,然后提供个查询接口给app。这个服务用的架构是厂商的,不是servlet容器那一套,它技术还是很厉害,其实是c语言写了个reactor这种类似netty的通信框架,通信协议是私有的tcp协议,然后启动时还通过jni拉起了一个java虚拟机,通信框架就负责底层通信,并且像servlet那样来调用上层的java类中的service方法这样。
至于为啥厂商这么玩,我觉得无非是把底层这套通信框架打成二进制提供给我们,提高技术壁垒,防破解啥的,并且通过私有协议提升维护难度,这样的话,就可以一直收我们维护费了。这些就不过多吐槽了,反正好多服务就是这样被厂商绑死了。
在windows上,这个进程没法像tomcat这类容器一样运行起来,每次只能对java代码部分进行junit单元测试,只能在linux上才能运行起来。
像java代码部分,我们自己是用了spring那一套,就像下面这样,启动时直接new一个spring的ClassPathXmlApplicationContext,而下面的location就是application.xml这种spring配置文件的url。

由于厂商也提供了些自己的访问数据库的框架,之前这个服务就是用的厂商这套(类似于jdbcTemplate操作sql语句);我接手这个服务之后,感觉不太喜欢厂商那套,就还是引入mybatis的mapper这套,如下:

结果,我本地junit调试时好好的,丢到服务器上,直接运行不起来了。给我报了个空指针。

解决过程
各种怀疑
上面的图里,看着是mybatis在创建一个什么XPathFactory的时候报错了,然后上图又说解析什么mapper.xml失败了,我还在想,是文件路径没对吗,结果文件路径挺对的:

接下来,开始怀疑起了mybatis,由于接手的这个项目,素质不怎么样,各种jar包冲突啥的,大家看我下图就知道了,就上面报错的堆栈里的org.apache.ibatis.parsing.XPathParser这个类,我在项目里一搜,发现同名类有两个,为啥呢,因为下面第一个jar包是厂商的,它把mybatis的源码封进了自己的jar包里,且没改包名:

当然了,厂商那个jar包里不止这一个,拷贝了很多类进来,也不知道拷贝进来后改了些啥。
我这时候的想法是,看看到底加载的哪个class吧。
查看类加载情况
然后在jvm的启动参数加了打印类加载的jvm参数: -verbose:class

注意,这个是打印到console,或者对console进行重定向到文件。
我这边看了下,发现是对的,仔细检查了,没发现加载了厂商的jar包的类,看起来是对的:

看堆栈相关代码
然后开始在本地junit调试,结果没走到报错的地方。
看之前那个错误堆栈,报错就是在下面的jdk1.7中的XPathFactoryFinder类的220这一行:

然后,这个resource应该就是触发空指针的,如果说resource为null,那么217行这个迭代器就有问题,通过迭代器的next获取出来的值为null;而迭代器是在215行的createServiceFileIterator赋值的。

点进来后就傻了,有分支,不知道走了哪一条,好在天无绝人之路,此时,之前的-verbose:class参数起了作用:

通过这里分析,在加载完XPathFactoryFinder类后,不久就加载了javax.xml.xpath.XPathFactoryFinder.SingleIterator,那意思就是走了if那个分支。
但目前对怎么解决这个问题还是不清楚,无奈,先本地debug。
debug
虽然本地junit不能完全模拟linux服务器上的状况,但是先试试吧,结果,本地调试发现:
在之前那个分支处,本地windows时,classloader不是null:

那我是否怀疑了classloader的问题呢,怀疑的不多,因为以前本地调试tomcat的时候已经遇到过,本地debug时用的classloader和最终把war包丢到tomcat里运行时用的classloader那些,确实不一样。
在debug的过程中,对这个类的理解加深了一些,看起来,这个XPathFactoryFinder主要就是要获取到一个XPathFactory。

先是从property中查找,property为javax.xml.xpath.XPathFactory:http://java.sun.com/jaxp/xpath/dom,如果能找到对应的实现类,就用这个实现类去创建对应的XPathFactory,这块就和java中的SPI机制类似:

接下来呢,会尝试从配置文件获取C:\Program Files\Java\jdk1.7.0_80\jre\lib\jaxp.properties:

如果还是没有,才会走到我们报错的地方,即用spi机制,问题是我们因为classloader为null,走不到下图这里:

开启debug日志
在debug过程中,发现很多debug日志:

这个日志如何开启呢?

我们设置了下,如下:

这下,通过日志,我们更加明确了程序运行的轨迹。
设置property解决bug
接下来,就是怎么解决了,在debug过程中,我们发现,本地windows的话,默认最终的实现类就是com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl,其实就是jdk自带的:


那我们这里也就好说了,设置下property吧,解决问题就行了,至于为啥classloader为null,就没有继续深究了:

这次就运行正常了:

总结
bug记录下还是挺好,因为很快就忘了,后来要写上线变更文档,才想起来这么个事。
一个java空指针异常的解决过程的更多相关文章
- 一个Java程序的执行过程(转)
我们手工执行java程序是这样的: 1.在记事本中或者是UE的文本编辑器中,写好源程序: 2.使用javac命令把源程序编译成.class文件: 编译后的.class(类字节码)文件中会包含 ...
- 一个 java 文件的执行过程详解
平时我们都使用 idea.eclipse 等软件来编写代码,在编写完之后直接点击运行就可以启动程序了,那么这个过程是怎么样的? 总体过程 我们编写的 java 文件在由编译器编译后会生成对应的 cla ...
- 一个疑难bug的解决过程
一个crontab脚本,下载一个文件并把内容入mysql数据库.具体流程如下: 1, wget一个文件. 2,处理文件生成一个中间文件. 3,将中间文件load入库. 05 10 * * * /hom ...
- 记录一个前端bug的解决过程
人在江湖飘,哪能不挨刀. 我挨了重重一bug.严格来讲这可能是我职业生涯以来的首个悲惨经历,因为凭我的知识储备和经验,基本上任何可重现的bug都是可解的.然而这个bug却困扰了我三个月之久,它具有以下 ...
- java.lang.UnsatisfiedLinkError解决方法汇集(转载)
我的解决方法: 将sigar.jar拷贝到/WEB-INF/lib/下,但这个方法不知道是不是终极解决办法,暂时没问题,其他方法可参考下面. 运行JSP报表程序页面出现java.lang.Unsati ...
- 在Eclipse中,如何把一个java项目变成web项目
经常在eclipse中导入web项目时,出现转不了项目类型的问题,导入后就是一个java项目.解决步骤:1.进入项目目录,可看到.project文件,打开.2.找到<natures>... ...
- 深入理解Java对象的创建过程:类的初始化与实例化
摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类 ...
- (转)深入理解Java对象的创建过程
参考来源:http://blog.csdn.net/justloveyou_/article/details/72466416 摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一 ...
- 深入学习Java对象创建的过程:类的初始化与实例化
在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完 ...
- 从解决一个java.lang.NoSuchMethodError想到的
今天在发布系统部署一个web app的时候,发现应用服务器(tomcat 7.0.26)不能正常启动,于是远程登陆到服务器上查看应用服务器的启动日志,在tomcat_home的logs/localho ...
随机推荐
- AutoFac(三)——装配扫描(批量注册之扫描类型)
一.装配扫描 Autofac允许通过常规组装的方式去注册组件(构造方法.实例.lambda表达式等),您可以扫描和注册单个类型,也可以具体的扫描Autofac模块去注册. 1.扫描类型 除了已知的的常 ...
- 设计即合规: 开放AI生态中的用户数据治理实践
Hugging Face Hub 已成为 AI 协作的核心平台,托管了数万个模型.数据集以及交互式应用程序 (Space). 在开放生态系统中,用户知情同意的管理方式与那些更 "数据饥渴&q ...
- 【经验】C++通过文件句柄获取文件路径(两种方式)
方法一 主要参考博客:根据文件句柄获得文件名(这篇超级清晰,就是有一点点小问题) 通过文件句柄获得文件路径(这篇不是很好用,但是思路大体是一致的) 主要思路: 根据HANDLE 创建映射文件,调用Ge ...
- WPF后台自动添加控件Demo
xaml <Window x:Class="EBPlugIn2.EBPlugIn2_YJW_13" xmlns="http://schemas.microsoft. ...
- 腾讯IMA VS 飞书知识问答:谁才是2025最强AI知识库?
AI创业失败,可私聊经验教训分享... 前几天小伙伴在讨论我开发的一套社群运营AI分身,其本质其实是一套个人知识库的AI产品,其依赖的就是我过往发布的文章. 这类AI聊天分身,最简单.不考虑" ...
- VSCode将本地项目代码上传到gitee中
1.创建远程仓库,这个就是该仓库的地址 2.查看git的版本 git --version 3.使用git init命令初始化git 4.使用git status命令来查看文件是否被修改 : gi ...
- PHP代码学习
在php传参过程中,如果服务器运行的Linux环境,可以加入命令的执行,比如参数名是a,命令可以是:a=ls,通过用:,&&,|| 来分割,还可以同时执行多个命令.在Windows环境 ...
- Django Web应用开发实战第二章
一.基本配置信息 """ Django settings for myblog project. Generated by 'django-admin startproj ...
- Ubuntu部署tensorflow(CPU/GPU)方法
本文介绍在Linux操作系统的发行版本Ubuntu中,配置可以用CPU或GPU运行的Python新版本深度学习库tensorflow的方法. 在文章部署CPU与GPU通用的tensorflow ...
- 【2020.11.19提高组模拟】二次剩余two 题解
[2020.11.19提高组模拟]二次剩余two 题解 题目描述 有\(n\)个二次函数,每个二次函数可以用两个值\(m,k\)描述: \[f(x)=(x-m)^2+k \] 现在有\(q\)次操作: ...