Я генерирую .java
файлы классов во время выполнения и должен немедленно использовать эти классы внутри кода.
Поэтому я компилирую классы .java
, используя API компилятора для создания .class
файлов:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
StandardJavaFileManager manager = compiler.getStandardFileManager(diagnostics, null, null);
File file = new File("path to file");
Iterable<? extends JavaFileObject> sources = manager.getJavaFileObjectsFromFiles(Arrays.asList(file));
CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, sources);
task.call();
manager.close();
Затем мне нужно получить ссылки на эти скомпилированные классы, используя Class.forName()
, но если я просто вызову Class.forName("com.foo.Bar")
, то получится ClassNotFoundException
, при условии, что новые файлы .class
не добавлены в classpath
I искал методы добавления классов к classpath
во время выполнения. Я столкнулся с некоторыми неясностями, связанными с этим понятием:
1. Правильный ли этот подход (сначала скомпилировать файл .java
, использовать API компилятора и добавить его в загрузчик классов на втором шаге)? Чтобы можно было мгновенно использовать класс в коде.
2. AFAIK, Есть 2 метода для динамической загрузки классов в classpath во время выполнения: один использует пользовательский ClassLoader, как это: (который я скомпилировал, так как он жаловался, что BuiltinClassLoader
не addURL
метод):
// Get the ClassLoader class
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class<?> clazz = cl.getClass();
// Get the protected addURL method from the parent URLClassLoader class
Method method = clazz.getSuperclass().getDeclaredMethod("addURL", new Class[] { URL.class });
// Run projected addURL method to add JAR to classpath
method.setAccessible(true);
method.invoke(cl, new Object[] { cls });
Другой метод использует Class.forName(name, instantiation, classLoader)
для добавления класса в classpath (который также дает ссылку на класс). Первый метод, который я не смог применить, так как я получил ошибку компилятора (Java 11), как упомянуто выше.
Что касается второго метода, присоединит ли Class.forName(name, instantiation, classLoader)
новые классы к classpath
, если мы вызовем загрузчик классов по умолчанию, как этот? :
Class.forName("com.foo.Bar",true, ClassLoader.getSystemClassLoader());
// or:
Class.forName("com.foo.Bar",true, ApiHandler.class.getClassLoader());
Это не работает для меня. Какой вариант приведенных выше аргументов classLoader
верны и почему они не работают? Обязательно ли создавать собственный загрузчик классов и передавать его в Class.forName()
?
3. Я создаю .java
файлы в пакете com.foo
в папке src
проекта eclipse. Их скомпилированные .class
файлы также генерируются в той же папке (с использованием API компилятора). Когда я обновляю проект, используя eclipse (щелкнув правой кнопкой мыши по проекту -> Обновить), соответствующие файлы .class
будут сгенерированы в папке target/classes
, и тогда к классам можно будет получить доступ через код (например, с помощью Class.forName("com.foo.Bar)
). Может быть, если я создам .class
файлов (с помощью API компилятора) в папке target/classes
, классы будут распознаваемы без необходимости их введения в путь к классам?
UPDATE:
Я смог использовать скомпилированные классы в своем коде, сохранив уважаемые файлы .class
в папке target/classes
, упомянутой в третьем вопросе выше) проекта. (Путем добавления опции -d
к методу getTask()
компилятора:
Iterable<String> options = Arrays.asList( new String[] { "-d", System.getProperty("user.dir") + "/target/classes/"} );
.
.
.
CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, sources);
Таким образом, кажется, что классы даже не нужно добавлять в путь к классам с помощью classLoader ; так как класс доступен с помощью простого Class.forName()
. Как вы это объясните?
Class<?> cls1 = Class.forName("com.foo.Bar");
А также через ClassLoader, конечно:
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> cls = classLoader.loadClass("com.foo.Bar");