我有一个非常简单的源文件——
HelloWorld.java
public class HelloWorld
{
public static void main(String[] args)
{
System.out.println("hello world");
}
}
我有以下编译命令。
javac HelloWorld.java
如果我运行它,效果很好。
我有以下运行命令。
java HelloWorld
如果我运行它,这也可以正常工作。
现在,我还有一个 jar 文件 (
some.jar
) 作为依赖项。我还没有使用它,但我会,因此,想开始将它放入我的类路径中。
因此,我尝试运行上述 2 个命令,这次包含了
--class-path
。
javac --class-path=".;some.jar" HelloWorld.java
java --class-path=".;some.jar" HelloWorld
这也有效。
最后,我不小心删除了我的
some.jar
,并重新运行了上述2条命令。
令人惊讶的是,Java 没有出现任何错误或警告。
我的问题是——如何让 Java 告诉我它找不到我在类路径中列出的项目?我并不关心警告是在编译时还是在运行时,但我想知道在我的程序实际尝试使用依赖项之前的某个时刻我犯了一个错误(并且失败,因为 Java 一直找不到它) ).
基本上,我想要某种形式的确认或重申,Java 知道我在类路径上指定的项目在哪里,并且可以到达它们。
请记住,这是一个最小的、可重现的示例。我的实际问题涉及更多的移动部分。我使用这个(诚然是人为的)示例来强调我的观点。
===
编辑 - 为了更好地解释为什么这是一个问题,让我们留下最小的例子并谈谈我的实际问题。我正在彻底修改我的代码库。我正在尝试更新依赖项、重构代码、重新组织我的文件夹结构等等。在此过程中,我遇到了 ClassNotFoundException。第一次,是因为我的依赖项版本已过时。但第二次,是因为我把 jar 文件放在了错误的位置,更具体地说,我放在类路径上的位置指向了一个不存在的 jar 文件位置。
我想让 Java 预先告诉我我提供的类路径位置是否指向任何内容,从而将这个问题消灭在萌芽状态。在大型重构和大修期间,无法做到这一点会严重损害我的代码库的可维护性。
编辑2 - 如果除了使用构建工具(Maven/Gradle/等)之外没有其他选择,那么我愿意考虑这一点。但我想强调,这是最坏情况的解决方案。我的项目本身就很复杂,我想做的最后一件事就是从头开始构建解决方案。
我的目标是对我的代码库进行小的增量更改。总而言之,它构成了一次彻底的检修,但它是由一堆微小的、一口大小的步骤组成的。将整个构建系统分层到我的代码库与小而小完全相反。我原来的构建解决方案效果很好,只是在重构时遇到了这个问题。
正如问题评论中所指出的,不久前我遇到了 Java 专家博客 文章,它仍然可能有帮助。
基本上,它定义了一个辅助类
ClassPathInfo
,如下所示:
import java.net.*;
import java.io.*;
public class ClassPathInfo {
private final ClassLoader classLoader;
public ClassPathInfo(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public ClassPathInfo() {
this(ClassLoader.getSystemClassLoader());
}
/**
* validates classpath, throws an LinkageError if invalid
* classpath was specified
*/
public void validateClassPath() {
try {
URL[] urls = ((URLClassLoader)classLoader).getURLs();
for(int i=0; i<urls.length; i++) {
try {
urls[i].openStream();
} catch(IllegalArgumentException iae) {
throw new LinkageError(
"malformed class path url:\n "+urls[i]);
} catch(IOException ioe) {
throw new LinkageError(
"invalid class path url:\n "+urls[i]);
}
}
} catch(ClassCastException e) {
throw new IllegalArgumentException(
"The current VM's System classloader is not a "
+ "subclass of java.net.URLClassLoader");
}
}
public static void main(String[] args) {
ClassPathInfo info = new ClassPathInfo();
info.validateClassPath();
}
}
该类使用系统类加载器(它是
URLClassLoader
的实例)来检查类路径中找到的每个条目是否存在。
该博客包含一个帮助程序脚本,用于简化名为
ClassPathInfo
的 checkcp.bat
的调用:
@echo off
rem Set STRICTHOME to the directory you're deploying this file to.
set STRICTHOME=C:\strictjava
if ""=="%3" (
set STRICTCP="%CLASSPATH%;%STRICTHOME%"
) else (
if -classpath==%1 (
set STRICTCP="%2;%STRICTHOME%"
)else (
echo If more than three parameters are specified,
echo the first has to be -classpath.
set ERRORLEVEL=1
goto end
)
)
java -classpath %STRICTCP% ClassPathInfo
set STRICTHOME=
set STRICTCP=
:end
以及一些用于
javac
和 java
命令调用的帮助程序脚本:
@echo off
call checkcp %1 %2 %3
if not %errorlevel%==0 goto END
javac %1 %2 %3 %4 %5 %6 %7 %8 %9
:END
@echo off
call checkcp %1 %2 %3
if not %errorlevel%==0 goto END
java %1 %2 %3 %4 %5 %6 %7 %8 %9
:END