场景:项目框架基于springboot,里用到了shiro.用了一个shell脚本来启动,该脚本大致逻辑就是先从git拉最新代码,然后maven打包,然后将打好的包拷贝到指定位置,然后kill项目的pid,最后启动。看起来没啥问题,但是每次调用该脚本的时候都会报一个异常:java.lang.NoClassDefFoundError: org/apache/shiro/util/LifecycleUtils。导致每次该脚本都得调用两次才能发版成功,很烦人。
经过排查,发现如果不调用脚本,直接先kill再start就没有问题,就怀疑是不是因为先cp了一下,导致类被替换了,然后再kill的时候,shiro会调用SessionsSecurityManager的destory方法,该方法又会调用LifecycleUtils的destory方法,而该类已经被换了,所以报NoClassDefFoundError异常。
修改了一下脚本,先kill,再cp,最后再start,果然好了。
进一步猜测,jvm在加载类的时候,是一个“懒加载”的机制,只有在第一次用到的时候才会加载,因为如果不是的话,那么LifecycleUtils应该已经被加载到内存中,不管class文件有没有变化,都不会报错。