У меня есть очень простой исходный файл -- HelloWorld.java
public class HelloWorld
{
public static void main(String[] args)
{
System.out.println("hello world");
}
}
У меня есть следующая команда компиляции.
javac HelloWorld.java
Если я запускаю его, это работает нормально.
У меня есть следующая команда запуска.
java HelloWorld
Если я запускаю его, это тоже работает нормально.
Теперь у меня также есть один файл jar (some.jar
) в качестве зависимости. Я еще не использую его, но буду, и поэтому хочу начать помещать его в свой путь к классам.
Итак, я попытался запустить две вышеупомянутые команды с включенным --class-path
на этот раз.
javac --class-path = ".;some.jar" HelloWorld.java
java --class-path = ".;some.jar" HelloWorld
Это тоже сработало.
Наконец, я случайно удалил свой some.jar
и повторно выполнил две приведенные выше команды.
Удивительно, но от Java не было ни ошибок, ни предупреждений.
Мой вопрос: как мне заставить Java сказать мне, что он не может найти элемент, который я указал в своем пути к классам? Мне все равно, появляется ли предупреждение при компиляции или при запуске, но я хотел бы знать, что допустил ошибку за какой-то момент до того, как моя программа действительно попытается использовать зависимость (и потерпит неудачу, потому что Java не мог найти ее все это время ).
По сути, мне нужна какая-то форма подтверждения или подтверждения того, что Java знает, где находятся мои указанные элементы в пути к классам, и может их получить.
Напоминаем, что это минимальный воспроизводимый пример. Моя реальная проблема связана с НАМНОГО большим количеством движущихся частей. Я использую этот (по общему признанию надуманный) пример, чтобы подчеркнуть свою точку зрения.
===
РЕДАКТИРОВАТЬ. Чтобы лучше объяснить, почему это проблема, давайте оставим минимальный пример и поговорим о моей реальной проблеме. Я нахожусь в середине капитального ремонта моей кодовой базы. Я пытаюсь обновить зависимости, провести рефакторинг кода, реорганизовать структуру папок и многое другое. В разгар этого я сталкиваюсь с ClassNotFoundException. В первый раз это было потому, что у меня была устаревшая версия моей зависимости. Но во второй раз это произошло из-за того, что я поместил файл jar в неправильное место, и, более конкретно, место, которое я поместил в путь к классам, указывало на несуществующее местоположение файла jar.
Я хочу пресечь эту проблему в зародыше, заставив Java заранее сообщить мне, указывает ли путь к классу, который я ему предоставляю, вообще ни на что не указывает. Неспособность сделать это значительно ухудшает ремонтопригодность моей кодовой базы во время крупных рефакторингов и капитальных ремонтов.
РЕДАКТИРОВАТЬ 2. Если нет другого варианта, кроме использования инструмента сборки (Maven/Gradle/и т. д.), то я готов рассмотреть это. Но я хочу подчеркнуть, что это решение наихудшего сценария. Мой проект и без того страдает от достаточной сложности, и последнее, что я хочу сделать, — это искоренить его, создав решение с нуля.
Моя цель — вносить небольшие постепенные изменения во всю мою кодовую базу. Все вместе это сводится к капитальному ремонту, но он состоит из множества крошечных шагов. Наслоение всей системы сборки на мою кодовую базу — это полная противоположность маленькому и мелкому. Мое исходное решение для сборки отлично работает, но оно столкнулось с этой проблемой во время рефакторинга.
@ Turing85 Я добавил правку, которая включает в себя дополнительный контекст.
Я рекомендую использовать инструмент сборки, такой как gradle (gradle.org) или maven (maven.apache.org) для управления вашими зависимостями. Возможно, вы захотите взглянуть на подпроекты/многомодульные сборки.
@ Turing85 Это просто закончилось бы тем, что я перенаправил бы свои зависимости через Maven/Gradle на Java, а не прямо на Java. Я очень хорошо знаком с Maven, но на данный момент это добавило бы моей проблеме гораздо больше сложности/хрупкости с небольшой пользой. Если нет другого способа добиться того, о чем я прошу, то я готов рассматривать это как ответ на мой вопрос. Но это, конечно, не подходит для моей проблемы вообще.
Подходит ли вам добавление кода в вашу программу, который явно проверяет записи пути к классам?
@TimMoore Лучше, чем инструмент для сборки! редактировать - ну может быть, я не знаю точно, что вы имеете в виду
Пожалуйста, рассмотрите эту статью из блога Java Specialists, она довольно устарела, но я надеюсь, что она может быть вам полезна.
@jccampanero WOW, это почти то, что я искал. Поместите это в ответ, и я награжу вас наградой. Какая умная идея. Java — такой гибкий язык.
Большое спасибо @davidalayachew, я очень ценю это, хотя вся заслуга принадлежит блогу Java Specialist людям. Я рад слышать, что комментарий был полезен.
Как указано в комментарии к вопросу, некоторое время назад я наткнулся на блог специалистов по 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
named 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
Этот скрипт в основном пытается установить значение переменной STRICTCP
, анализируя аргументы, предоставленные файлу скрипта bat
. Эта переменная будет фактически использоваться для проверки пути к классам при вызове ClassPathInfo
.
В статье есть пара вспомогательных скриптов для обоих javac
:
@echo off
call checkcp %1 %2 %3
if not %errorlevel%==0 goto END
javac %1 %2 %3 %4 %5 %6 %7 %8 %9
:END
и java
вызовы команд:
@echo off
call checkcp %1 %2 %3
if not %errorlevel%==0 goto END
java %1 %2 %3 %4 %5 %6 %7 %8 %9
:END
Чтобы понять checkcp.bat
, давайте рассмотрим пример, представленный на сайте, основанный на вызове strictjava
:
strictjava -classpath "c:\sub\jar1.jar;c:\sub2\jar2.jar" MyClass
Как указано во вспомогательных файлах bat
:
call checkcp %1 %2 %3
мы передаем здесь три аргумента checkcp
по порядку:
%1=-classpath
%2=c:\sub\jar1.jar;c:\sub2\jar2.jar
%3=MyClass
Это означает, что согласно этой инструкции в checkcp.bat
:
set STRICTCP = "%2;%STRICTHOME%"
эффективный путь класса, который будет проверен, следующий:
STRICTCP = "c:\sub\jar1.jar;c:\sub2\jar2.jar;C:\strictjava"
Если, напротив, вы используете что-то вроде этого:
strictjava MyClass
checkcp
попытается проанализировать переменную окружения CLASSPATH
для проверки пути к классу, как указано в этом фрагменте кода:
if ""= = "%3" (
set STRICTCP = "%CLASSPATH%;%STRICTHOME%"
)
Обратите внимание, что в соответствии со сценарием флаг -classpath
должен быть указан первым.
Пожалуйста, берите сценарий только как референс реальной реализации, адаптируйте его под свои нужды.
Хорошо, я приму этот ответ, но не могли бы вы рассказать о пакетных и вспомогательных сценариях? Я их вообще не понимаю.
Извините за поздний ответ @davidalayachew. Конечно. Я обновил ответ с дальнейшими пояснениями. Пожалуйста, не стесняйтесь сообщить мне, если вам нужна дополнительная информация, я буду рад помочь, если это возможно.
Присуждена награда, это именно тот ответ, который я искал, когда задавал этот вопрос. Я просто никогда не знал, что это возможно с голой Java. Отлично, ты вм! РЕДАКТИРОВАТЬ - на самом деле собирается присудить дополнительную награду, потому что этот ответ настолько образцовый. Для тех, кто читает, не стесняйтесь отвечать, но знайте, что награда в 300 баллов будет начислена этому плакату. Если ты придешь с чем-то получше, я тоже дам тебе отдельную награду.
У меня нет слов @davidalayachew... Новая награда была абсолютно ненужной... Честно говоря, я не знаю, что сказать... Спасибо, большое спасибо, я очень ценю это.
В любое время, нп вообще
Я не понимаю проблемы. Если программа работает в обоих случаях, это говорит о том, что внешний
.jar
не используется. Java не может знать, что вы собираетесь использовать этот.jar
в будущем. Если вы на самом деле используете.jar
, а его нет, вы получите ошибку компиляции или выполнения, в зависимости от того, как вы используете внешний.jar
.