一次Spring项目打包问题排查的实战记录

一个 Spring 项目,打成 jar 包之后运行,在有网络的时候是正常的,但是一旦无网络就会报错,具体是怎么回事呢?这篇文章就来记录下这次问题排查经过。

背景介绍

一个图形化的界面,带本地数据库,要求可以在无网络环境下运行,我帮朋友用的 Java 写的图形化界面,虽然不是很美观,但是胜在熟悉 Java。

项目使用的是 idea 的「Build Artifacts」打包,打包之后运行正常,界面和数据库访问都正常,最开始报过几次错,后来就没出现了,也没找到原因,就先那样了。

后来发给别人了,完全打不开,跟之前的报错一样,看来必须要搞清楚报错的原因了。

问题排查

报错内容

首先贴一下报错的信息:

org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 8 in XML document from class path resource [spring.xml] is invalid;
nested exception is org.xml.sax.SAXParseException;
lineNumber: 8; columnNumber: 76;
cvc-elt.1: 找不到元素 'beans' 的声明。

spring.xml 代码截取如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
</beans>

问题复现

因为我之前也碰到过这个报错,所以想着复现一下问题,方便排查。

我直接在虚拟机中运行,无论是覆盖安装还是卸载再次安装都没问题,我就想可能是环境问题导致的吧,就想着装一个新的虚拟机试一下。

新系统装完之后,一运行,果然崩溃了,报错信息一样。能复现问题,可以说为解决问题开了个好头。

问题排查

找到原因

通过关键字 cvc-elt.1: 找不到元素 'beans' 的声明 开始搜索,搜索到的结果基本都是以下的内容。

配置文件头部配置的 xsd 版本信息不正确,造成解析时出错。spring 头部 xsd 或 dtd 校验文件的查找分两步,第一先从本地 jar 包中找,如果找到则用本地jar包的进行校验(可以在 spring-beans.jar 或 spring-context.jar 里的 META-INF 下的 spring.schemas 文件中找到 xsd 文件位置的定义),如果没有找到则进行第二步查找,它会尝试从网络中下载该文件然后校验,如果系统断网或下载不下来,则会抛出上述异常。

照着这个思路,朋友也提醒我,看下虚拟机的系统是否联网了,我一看,果然没联网,赶紧联网测试发现能正常运行,问题的原因找到了。

再次困惑

找到了问题原因,好像就马上就能解决了,但事实并不是我想的那样。

网上的解决办法是:

将 applicationContext.xml 中 xsd 文件定义的版本改为 spring jar 包中定义的 xsd 的版本,如果版本定义的太高在本地会无法找到,只能从网络上下载。

但是,查看 spring-beans.jar 里 META-INF 下的 spring.schemas 文件,发现无论版本号是多少,最后指向的都是一个。

截取了部分代码如下:

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.3.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd

后来又搜到一篇文章「spring5之SAXParseException:cvc-elt.1: 找不到元素 “beans” 的声明[1]」,说 Spring 5 之后就不需要写版本号了。

那跟我看到的情况是一样的,确实各个版本号指向的都是同一个,看来不是版本号的问题,那到底是怎么回事?

一波三折

既然 xsd 的版本没错,那问题出在哪里呢?

继续搜索之后,看到了这篇文章「系统启动时,spring配置文件解析失败,报”cvc-elt.1: 找不到元素 'beans' 的声明“异常[2]」。

打包之后,spring.schemas 位于 jar 包的 META-INF 目录下,但是 spring.schemas 里并没有 spring-beans.xsd,看来问题是在这了,我仿照作者的方法,把上面提到的 spring-beans.jar 里 META-INF 下的 spring.schemas 里的关于 spring-beans.xsd 的部分添加到了压缩包的 spring.schemas 里。

重新打包之后仍然报错,但是报的错不是之前的错了,是说我的主函数类找不到了,真的是让人摸不着头脑,情况类似啊,为什么我的不好使。

豁然开朗

还是上面的操作,我让朋友试了一下,他测试之后居然好使!

我问他怎么操作的,他说跟我的操作一样,只不过把 https 部分的也加上了,我心想不应该啊,也没引用 https 的 spring-beans.xsd,不应该是这个问题吧。

我按照他的操作,在我电脑上重新打包,发现还是不好使;我又把他给我的文件替换到我打的包里,还是不好使。真的是蒙了,操作是一样的,为什么我的不好使。

后来朋友说让我把修改好的包发给他,他看了之后发现我打的包大小不一样,正常应该是 14MB,但是我的包是 500KB,我又重新操作了一遍,发现我把文件替换之后,整个 jar 包的大小变小了,原来是我的压缩包软件有问题。

朋友的电脑是 Windows,是直接用 WinRAR 打开 jar 包,然后把文件替换;我的是 Mac,用的 eZip 软件打开 jar 包,然后替换文件。最后,我使用虚拟机按照朋友的操作测试后发现 ok 了。

谁能料到是压缩软件的问题呢?但是也不能全怪软件,毕竟人家是解压缩软件,并不是专门处理 jar 文件的。

完美解决

虽然找到了问题原因,也有了解决办法,但是解决的办法却不够好,总不能每次打好包再手动替换文件吧?

我觉得既然我们能遇到的问题,肯定也有其他人遇到,我就使用关键字 spring.schemas 打包之后没有把 beans 打进去 进行了搜索,发现了完美的解决方案:「spring打包到jar问题[3]」。

问题的根本原因是,由于 spring 多个 jar 包都包含 spring.schemas,「Build Artifacts」和「Maven」默认打包只会把第一次遇到的 schemas 文件打入 jar 包。

解决办法是使用 maven shade 打包,具体的代码如下:

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-shade-plugin</artifactId>
 <version>3.2.2</version>
 <executions>
 <execution>
 <phase>package</phase>
 <goals>
 <goal>shade</goal>
 </goals>
 <configuration>
 <transformers>
 <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
 <mainClass>xxx.xxx.xxx.App</mainClass>
 </transformer>
 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
 <resource>META-INF/spring.handlers</resource>
 </transformer>
 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
 <resource>META-INF/spring.schemas</resource>
 </transformer>
 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
 <resource>META-INF/spring.tooling</resource>
 </transformer>
 </transformers>
 </configuration>
 </execution>
 </executions>
</plugin>

将代码放在项目 pom.xml 的 <plugins> 节点里,其中代码里的 <mainClass> 节点里要写主函数的完整目录。

感受

问题解决了,从中也得到一些感受:

•遇到问题还是得尽快解决,因为问题是不会放过你的。

•Mac 下的压缩软件暂时没有 Windows 下的好使。

•网上的答案真的是千篇一律,都是一个人的答案被很多人复制,而且没有讲清楚问题。

•搜到的千篇一律的答案,可能已经过时了,并不适合自己的情况。

•自己遇到的问题,绝大部分都是别人遇到过的,是可以解决的,耐心搜索就会找到解决办法。

引用链接

[1] spring5之SAXParseException:cvc-elt.1: 找不到元素 “beans” 的声明

[2] 系统启动时,spring配置文件解析失败,报”cvc-elt.1: 找不到元素 'beans' 的声明“异常

[3] spring打包到jar问题

总结

到此这篇关于一次Spring项目打包问题排查的文章就介绍到这了,更多相关Spring项目打包问题排查内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 解决idea打包成功但是resource下的文件没有成功的问题

    如下所示: 1 需要在project structure中的Artifacts下的项目classes文件夹下添加Directory Content,地址指向resource文件夹. 2 因为 resource(s)目录也是classpath啊,打包之后和src目录一样,里面的文件都会放在classes目录下面. 补充知识:IDEA打jar包没有将resource打包解决方法 在pom文件中添加配置 <build> <resources> <resource> <d

  • 将idea工程打包成jar文件的全步骤

    前言 近日在工作中遇到了一个问题,需要把本地的java文件打成jar包,传到云服务器上运行.于是学习了一下如何在intellijidea中将java工程打成jar包. 步骤如下: *File->PorjectStructure *在ProjectSetings中选择Artifects.点击+号选择jar文件中的Empty选项 *在Name选项中修改jar文件的名字.*然后点击下方的CreatManifest按钮,点击ok(应该默认是当前工程) * 点击Main Class右侧的文件夹图标,选择当

  • 成功解决IDEA2020 Plugins 连不上、打不开的方法

    IntelliJ IDEA 2020.1 插件中心一直打不开,鉴于有部分同学反馈设置http proxy不能解决,所以可按以下顺序检查 一.设置 http proxy->勾上Auto-detect proxy setting,参照下图,加上地址 http://127.0.0.1:1080 或者 选择 File->Setting->Appearance&Behavior->System Settings->HTTP Proxy 进行配置,地址除了用http://127.

  • 一次Spring项目打包问题排查的实战记录

    一个 Spring 项目,打成 jar 包之后运行,在有网络的时候是正常的,但是一旦无网络就会报错,具体是怎么回事呢?这篇文章就来记录下这次问题排查经过. 背景介绍 一个图形化的界面,带本地数据库,要求可以在无网络环境下运行,我帮朋友用的 Java 写的图形化界面,虽然不是很美观,但是胜在熟悉 Java. 项目使用的是 idea 的「Build Artifacts」打包,打包之后运行正常,界面和数据库访问都正常,最开始报过几次错,后来就没出现了,也没找到原因,就先那样了. 后来发给别人了,完全打

  • vue项目打包优化的方法实战记录

    目录 1.按需加载第三方库 2.移除console.log 3. Close SourceMap 4. Externals && CDN 5.路由懒加载的方式 总结 1.按需加载第三方库 例如 ElementUI.lodash 等 a, 装包 npm install babel-plugin-component -D b, babel.config.js module.exports = { "presets": [ "@vue/cli-plugin-babe

  • node vue项目开发之前后端分离实战记录

    前言 本文主要介绍了关于node vue前后端分离的相关资料,分享出来供大家参考学习,下面随着小编来一起学习学习吧. node vue项目开发 最近看了近一周的vue开发,有诸多感触,我之前接触过react.angular所以特别想学学久仰大名的vue.学习半天以后发现,接触到的东西多了,学习起来就是容易很多,vue的指令我能个联想到angular的指令,vue组件化设计类似于react的组件化设计,包括一些router的设置跟react里的路由或者nodejs里的路由都差不多,vuex更是根据

  • 一次NodeJS内存泄漏排查的实战记录

    目录 前言 案例一 故障现象 排查过程 案例二 故障现象 排查过程 问题原因 node-v9.x 以下的版本 node-v10.x 以上的版本 修复泄露 总结 前言 性能问题(内存.CPU 飙升导致服务重启.异常)排查一直是 Node.js 服务端开发的难点,去年在经过调研后,在我们项目的 Node.js 服务上都接入了 Easy-Monitor 来帮助排查生产环境遇到的性能问题.前段时间遇到了两例内存泄漏的案例,在这里做一个排查经过的整理. 案例一 故障现象 线上的某个服务发生了重启,经过观察

  • Vue-cli3项目配置Vue.config.js实战记录

    Vue-cli3 搭建的项目 界面想对之前较为简洁 之前的build和config文件夹不见了,那么应该如何配置 如webpack等的配那只需要在项目的根目录下vue.config.js 文件(是根目录,不是src目录 语法 module.exports = { // 基本路径 baseUrl: '/', // 输出文件目录 outputDir: 'dist', // eslint-loader 是否在保存的时候检查 lintOnSave: true, // use the full build

  • 一次关于Redis内存诡异增长的排查过程实战记录

    一.现象 实例名:r-bp1cxxxxxxxxxd04(主从) 问题:一分钟内存上涨了2G,如下图所示: 键值规模:6000万左右 内存一分钟增长2G.png 二.Redis内存分析 1. 内存组成 上图中的内存统计的是Redis的info memory命令中的used_memory属性,例如: redis>infomemory#Memoryused_memory:9195978072used_memory_human:8.56Gused_memory_rss:9358786560used_me

  • 一次Jvm old过高的排查过程实战记录

    前言 最近遇到一个Jvm old过高的案例,现象是一个站点的jvm old区过高,分析原因是,原来的设计方案有问题,给前端返回的数据里面包含了大量的html代码,从存储中拿数据的过程.拼接数据的过程过于漫长了,造成了大量对象的生命周期过长,对象被 标记到了old中,造成了old区过高,监控系统进行了报警,详细原因就不做详细分析了,主要分享一下问题排查的过程. 收到了监控系统的报警,在服务器上查询jvm内存情况 jstat -gcutil pid 时间间隔,可以按时间间隔打印jvm的内存情况,例如

  • 一次现场mysql重复记录数据的排查处理实战记录

    目录 前言 分析 数据总计 重复次数占比 where 和 having 的区别 总结 前言 我当时正好出差在客户现场部署调试软件,有一天客户突然找到我这里,说他们现场生产的数据出现了异常的情况,最直接的表现就是 同一个标签,出现在了多个物料上,需要我配合,看怎么排查问题 分析 客户当时直接一摞重复标签的盒子码在我面前,我慌得一匹,这怕不是捅娄子了 稍加思索,现在需要做的就是,在数据库中查询出重复的标签,即对一个标签进行统计,判断出计数> 1 的即可 emmm,语法错误,我记得还有个Having

  • 一次线上websocket返回400问题排查的实战记录

    目录 现象 抓包排查 问题定位 解决方案1 解决方案2 原因探讨 总结 现象 生产环境websocket无法正常连接,服务端返回400 bad request,开发及测试环境均正常. 抓包排查 src:nginx服务器 172.16.177.193 dst:imp应用服务器 172.16.177.218 问题定位 观察到header中的host值带有下划线,在一些中间件(如kafka.hadoop)中,对host中的特殊字符也有限制.由此猜测是header问题. 经排查,此header来自ngi

  • Oracle数据库丢失表排查思路实战记录

    目录 说明: 写在最后: 总结 说明: 由于系统采用ID取模分表法进行Oracle数据存储,某日发现Oracle数据库中缺少对应的几张业务数据表,遂进行相关问题查询,简单记录一下排查思路: 由于我们代码中实现思路是判断如果没有对应的表会自动创建,所以首先需要查询一下缺失数据库表的创建时间 SELECT * FROM dba_objects where OBJECT_NAME LIKE 'LOG_5%' AND owner = 'Geoff'; 通过查询Oracle执行SQL历史记录,数据库表的删

随机推荐