背景

上一篇讲了我们从另外一个部门迁移了一个线上系统回来,迁回来是为啥呢,因为这个好几年没新需求的系统,突然有新需求要开发,然后我就开发呗,其实就是在某个服务里加点表,然后提供个查询接口给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空指针异常的解决过程的更多相关文章

  1. 一个Java程序的执行过程(转)

    我们手工执行java程序是这样的:  1.在记事本中或者是UE的文本编辑器中,写好源程序:  2.使用javac命令把源程序编译成.class文件:    编译后的.class(类字节码)文件中会包含 ...

  2. 一个 java 文件的执行过程详解

    平时我们都使用 idea.eclipse 等软件来编写代码,在编写完之后直接点击运行就可以启动程序了,那么这个过程是怎么样的? 总体过程 我们编写的 java 文件在由编译器编译后会生成对应的 cla ...

  3. 一个疑难bug的解决过程

    一个crontab脚本,下载一个文件并把内容入mysql数据库.具体流程如下: 1, wget一个文件. 2,处理文件生成一个中间文件. 3,将中间文件load入库. 05 10 * * * /hom ...

  4. 记录一个前端bug的解决过程

    人在江湖飘,哪能不挨刀. 我挨了重重一bug.严格来讲这可能是我职业生涯以来的首个悲惨经历,因为凭我的知识储备和经验,基本上任何可重现的bug都是可解的.然而这个bug却困扰了我三个月之久,它具有以下 ...

  5. java.lang.UnsatisfiedLinkError解决方法汇集(转载)

    我的解决方法: 将sigar.jar拷贝到/WEB-INF/lib/下,但这个方法不知道是不是终极解决办法,暂时没问题,其他方法可参考下面. 运行JSP报表程序页面出现java.lang.Unsati ...

  6. 在Eclipse中,如何把一个java项目变成web项目

    经常在eclipse中导入web项目时,出现转不了项目类型的问题,导入后就是一个java项目.解决步骤:1.进入项目目录,可看到.project文件,打开.2.找到<natures>... ...

  7. 深入理解Java对象的创建过程:类的初始化与实例化

    摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类 ...

  8. (转)深入理解Java对象的创建过程

    参考来源:http://blog.csdn.net/justloveyou_/article/details/72466416 摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一 ...

  9. 深入学习Java对象创建的过程:类的初始化与实例化

    在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完 ...

  10. 从解决一个java.lang.NoSuchMethodError想到的

    今天在发布系统部署一个web app的时候,发现应用服务器(tomcat 7.0.26)不能正常启动,于是远程登陆到服务器上查看应用服务器的启动日志,在tomcat_home的logs/localho ...

随机推荐

  1. AutoFac(三)——装配扫描(批量注册之扫描类型)

    一.装配扫描 Autofac允许通过常规组装的方式去注册组件(构造方法.实例.lambda表达式等),您可以扫描和注册单个类型,也可以具体的扫描Autofac模块去注册. 1.扫描类型 除了已知的的常 ...

  2. 设计即合规: 开放AI生态中的用户数据治理实践

    Hugging Face Hub 已成为 AI 协作的核心平台,托管了数万个模型.数据集以及交互式应用程序 (Space). 在开放生态系统中,用户知情同意的管理方式与那些更 "数据饥渴&q ...

  3. 【经验】C++通过文件句柄获取文件路径(两种方式)

    方法一 主要参考博客:根据文件句柄获得文件名(这篇超级清晰,就是有一点点小问题) 通过文件句柄获得文件路径(这篇不是很好用,但是思路大体是一致的) 主要思路: 根据HANDLE 创建映射文件,调用Ge ...

  4. WPF后台自动添加控件Demo

    xaml <Window x:Class="EBPlugIn2.EBPlugIn2_YJW_13" xmlns="http://schemas.microsoft. ...

  5. 腾讯IMA VS 飞书知识问答:谁才是2025最强AI知识库?

    AI创业失败,可私聊经验教训分享... 前几天小伙伴在讨论我开发的一套社群运营AI分身,其本质其实是一套个人知识库的AI产品,其依赖的就是我过往发布的文章. 这类AI聊天分身,最简单.不考虑" ...

  6. VSCode将本地项目代码上传到gitee中

    1.创建远程仓库,这个就是该仓库的地址   2.查看git的版本 git --version 3.使用git init命令初始化git 4.使用git status命令来查看文件是否被修改  : gi ...

  7. PHP代码学习

    在php传参过程中,如果服务器运行的Linux环境,可以加入命令的执行,比如参数名是a,命令可以是:a=ls,通过用:,&&,|| 来分割,还可以同时执行多个命令.在Windows环境 ...

  8. Django Web应用开发实战第二章

    一.基本配置信息 """ Django settings for myblog project. Generated by 'django-admin startproj ...

  9. Ubuntu部署tensorflow(CPU/GPU)方法

      本文介绍在Linux操作系统的发行版本Ubuntu中,配置可以用CPU或GPU运行的Python新版本深度学习库tensorflow的方法.   在文章部署CPU与GPU通用的tensorflow ...

  10. 【2020.11.19提高组模拟】二次剩余two 题解

    [2020.11.19提高组模拟]二次剩余two 题解 题目描述 有\(n\)个二次函数,每个二次函数可以用两个值\(m,k\)描述: \[f(x)=(x-m)^2+k \] 现在有\(q\)次操作: ...