- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章基于ClasspathResource路径问题的解决由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在项目中工程以springboot jar形式发布,跟之前容器比少了一个解压目录,这个过程中出现了ClasspathResource的文件获取问题。具体如下:
本地springboot工程打成jar包发布,在以下代码r.getFile()获取类目录下模板Excel文件报错:
cannot be resolved to absolute file path because it does not reside in the file system: jar 。
调整代码,直接获取对应的文件流,进行封装.
ClassPathReource resource=new ClassPathResource("spring_beans.xml");
在ClassPathResource中,含参数String path的构造函数:
public ClassPathResource(String path ) { this (path , (ClassLoader) null); }
public ClassPathResource (String path , ClassLoader classLoader ) { Assert. notNull(path, "Path must not be null"); String pathToUse = StringUtils.cleanPath(path); if (pathToUse .startsWith("/")) { pathToUse = pathToUse .substring(1); } this .path = pathToUse; this .classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); }
能够看到path由StringUtils的cleanPath方法返回了pathToUse。由此,我们找到StringUtils的cleanPath方法 。
public static String cleanPath (String path ) { if (path == null) { return null ; } String pathToUse = replace( path , WINDOWS_FOLDER_SEPARATOR , FOLDER_SEPARATOR); int prefixIndex = pathToUse .indexOf(":" ); String prefix = "" ; if (prefixIndex != -1) { prefix = pathToUse .substring(0, prefixIndex + 1); pathToUse = pathToUse .substring(prefixIndex + 1); } if (pathToUse .startsWith(FOLDER_SEPARATOR)) { prefix = prefix + FOLDER_SEPARATOR; pathToUse = pathToUse .substring(1); } String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR ); List<String> pathElements = new LinkedList<String>(); int tops = 0; for (int i = pathArray. length - 1; i >= 0; i --) { String element = pathArray [i ]; if (CURRENT_PATH .equals(element)) { // Points to current directory - drop it. } else if (TOP_PATH.equals(element)) { // Registering top path found. tops ++; } else { if (tops > 0) { // Merging path element with element corresponding to top path. tops --; } else { // Normal path element found. pathElements .add(0, element ); } } } // Remaining top paths need to be retained. for (int i = 0; i < tops; i++) { pathElements .add(0, TOP_PATH); } return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR ); }
public static String replace (String inString , String oldPattern , String newPattern ) { if (!hasLength( inString ) || !hasLength(oldPattern) || newPattern == null ) { return inString ; } StringBuilder sb = new StringBuilder(); int pos = 0; // our position in the old string int index = inString .indexOf(oldPattern ); // the index of an occurrence we"ve found, or -1 int patLen = oldPattern.length(); while (index >= 0) { sb.append( inString .substring(pos , index )); sb.append( newPattern ); pos = index + patLen; index = inString .indexOf(oldPattern, pos ); } sb.append( inString .substring(pos )); // remember to append any characters to the right of a match return sb .toString(); }
由此可以看出,同样的方法名,不同的方法签名,然后在其中一个方法中引用另外一个方法。好多类都是这么用的,就像开始的时候的构造函数那样,虽然不知道好处 是什么,但先记下来.
public static boolean hasLength (String str ) { return hasLength((CharSequence) str); } public static boolean hasLength (CharSequence str) { return (str != null && str.length() > 0); }
跟踪到这里,可以知道hasLength方法的目的就是str不为空且str的长度大于0。突然发现CharSequence这个类没接触过,来看一下它的源码 。
额,源码没看懂 就不粘贴过来了.
首先判断传入的三个参数,如果为空后者长度小于0,直接返回inString;那么我看一下这三个参数都是什么:
inString:path 这个就是我们传入的文件名 。
oldPattern:private static final String WINDOWS_FOLDER_SEPARATOR = "",
newPattern:private static final String FOLDER_SEPARATOR = "/" ;这两个是文件分隔符 。
然后给局部变量index赋值,通过查阅API:
public int indexOf(int ch)
返回指定字符在此字符串中第一次出现处的索引.
意思就是在path中查找"",例如我写文件的绝对路径是D:文件APIJDK_API_1_6_zh_CN.CHM,我就需要循环的读取“”,接下来while循环中出现了substring方法,继续查阅API
public String substring(int beginIndex)
返回一个新的字符串,它是此字符串的一个子字符串。该子字符串从指定索引处的字符开始,直到此字符串末尾.
public String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex 。
故此 ,第一次循环会把path路径中从0索引开始,直到第一个""之间的内容添加到StringBuffer中,然后再在StringBuffer中添加“/”,接下来pos和index都需要改变,要往后挪。因为循环需要往后走,我们要找到第二个“”,觉得这个有点算法的意思。返回sb.toString().
总结一下replace方法,本意是根据传入的路径path,如果是D:文件APIJDK_API_1_6_zh_CN.CHM这种格式的,给转换成D:/文件/API/JDK_API_1_6_zh_CN.CHM这种格式.
通过replace的返回值,我们得到了可以用的路径pathToUse,然后我们要把这个路径下“:”给找出来,正如代码 。
int prefixIndex = pathToUse.indexOf(":" );
那样,需要知道,indexOf方法只要没找到相应的字符,就会返回-1,所以在下面的判断中才会以perfixIndex是否为-1来进行判断。如果路 径中有“:”,接着以D:/文件/API/JDK_API_1_6_zh_CN.CHM举例,prefix="D:" pathToUse="/文件/API/JDK_API_1_6_zh_CN.CHM ",这个很有意思,因为程序不知道我们输入的是绝对路径 带D:的这种 ,还是/开头的这种,或者说相对路径,程序直接全给你判断了.
接下来会判断pathToUse是否以“/"开头,是的话prefix会加上“/”,现在的prefix有两种情况,可能是"D:/"这种,也可能是"/"这种,而pathToUse肯定是“文件/API/JDK_API_1_6_zh_CN.CHM ”这种了.
String[] pathArray = delimitedListToStringArray( pathToUse, FOLDER_SEPARATOR );
看到这句代码,我估计是把pathToUse给拆成字符串数组里,就像是这样,文件 API ***的这种。接下来看看具体的代码是不是这样:
public static String[] delimitedListToStringArray(String str, String delimiter) { return delimitedListToStringArray( str, delimiter, null ); } public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete ) { if (str == null) { return new String[0]; } if (delimiter == null) { return new String[] {str}; } List<String> result = new ArrayList<String>(); if ("" .equals(delimiter)) { for (int i = 0; i < str.length(); i++) { result.add(deleteAny( str.substring(i , i + 1), charsToDelete)); } } else { int pos = 0; int delPos ; while ((delPos = str.indexOf(delimiter , pos )) != -1) { result.add(deleteAny( str.substring(pos , delPos), charsToDelete )); pos = delPos + delimiter.length(); } if (str .length() > 0 && pos <= str.length()) { // Add rest of String, but not in case of empty input. result.add(deleteAny( str.substring(pos ), charsToDelete)); } } return toStringArray( result); }
先看看传入的参数:
str:pathToUse,就是文件/API/JDK_API_1_6_zh_CN.CHM delimiter:"/" charsToDelete:null
public static String deleteAny(String inString, String charsToDelete ) { if (!hasLength( inString) || !hasLength(charsToDelete)) { return inString ; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < inString.length(); i++) { char c = inString.charAt( i); if (charsToDelete .indexOf(c) == -1) { sb.append( c); } } return sb .toString(); }
如果说我们传入的"/"等于""的话,显然是不可能,我们所假如的话,会把pathTOUse倒着循环,每个字符都摘出来,然后当成字符串用,传入deleteAny中,然后又是循环,对每个字符而言,如果charsToDelete中没有这个字符,就在StringBuilder中添加这个字符。返回值是String。当然了,这个还没用到。我们用到的是那个很复杂的else 。
我们遇到了一个循环,对pathToUse而言,从索引0开始,如果pathToUse中有"/",就像文件/API/JDK_API_1_6_zh_CN.CHM 我们会得到“文件,然后还会进入deleteAny这个方法,参数inString就是"文件",charsToDelete是null,突然发现charsToDelete的值为Null的话会直接返回InString,也就是“文件”.
返回到delimitedListToStringArray方法之后,接着往后循环,最终的结果就是实现了把pathToUse给切割成若干个String的形式.
我们遇到了一个倒着的循环,如果说我们这个是特别正常的路径,就相当于复制了,如果是以.或者..结尾的这些内容,我们就把它给忽略了.
我得承认上面这些过程真的好复杂,其实就是做了一件事,对输入的路径进行了处理,只不过考虑的情况多了一点。所以我决定还是用debug来走一遍看看.
这时我用的是绝对路径 。
终于熬到了这个方法的结束.
this .classLoader = ( classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
如果传入的classLoaser有值,就返回这个值,如果没有,就获取一个.
public static ClassLoader getDefaultClassLoader() { ClassLoader cl = null; try { cl = Thread.currentThread().getContextClassLoader(); } catch (Throwable ex ) { // Cannot access thread context ClassLoader - falling back... } if (cl == null) { // No thread context class loader -> use class loader of this class. cl = ClassUtils.class .getClassLoader(); if (cl == null) { // getClassLoader() returning null indicates the bootstrap ClassLoader try { cl = ClassLoader.getSystemClassLoader(); } catch (Throwable ex ) { // Cannot access system ClassLoader - oh well, maybe the caller can live with null... } } } return cl ; }
写到这,我们的ClassPathResouce resouce实例就有了path 和 classLoader这两个关键属性.
如果我们想获取输入流 。
@Override public InputStream getInputStream() throws IOException { InputStream is; if (this .clazz != null) { is = this.clazz .getResourceAsStream(this. path); } else if (this.classLoader != null) { is = this.classLoader .getResourceAsStream(this. path); } else { is = ClassLoader.getSystemResourceAsStream( this.path ); } if (is == null) { throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is ; }
会判断clazz 有没有值,classLoader有没有值,然后再获取输入流。前两天整理了java.lang.Class这个类的意思,现在就能用一点了,先看看定义的一些属性 。
private final String path ; private ClassLoader classLoader; private Class<?> clazz;
ClassPathResource有好几个构造函数,有的构造函数会传入classLoader,有的会传入clazz,这个clazz就是相应的类在JVM上的实例,显然上面的例子中并没有这个东西,而classLoader是有的,所以通过classLoader获取输入流.
我觉得对ClassPathResource理解的更透彻了,虽然大部分时间都是在对path进行处理.
近期还要看看ClassLoader,还不是很清楚它的工作机制.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/weixin_42005602/article/details/106661322 。
最后此篇关于基于ClasspathResource路径问题的解决的文章就讲到这里了,如果你想了解更多关于基于ClasspathResource路径问题的解决的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
关闭。这个问题是off-topic .它目前不接受答案。 想要改进这个问题? Update the question所以它是on-topic用于堆栈溢出。 关闭 12 年前。 Improve thi
我有一个动态网格,其中的数据功能需要正常工作,这样我才能逐步复制网格中的数据。假设在第 5 行中,我输入 10,则从第 6 行开始的后续行应从 11 开始读取,依此类推。 如果我转到空白的第一行并输入
我有一个关于我的按钮消失的问题 我已经把一个图像作为我的按钮 用这个函数动画 function example_animate(px) { $('#cont
我有一个具有 Facebook 连接和经典用户名/密码登录的网站。目前,如果用户单击 facebook_connect 按钮,系统即可运行。但是,我想将现有帐户链接到 facebook,因为用户可以选
我有一个正在为 iOS 开发的应用程序,该应用程序执行以下操作 加载和设置注释并启动核心定位和缩放到位置。 map 上有很多注释,从数据加载不会花很长时间,但将它们实际渲染到 map 上需要一段时间。
我被推荐使用 Heroku for Ruby on Rails 托管,到目前为止,我认为我真的会喜欢它。只是想知道是否有人可以帮助我找出问题所在。 我按照那里的说明在该网站上创建应用程序,创建并提交
我看过很多关于 SSL 错误的帖子和信息,我自己也偶然发现了一个。 我正在尝试使用 GlobalSign CA BE 证书通过 Android WebView 访问网页,但出现了不可信错误。 对于大多
我想开始使用 OpenGL 3+ 和 4,但我在使用 Glew 时遇到了问题。我试图将 glew32.lib 包含在附加依赖项中,并且我已将库和 .dll 移动到主文件夹中,因此不应该有任何路径问题。
我已经盯着这两个下载页面的源代码看了一段时间,但我似乎找不到问题。 我有两个下载页面,一个 javascript 可以工作,一个没有。 工作:http://justupload.it/v/lfd7不是
我一直在使用 jQuery,只是尝试在单击链接时替换文本字段以及隐藏/显示内容项。它似乎在 IE 中工作得很好,但我似乎无法让它在 FF 中工作。 我的 jQuery: $(function() {
我正在尝试为 NDK 编译套接字库,但出现以下两个错误: error: 'close' was not declared in this scope 和 error: 'min' is not a m
我正在使用 Selenium 浏览器自动化框架测试网站。在测试过程中,我切换到特定的框架,我们将其称为“frame_1”。后来,我在 Select 类中使用了 deselectAll() 方法。不久之
我正在尝试通过 Python 创建到 Heroku PostgreSQL 数据库的连接。我将 Windows10 与 Python 3.6.8 和 PostgreSQL 9.6 一起使用。 我从“ht
我有一个包含 2 列的数据框,我想根据两列之间的比较创建第三列。 所以逻辑是:第 1 列 val = 3,第 2 列 val = 4,因此新列值什么都没有 第 1 列 val = 3,第 2 列 va
我想知道如何调试 iphone 5 中的 css 问题。 我尝试使用 firelite 插件。但是从纵向旋转到横向时,火石占据了整个屏幕。 有没有其他方法可以调试 iphone 5 中的 css 问题
所以我有点难以理解为什么这不起作用。我正在尝试替换我正在处理的示例站点上的类别复选框。我试图让它做以下事情:未选中时以一种方式出现,悬停时以另一种方式出现(选中或未选中)选中时以第三种方式出现(而不是
Javascript CSS 问题: 我正在使用一个文本框来写入一个 div。我使用以下 javascript 获取文本框来执行此操作: function process_input(){
你好,我很难理解 P、NP 和多项式时间缩减的主题。我试过在网上搜索它并问过我的一些 friend ,但我没有得到任何好的答案。 我想问一个关于这个话题的一般性问题: 设 A,B 为 P 中的语言(或
你好,我一直在研究 https://leetcode.com/problems/2-keys-keyboard/并想到了这个动态规划问题。 您从空白页上的“A”开始,完成后得到一个数字 n,页面上应该
我正在使用 Cocoapods 和 KIF 在 Xcode 服务器上运行持续集成。我已经成功地为一个项目设置了它来报告每次提交。我现在正在使用第二个项目并收到错误: Bot Issue: warnin
我是一名优秀的程序员,十分优秀!