2013-06-23 2 views
10

Я хотел бы создать подкласс программно. У меня есть несколько вариантов: Javassist, CGLib, BCEL или ASM.Java - создание подкласса динамически

Практический пример: внутренние элементы одного приложения ориентированы на классы, а расширения - на основе классов. Поэтому я не могу иметь один класс в качестве базы для нескольких расширений, управляемых внешними скриптами.

Теперь - как бы я это сделал? Я нашел примеры с перехватом вызовов методов, доступом к полям, инициализацией и т. Д. Но ничего не касается подкласса.

Я хотел бы закончить с классом, который:

  • имеет имя, которое я хочу.
  • является (прямой, в лучшем случае) подкласс данного класса
  • копирует конструктор (s) из родительского класса (или вызывает super(...))
  • в конце концов, я хотел бы дать ему несколько аннотаций.

Я знаю, что это возможно, потому что различные динамические интеграционные языки, такие как GroovyClassLoader, могут это сделать.

ответ

5

Это довольно просто с Javassist:

import javassist.CannotCompileException; 
import javassist.ClassPool; 
import javassist.CtClass; 
import javassist.NotFoundException; 

static Class<? extends DefinitionBasedMigrator> createClass(String fullName) 
     throws NotFoundException, CannotCompileException 
{ 
    ClassPool pool = ClassPool.getDefault(); 

    // Create the class. 
    CtClass subClass = pool.makeClass(fullName); 
    final CtClass superClass = pool.get(DefinitionBasedMigrator.class.getName()); 
    subClass.setSuperclass(superClass); 
    subClass.setModifiers(Modifier.PUBLIC); 

    // Add a constructor which will call super(...); 
    CtClass[] params = new CtClass[]{ 
     pool.get(MigratorDefinition.class.getName()), 
     pool.get(GlobalConfiguration.class.getName()) 
    }; 
    final CtConstructor ctor = CtNewConstructor.make(params, null, CtNewConstructor.PASS_PARAMS, null, null, subClass); 
    subClass.addConstructor(ctor); 

    return subClass.toClass(); 
} 

Maven зависимость:

<!-- https://mvnrepository.com/artifact/org.javassist/javassist --> 
<dependency> 
    <groupId>org.javassist</groupId> 
    <artifactId>javassist</artifactId> 
    <version>3.22.0-GA</version> 
</dependency> 
2

Java Доверенные может быть в состоянии делать то, что вам требуется - они по существу позволяют динамически слой функциональность поверх объект, так как вы можете перехватывать вызовы любого метода на этот объект и сами обрабатывать их или отправлять вызовы метода базовому классу. В зависимости от того, что вы хотите сделать, может быть, вы можете получить тот же результат, что и вы, создав динамический динамический класс

У Oracle есть decent introduction на своем веб-сайте (ссылка на URL-адрес Java версии 1.4.2, но я не думаю, что поведение этого изменилось в более поздних версиях). Вот more concise example, который дает хороший вкус тому, что выглядит прокси-код.

Также можно делать вещи, используя манипуляции с прямым байтовым кодом (поддерживаемые ASM framework), однако я полагаю, что использование прокси было бы более простым подходом.

+0

На самом деле я сначала посмотрел на прокси, но оставил это из-за сложности. Джавасисту так легче. А также прокси-серверы нуждаются в интерфейсе и не могут быть переопределены как обычные классы ... в любом случае +1 –

7

Здесь может быть использована одна библиотека, которую я особенно люблю; Bytebuddy.

Пример взят непосредственно с целевой страницы:

Class<?> dynamicType = new ByteBuddy() 
    .subclass(Object.class) 
    .method(ElementMatchers.named("toString")) 
    .intercept(FixedValue.value("Hello World!")) 
    .make() 
    .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
    .getLoaded(); 

Это невероятно гибкий и, безусловно, стоит проверить, если вы хотите, чтобы ваши волосы, я лично найти тяжелое использование Javassist может получить довольно уродливый и беспорядочно время от времени, bytebuddy чувствует себя как необходимое дыхание свежего воздуха!

Rafael Winterhalter также активен в StackOverflow, что позволяет узнать все, что вы не уверены в ветре.

Редактировать: мои извинения за некропостинг.Приземлился здесь, когда друг связал вопрос и забыл проверить дату.