Как вызвать ошибку/предупреждение сразу после компиляции/запуска, если элемент в моем пути к классу отсутствует

У меня есть очень простой исходный файл -- 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/и т. д.), то я готов рассмотреть это. Но я хочу подчеркнуть, что это решение наихудшего сценария. Мой проект и без того страдает от достаточной сложности, и последнее, что я хочу сделать, — это искоренить его, создав решение с нуля.

Моя цель — вносить небольшие постепенные изменения во всю мою кодовую базу. Все вместе это сводится к капитальному ремонту, но он состоит из множества крошечных шагов. Наслоение всей системы сборки на мою кодовую базу — это полная противоположность маленькому и мелкому. Мое исходное решение для сборки отлично работает, но оно столкнулось с этой проблемой во время рефакторинга.

Я не понимаю проблемы. Если программа работает в обоих случаях, это говорит о том, что внешний .jar не используется. Java не может знать, что вы собираетесь использовать этот .jar в будущем. Если вы на самом деле используете .jar, а его нет, вы получите ошибку компиляции или выполнения, в зависимости от того, как вы используете внешний .jar.

Turing85 18.08.2023 21:10

@ Turing85 Я добавил правку, которая включает в себя дополнительный контекст.

davidalayachew 18.08.2023 21:20

Я рекомендую использовать инструмент сборки, такой как gradle (gradle.org) или maven (maven.apache.org) для управления вашими зависимостями. Возможно, вы захотите взглянуть на подпроекты/многомодульные сборки.

Turing85 18.08.2023 21:27

@ Turing85 Это просто закончилось бы тем, что я перенаправил бы свои зависимости через Maven/Gradle на Java, а не прямо на Java. Я очень хорошо знаком с Maven, но на данный момент это добавило бы моей проблеме гораздо больше сложности/хрупкости с небольшой пользой. Если нет другого способа добиться того, о чем я прошу, то я готов рассматривать это как ответ на мой вопрос. Но это, конечно, не подходит для моей проблемы вообще.

davidalayachew 18.08.2023 21:36

Подходит ли вам добавление кода в вашу программу, который явно проверяет записи пути к классам?

Tim Moore 19.08.2023 00:27

@TimMoore Лучше, чем инструмент для сборки! редактировать - ну может быть, я не знаю точно, что вы имеете в виду

davidalayachew 19.08.2023 01:46

Пожалуйста, рассмотрите эту статью из блога Java Specialists, она довольно устарела, но я надеюсь, что она может быть вам полезна.

jccampanero 20.08.2023 23:54

@jccampanero WOW, это почти то, что я искал. Поместите это в ответ, и я награжу вас наградой. Какая умная идея. Java — такой гибкий язык.

davidalayachew 21.08.2023 04:21

Большое спасибо @davidalayachew, я очень ценю это, хотя вся заслуга принадлежит блогу Java Specialist людям. Я рад слышать, что комментарий был полезен.

jccampanero 21.08.2023 14:49
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
9
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как указано в комментарии к вопросу, некоторое время назад я наткнулся на блог специалистов по 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 21.08.2023 16:41

Извините за поздний ответ @davidalayachew. Конечно. Я обновил ответ с дальнейшими пояснениями. Пожалуйста, не стесняйтесь сообщить мне, если вам нужна дополнительная информация, я буду рад помочь, если это возможно.

jccampanero 22.08.2023 00:10

Присуждена награда, это именно тот ответ, который я искал, когда задавал этот вопрос. Я просто никогда не знал, что это возможно с голой Java. Отлично, ты вм! РЕДАКТИРОВАТЬ - на самом деле собирается присудить дополнительную награду, потому что этот ответ настолько образцовый. Для тех, кто читает, не стесняйтесь отвечать, но знайте, что награда в 300 баллов будет начислена этому плакату. Если ты придешь с чем-то получше, я тоже дам тебе отдельную награду.

davidalayachew 22.08.2023 00:21

У меня нет слов @davidalayachew... Новая награда была абсолютно ненужной... Честно говоря, я не знаю, что сказать... Спасибо, большое спасибо, я очень ценю это.

jccampanero 22.08.2023 00:38

В любое время, нп вообще

davidalayachew 22.08.2023 00:46

Другие вопросы по теме