springboot项目在Linux环境下,打出的jar包找不到资源文件的问题总结

今天的开发中遇到的一个问题:

我在resources目录下的templates下建了一个xml文件。

在config类中想读取这个文件, 我使用了如下的代码:

File file = ResourceUtils.getFile("classpath:templates/importEXCEL.xml");

这种代码在windows下是没有问题的,但是一旦打包到了linux环境下,linux系统在jar包路径上多加了!符号,导致程序按这种路径找文件找不到。

改成下面这种写法就可以了:

ClassPathResource classPathResource = new ClassPathResource("templates/importEXCEL.xml");

 

附上xml文件和代码:


import com.techen.tap.exception.BusinessException;
import com.techen.tap.utils.I18nUtil;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @param paraMap
 * @Modify: EXCEL导入模板操作类
 * @author: lihao
 * @date: 2021/5/12 17:33
 * @return ResponseResult<Object>
 */
@Configuration
@Component
public class EXCELConfig {

    //    @Value("${excel.xmlPath}")
//    static String xmlPath;
    final static String EXAMPLE = "example";
    final static String DEFAULT = "default";

    Document document;

    public EXCELConfig() throws FileNotFoundException, DocumentException {
        ClassPathResource classPathResource = new ClassPathResource("templates/importEXCEL.xml");
//        File file = ResourceUtils.getFile("classpath:templates/importEXCEL.xml");
//        File file = ResourceUtils.getFile(xmlPath);
//        InputStream in = new FileInputStream(file);
        InputStream in = null;
        try {
            in = classPathResource.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        SAXReader reader = new SAXReader();
        document = reader.read(in);
    }

    /**
     * @param paraMap
     * @return ResponseResult<Object>
     * @Modify: 获取模板配置中的所有模板名称
     * @author: lihao
     * @date: 2021/5/15 13:55
     */
    public StringBuffer getAllExcelModelName() {
        StringBuffer result = new StringBuffer();
        // 获取xml文档根节点
        Element root = document.getRootElement();
        List<Element> listElement = root.elements();
        for (int i = 0; i < listElement.size(); i++) {
            String elementName = listElement.get(i).getName();
            result.append(elementName);
        }
        return result;
    }

    /**
     * @param paraMap
     * @return ResponseResult<Object>
     * @Modify: 按模版名称获取模版的excel文件
     * @author: lihao
     * @date: 2021/5/15 14:12
     */
    public byte[] getExcelModelByName(String modelName) throws IOException {
        // 获取xml文档根节点
        Element root = document.getRootElement();
        Element element = root.element(modelName);
        if (element == null) {
            return null;
        }
        //构造excel文件
        HSSFWorkbook excel = new HSSFWorkbook();
        HSSFSheet sheet0 = excel.createSheet(element.getName());
        HSSFRow rowNameExcel = sheet0.createRow(0);
        HSSFRow rowDataExcel = sheet0.createRow(1);
        List<Element> colList = element.elements();
        for (int i = 0; i < colList.size(); i++) {
            String data = colList.get(i).getData().toString();
            // 标头
            HSSFCell cellExcel = rowNameExcel.createCell(i);
            cellExcel.setCellValue(data);
            // 示例内容
            HSSFCell cellExample = rowDataExcel.createCell(i);
            Attribute attribute = colList.get(i).attribute(EXAMPLE);
            if (attribute != null) {
                cellExample.setCellValue(attribute.getValue());
            }
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        excel.write(bos);
        byte[] brray = bos.toByteArray();
        return brray;
    }

    /**
     * @param paraMap
     * @return ResponseResult<Object>
     * @Modify: excel转换为map
     * 将要导入的excel表格信息转换为map格式
     * @author: lihao
     * @date: 2021/5/22 11:47
     */
    public List<Map> exacleToMap(MultipartFile file, String modelName) {
        HSSFWorkbook excel = null;
        HSSFSheet sheet = null;
        HSSFRow row = null;
        List<Map> resultMap = new ArrayList<>();

        //构造excel文件
        try {
            excel = new HSSFWorkbook(new POIFSFileSystem(file.getInputStream()));
        } catch (IOException e) {
            throw new BusinessException(I18nUtil.getMessageDefault("file.InvalidFormat", "文件格式错误,请重试"));
        }

        // 获取指定名称的节点
        Element root = document.getRootElement();
        Element elementModel = root.element(modelName);
        List<Element> elements = elementModel.elements();

        if (excel != null) {
            for (int i = 0; i < excel.getNumberOfSheets(); i++) {
                sheet = excel.getSheetAt(i);
                // 从索引为1的行开始获取
                for (int r = 1; r < sheet.getPhysicalNumberOfRows(); r++) {
                    row = sheet.getRow(r);
                    for (int c = 0; c < row.getPhysicalNumberOfCells(); c++) {
                        Map map = new HashMap();
                        String key = elements.get(c).getName();
                        map.put(key, row.getCell(c));
                        resultMap.add(map);
                    }
                }
            }
        }
        System.out.print(resultMap.toString());
        return resultMap;
    }
}

xml文件代码:

<root>
    <ConsAccount>
        <cons_name default="1" example="张三">用户名称</cons_name>
        <cons_addr default="1" example="省市区">用户地址</cons_addr>
        <identity_card default="1" example="XXX">身份证号</identity_card>
        <cons_tel default="1" example="XXX">电话号码</cons_tel>
        <contact_name default="1" example="张三">联系人</contact_name>
        <postal_code default="1" example="05321">邮政编码</postal_code>
        <cons_email default="1" example="xxx@qq.com">电子邮件</cons_email>
        <inuse_num default="1" example="3">使用人口</inuse_num>
        <living_area default="1" example="108">居住面积</living_area>
        <msg_mobile default="1" example="XXX">短信提醒号码</msg_mobile>
    </ConsAccount>
    <Meter>
        <install_addr default="1" example="xxx">安装地址</install_addr>
        <comm_module_addr default="1" example="xxx">模块地址</comm_module_addr>
        <asset_no default="1" example="xxx">资产号</asset_no>
        <run_status_code default="01" example="xxx">运行状态</run_status_code>
        <flag_total default="0" example="xxx">总分类型</flag_total>
        <flag_download default="1" example="xxx">档案上云状态</flag_download>
        <R_val default="1" example="78">R值</R_val>
        <thermal_adapt default="1" example="34℃~67℃">温度自适应性</thermal_adapt>
    </Meter>
</root>

 

<think>嗯,我现在需要解决如何在Spring Boot项目中打印所有引用的JAR文件。这个问题我之前可能没遇到过,但根据我的Java知识,应该有一些方法可以做到。让我仔细想想。 首先,Spring Boot项目通常会使用Maven或者Gradle作为构建工具。所以,我需要分别考虑这两种情况。对于Maven项目,依赖项会在pom.xml中声明,而Gradle则是在build.gradle里。不过用户问的是如何打印已经引用的JAR文件,也就是实际被项目使用的依赖,而不仅仅是声明的。可能需要查看项目的依赖树或者类路径。 我记得Maven有一个命令可以显示依赖树,比如mvn dependency:tree。这个命令会列出所有依赖,括传递性依赖。可能执行这个命令后,将输出重定向到文件里,就能看到所有JAR的名称了。不过用户可能希望直接在应用程序运行时输出这些信息,而不仅仅是构建时。 如果是运行时的话,可能需要通过Java代码来获取类路径。Java的系统属性中有一个java.class.path,可以打印出来。不过Spring Boot打成可执行JAR后,类路径的处理可能和普通应用不同,因为Spring Boot使用自定义的类加载器。这时候直接获取系统属性可能不准确。 另外,Spring Boot的Fat JAR结构可能含BOOT-INF/lib目录下的所有依赖JAR。也许在启动应用时,可以通过代码遍历这个目录下的文件,打印出所有JAR的名称。不过需要注意,这种方式可能只在打后的可执行JAR中有效,开发环境下可能路径不同。 还有一种方法是在应用启动时,通过ClassLoader获取所有已加载的JAR资源。比如,使用ClassLoader.getResources()来查所有JAR文件,然后记录它们的路径。但需要考虑不同环境下的类加载机制,特别是Spring Boot使用的LaunchedURLClassLoader。 可能需要综合几种方法。比如,在应用启动时,添加一个监听事件,当应用上下文初始化完成后,获取类路径下的所有JAR文件并打印。或者使用Maven的依赖树命令,在构建阶段生成依赖列表。 现在,我需要验证这些思路是否正确。例如,如何在代码中获取类路径信息。Java中可以通过System.getProperty("java.class.path")来获取类路径,但在Spring Boot的可执行JAR中,这个值可能只含主JAR文件本身,而不是内部的BOOT-INF/lib中的JAR。这时候可能需要通过其他方式访问这些内部的JAR。 另外,Spring Boot的LaunchedURLClassLoader可能保存了所有依赖的JAR信息。可以尝试获取这个类加载器,然后反射获取其中的URLs。比如,通过Thread.currentThread().getContextClassLoader()获取类加载器,然后检查是否是LaunchedURLClassLoader的实例,如果是的话,调用其getURLs()方法,这样就能得到所有依赖的JAR路径。 不过需要注意,反射可能会有访问权限的问题,特别是不同版本的Spring Boot可能类名或方法有所变化。比如,LaunchedURLClassLoader在Spring Boot 2.x中的路径是org.springframework.boot.loader.LaunchedURLClassLoader。 总结一下可能的步骤: 1. 使用构建工具命令(如Maven的dependency:tree)在构建时生成依赖列表。 2. 在应用程序启动时,通过类加载器获取所有依赖JAR路径,并打印出来。 对于第一种方法,用户只需要运行mvn dependency:tree或者gradle dependencies命令即可查看依赖树,括所有传递性依赖。但用户可能希望在运行时动态获取,所以第二种方法更符合需求。 编写一个Spring Boot的CommandLineRunner或者ApplicationRunner组件,在应用启动时执行代码,获取类加载器中的URLs,并过滤出JAR文件。例如: ```java import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import org.springframework.boot.loader.LaunchedURLClassLoader; @Component public class JarPrinter implements CommandLineRunner { @Override public void run(String... args) throws Exception { ClassLoader classLoader = getClass().getClassLoader(); if (classLoader instanceof LaunchedURLClassLoader) { LaunchedURLClassLoader launchedLoader = (LaunchedURLClassLoader) classLoader; URL[] urls = launchedLoader.getURLs(); for (URL url : urls) { String path = url.getFile(); if (path.endsWith(".jar")) { System.out.println("JAR文件: " + path.substring(path.lastIndexOf('/') + 1)); } } } else { System.out.println("当前类加载器不是LaunchedURLClassLoader,无法获取依赖JAR列表。"); } } } ``` 这段代码利用了Spring Boot的LaunchedURLClassLoader来获取所有加载的JAR的URL,并提取文件名。需要注意的是,路径可能含文件系统的绝对路径或者JAR内部的路径,需要适当处理字符串。 不过,在开发环境中,比如使用IDE直接运行应用时,类加载器可能不是LaunchedURLClassLoader,这时候这种方法可能不适用。此时,可能需要考虑不同的环境,或者结合其他方法。 另一种方法是在启动应用时,通过JVM参数将依赖列表输出,比如使用-verbose:class参数,但这会输出大量类加载信息,需要过滤处理。 综合考虑,最可靠的方法可能是在构建时生成依赖树,或者在运行时通过类加载器获取JAR列表。两种方法各有适用场景,需要根据用户的具体需求选择。</think>在Spring Boot项目中打印所有引用的JAR文件,可以通过以下两种主要方式实现: --- ### **方法一:通过构建工具生成依赖树(构建阶段)** 适用于**查看声明的依赖及传递性依赖**,无需运行应用。 #### **Maven项目** 1. 执行命令生成依赖树: ```bash mvn dependency:tree ``` - 输出会显示所有依赖及其层级关系。 - 若需保存到文件: ```bash mvn dependency:tree > dependencies.txt ``` #### **Gradle项目** 1. 执行命令生成依赖列表: ```bash gradle dependencies ``` - 若需保存到文件: ```bash gradle dependencies > dependencies.txt ``` --- ### **方法二:通过代码打印运行时加载的JAR(运行阶段)** 适用于**在应用启动时动态获取实际加载的JAR路径**。 #### 步骤: 1. 创建一个Spring Boot组件(如`CommandLineRunner`): ```java import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import org.springframework.boot.loader.LaunchedURLClassLoader; import java.net.URL; @Component public class JarPrinter implements CommandLineRunner { @Override public void run(String... args) throws Exception { ClassLoader classLoader = getClass().getClassLoader(); if (classLoader instanceof LaunchedURLClassLoader) { // 获取所有JAR的URL URL[] urls = ((LaunchedURLClassLoader) classLoader).getURLs(); System.out.println("===== 加载的JAR文件列表 ====="); for (URL url : urls) { String path = url.getFile(); if (path.contains(".jar")) { // 提取JAR文件名(兼容Windows和Linux路径) String jarName = path.substring(path.lastIndexOf('/') + 1); System.out.println(jarName); } } } else { System.out.println("非可执行JAR环境,无法获取依赖列表。"); } } } ``` 2. **运行应用**: - 打后通过`java -jar`运行: ```bash java -jar your-application.jar ``` - 控制台会输出所有加载的JAR文件名,例如: ``` spring-boot-starter-web-2.5.4.jar spring-boot-starter-json-2.5.4.jar ... ``` --- ### **注意事项** 1. **开发环境与生产环境差异**: - 在IDE中直接运行(如IntelliJ)时,类加载器可能不是`LaunchedURLClassLoader`,此时需改用其他方式(如`System.getProperty("java.class.path")`)。 2. **路径处理**: - 若JAR路径含特殊字符(如`!/`),可能需要进一步解析(通常只需关注`BOOT-INF/lib/`下的文件名)。 3. **依赖范围**: - `dependency:tree`会含所有`compile`和`runtime`依赖,而代码方式仅打印实际加载的JAR。 --- ### **总结** - 若需**静态分析依赖**,使用构建工具命令。 - 若需**动态获取运行时JAR列表**,通过`LaunchedURLClassLoader`实现。两者结合可全面掌握项目依赖情况。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值