Статьи

Классная загрузка веселья с Groovy

Иногда вам нужны особые меры. Не для того, чтобы Groovy работал, а для того, чтобы сделать ваши приложения или фреймворки немного более мощными или универсальными.

Вот две техники загрузки классов, которые я использовал в сочетании с Groovy, чтобы сделать жизнь веселее и интереснее.

Один: избавиться от Groovy

Иногда вам нужно заполучить ClassLoaderтот, который не включает классы Groovy. Иногда вам нужно иметь ClassLoaderтолько те классы, которые поставляются с JVM.

У каждого Java-приложения есть ClassLoaderтолько классы JVM, и вы можете получить его, если знаете, как.

java.lang.ClassLoaderКласс имеет статический getSystemClassLoader()метод , который возвращает , ClassLoaderкоторый содержит все классы на CLASSPATH , когда виртуальная машина начала:

ClassLoader.systemClassLoader.loadClass "some.class.on.the.ClassPath"

Это ClassLoaderимеет один или несколько родительских ClassLoaderс. На ClassLoaderвершине этой иерархии находится только классы JVM. Когда вы найдете ClassLoaderбез родителей, вы знаете, что нашли его:

def classLoader = ClassLoader.systemClassLoader
while (classLoader.parent) {
       classLoader = classLoader.parent
}

Эта маленькая рутина поднимает тебя на вершину ClassLoader. Теперь вы можете использовать это, чтобы создать новую ClassLoaderиерархию. Требуется только создать новый URLClassLoaderи указать расположение файлов JAR, которые вы хотите включить:

def classLoader = ClassLoader.systemClassLoader
while (classLoader.parent) {
classLoader = classLoader.parent
}
def newClassLoader = new URLClassLoader([new File("location/to.jar").toString().toURL()] as URL[], classLoader)

Теперь у вас есть новый ClassLoader. Предупреждение: он не включает Groovy, если вы не добавляете JAR Groovy специально, и все классы, которые уже были загружены, будут загружены снова, увеличивая объем памяти вашего приложения. Кроме того, не создавайте слишком много новых ClassLoaderфайлов в одном приложении, поскольку они имеют тенденцию вызывать утечки памяти. Создание одного или нескольких должно быть хорошо. Классы, загруженные с новым ClassLoader, не будут совместимы с классами, загруженными текущей системой ClassLoader, за исключением классов JVM.

Вы можете улучшить этот код, используя org.apache.tools.ant.launch.Locatorкласс (поставляется с Ant). Его fileToURL()метод немного лучше работает при преобразовании java.io.Fileобъектов в java.net.URLметоды.

Два: овладеть GroovyClassLoaderJava-кодом

Иногда вам нужно получить groovy.lang.GroovyClassLoaderобъект в коде Java. Это может произойти, например, когда код Java вызывается из кода Groovy. GroovyClassLoaderКак правило, они находятся в нижней части ClassLoaderиерархий, и иногда вам нужно загружать классы, GroovyClassLoaderа не где-либо еще в ClassLoaderиерархии.

Однако ваш Java-код, скорее всего, не был загружен GroovyClassLoader, так как его заполучить? Вы можете передать это явно из вашего Groovy-кода, но это не практично. Вместо этого использовать sun.reflect.Reflectionкласс? Никогда не слышал об этом? Тогда пришло время познакомиться с getCallerClass()методом.

Когда Groovy-код вызывает Java-код, то где-то в стеке есть класс Groovy, который был загружен с помощью GroovyClassLoader. Вот как можно получить его из кода Java:

import sun.reflect.Reflection;
import groovy.lang.GroovyClassLoader;
public class MyClass {
public void myMethod() throws Exception {
int frame = 1;
// number of levels above you in the stack
Class c = Reflection.getCallerClass(frame); 
while (c != null && !(c.getClassLoader() instanceof GroovyClassLoader)) {
frame++;
c = Reflection.getCallerClass(frame);
}
if (c != null) {
GroovyClassLoader gcl = (GroovyClassLoader) c.getClassLoader();
// do Groovy stuff here
Class newClass = gcl.parseClass("class MyNewClass{}");
}       
}
}

Эти приемы показывают, насколько Groovy интегрирован с JVM и как Groovy генерирует надлежащий байт-код Java.

Удачного Groovy кодирования!