Сначала большое предупреждение: вы должны быть уверены в том, откуда исходит код, который вы хотите скомпилировать, поскольку это создает огромный потенциал для, ну, в общем, внедрения кода.
Еще одно предупреждение: если вы компилируете классы, вы не можете использовать одно и то же имя класса снова и снова , поэтому ваш ClassLoader в конечном итоге съест всю вашу оперативную память перед OOME!Одна только эта причина должна заставить вас найти другой способ сделать это! Редактировать : начиная с Java 8, больше нет пространства PermGen (где хранились метаданные класса), но метапространство .Одно из различий заключается в том, что Metaspace собирает мусор, а PermGen нет;так что это должно уменьшить потенциал для OOME.
Если вы просто ищете способ добавить более динамичную конфигурацию в вашу программу, я настоятельно рекомендую вам взглянуть на ScriptEngine , которыйподдерживает ECMAScript (очень близко к JavaScript) "из коробки" (если вы используете Oracle HotSpot JVM по крайней мере).Это избавит вас от лишних затрат на написание собственного ClassLoader и компиляцию кода.
Тем не менее, если вы все еще хотите продолжить в этом направлении, я просто предполагаю, что здесь будет дан старт, но вывероятно, придется скомпилировать файл с помощью JavaCompiler объекта и вставить содержимое результата компиляции в ClassLoader .
Если бы у меня былчтобы протестировать компьютер, я бы попробовал что-то вроде:
Files[] files = ... ; // input for first compilation task
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files));
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call().get(); // Compile the class and wait for it to finish
Class<?> cls = null;
for (JavaFileObject compiledClass : compilationUnits) {
if (compiledClass.getKind() != Kind.CLASS)
continue;
int n;
byte[] classData;
try (InputStream is = compiledClass.openInputStream()) {
classData = new byte[1024]; // DO A PROPER FULL READ HERE!
n = is.read(classData);
} // catch Exceptions here
cls = myClassLoader.defineClass(className, classData, 0, n);
break;
}
if (cls != null) {
// Now, cls.newInstance() etc.
}
Помимо того, что это полная попытка в темноте, вам также придется определить свой собственный ClassLoader, как defineClass()
*Метод 1036 * защищен.
Кроме того, вы также можете вызвать вызов javac
, используя Runtime.exec()
и считывая байты байт-кода непосредственно из сгенерированного .class
, или даже, возможно, сгенерироватьэто прямо в classpath.