2010-06-11 4 views
15

Я хотел бы избежать отражения в проекте с открытым исходным кодом, который я разрабатываю. Здесь у меня есть классы вроде следующего.Улучшение поля получить и установить производительность с ASM или Javassist

public class PurchaseOrder { 

    @Property 
    private Customer customer; 

    @Property 
    private String name; 
} 

Я просматриваю для @Property аннотацию, чтобы определить, что можно установить и получить от PurchaseOrder рефлекторно. Существует много таких классов, которые используют java.lang.reflect.Field.get() и java.lang.reflect.Field.set().

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

public interface PropertyAccessor<S, V> { 
    public void set(S source, V value); 
    public V get(S source); 
} 

Теперь, когда я просматриваю класс я могу создать статический внутренний класс PurchaseOrder как так.

static class customer_Field implements PropertyAccessor<PurchaseOrder, Customer> { 
    public void set(PurchaseOrder order, Customer customer) { 
     order.customer = customer; 
    } 
    public Customer get(PurchaseOrder order) { 
     return order.customer; 
    } 
} 

С этими словами я полностью избегаю стоимости отражения. Теперь я могу установить и получить из своих экземпляров собственную производительность. Может ли кто-нибудь сказать мне, как я это сделаю. Пример кода был бы замечательным. Я искал сеть для хорошего примера, но не могу найти ничего подобного. Примеры ASM и Javasist также довольно бедны.

Ключ здесь в том, что у меня есть интерфейс, который я могу пройти. Таким образом, у меня могут быть различные реализации, возможно, один с Java Reflection по умолчанию, один с ASM и один с Javassist?

Любая помощь была бы принята с благодарностью.

+0

, учитывая ваше текущее описание, трудно понять, какова конечная цель. Почему бы просто не использовать Ломбок? Он предлагает поколение геттеров/сеттеров без отражения: http://projectlombok.org/ – SteveD

+0

Мне не нужны геттеры и сеттеры. Целью является производительность!Родные настройки и получение до 14 раз быстрее, чем отражение. JAXB использует аналогичную схему, как и многие другие инструменты, требующие динамического взаимодействия с POJOS. –

+3

@ng: почему вы считаете, что геттеры/сеттеры оценивают стоимость? Недавние JVM ужасно хороши в оптимизации общих шаблонов производительности, а getter/setter - это общий шаблон, который он получает. Единственная причина для поиска таких оптимизаций будет заключаться в том, что если ваше приложение работает с не очень мощным JVM, которое не так хорошо оптимизирует (например, сотовый телефон). –

ответ

10

ASM

Используя ASMifierClassVisitor, вы можете точно узнать, какой код нужно написать, чтобы генерировать внутренние классы:

ASMifierClassVisitor.main(new String[] { PurchaseOrder.customer_Field.class 
    .getName() }); 

Остальное просто определить, какие биты вы должны параметризовать в вашем код генератора. Пример вывод для PurchaseOrder$customer_Field который станет файлом inject/PurchaseOrder$customer_Field.class: (. Я использовал «впрыснуть» в упаковке)

public static byte[] dump() throws Exception { 

    ClassWriter cw = new ClassWriter(0); 
    FieldVisitor fv; 
    MethodVisitor mv; 
    AnnotationVisitor av0; 

    cw.visit(V1_6, ACC_SUPER, "inject/PurchaseOrder$customer_Field", 
     "Ljava/lang/Object;"+ 
     "Linject/PropertyAccessor<Linject/PurchaseOrder;Linject/Customer;>;", 
     "java/lang/Object", 
     new String[] { "inject/PropertyAccessor" }); 
//etc 

Вы также должны создать синтетические аксессор с помощью классов посетителя ASM в:

{ 
mv = cw.visitMethod(ACC_STATIC + ACC_SYNTHETIC, "access$0", 
      "(Linject/PurchaseOrder;Linject/Customer;)V", null, null); 
mv.visitCode(); 
mv.visitVarInsn(ALOAD, 0); 
mv.visitVarInsn(ALOAD, 1); 
mv.visitFieldInsn(PUTFIELD, "inject/PurchaseOrder", 
      "customer", "Linject/Customer;"); 
mv.visitInsn(RETURN); 
mv.visitMaxs(2, 2); 
mv.visitEnd(); 
} 
{ 
mv = cw.visitMethod(ACC_STATIC + ACC_SYNTHETIC, "access$1", 
      "(Linject/PurchaseOrder;)Linject/Customer;", null, null); 
mv.visitCode(); 
mv.visitVarInsn(ALOAD, 0); 
mv.visitFieldInsn(GETFIELD, "inject/PurchaseOrder", " 
      customer", "Linject/Customer;"); 
mv.visitInsn(ARETURN); 
mv.visitMaxs(1, 1); 
mv.visitEnd(); 
} 

См. this project для примера того, как вводить методы.


С этим я полностью избежать затрат на отражение.

Так как это все будет сделана во время выполнения:

  • есть авансовая стоимость этой синтаксического анализ и генерация кода
  • вам нужно обнаружить и самоанализ этих сгенерированных типов как-то
+0

См. Также [reflectasm] (https://github.com/EsotericSoftware/reflectasm). – McDowell

1

Вы также можете использовать Обработчики аннотаций, тем самым избегая сложности манипуляции байт-кодами. (см. this article on javabeat)

+0

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

+1

Я не говорил об использовании рефлексии для поиска аннотаций. Я говорил об обработчиках аннотаций. Вы даже не проверяли ссылку, которую я предоставил ... Подключаемые обработчики аннотации похожи на плагины компилятора. Они запускаются во время фазы компиляции и позволяют искать комментарии в исходном коде, чтобы создавать новые сущности - большинство файлов конфигурации времени или новые классы, такие как классы доступа, которые вы хотите создать. Следовательно, в предлагаемом решении нет рефлексии: все сгенерировано и скомпилировано. –

+0

Здесь я просматриваю файлы во время выполнения, я даже не знаю, что они существуют из библиотеки. Цель состоит в том, чтобы динамически определять, какие поля меня интересуют, ища @Property, когда я их нахожу, я могу динамически генерировать динамический доступ, чтобы взаимодействовать со значением. –

0

Я удивлен, что отражение намного медленнее. Если вы разогреваете JVM, это не должно быть более чем на 5 раз медленнее, чем прямой доступ. BTW Микро-бенчмарк может дать неверные результаты, потому что простой геттер/сеттер можно легко оптимизировать до нуля, если он не выполняет реальной работы.

Другой способ избежать отражения и байт-кода - использовать класс sun.misc.Unsafe. Его нужно обрабатывать с осторожностью и не переносить на все JVM, но он в 2-3 раза быстрее, чем отражение. Для примера см. Мой проект essence-rmi.

Другой вариант - сгенерировать код и скомпилировать его на лету. Вы можете использовать API компилятора или библиотеку, такую ​​как BeanShell.

Примечание: если у вас есть личное поле, к нему нельзя получить доступ из другого класса с использованием байтового кода. Это ограничение JVM. Внутренние и вложенные классы избегают этого, создавая методы доступа для вас, как доступ к классу $ 100 в классе с частными методами (возможно, вы видели их в стеке вызовов). Однако это означает, что вы не можете добавить класс для доступа к закрытым полям без изменения оригинала класс.

+0

Это не точный ответ, вы когда-нибудь даже смотрели на java.lang.Field.set()? Модификация кода для меня не проблема. Есть много способов сделать это, используя BeanShell, кажется, это действительно противный способ сделать это. API-интерфейс компилятора мог бы замедлить работу простых аксессуаров. –

-1

Целью является производительность!

Да, это во многих случаях цель. Но что вы делаете сейчас с PropertyAccessor, ваша производительность идет вниз! Каждый раз, когда вы хотите получить или установить свойство, вам нужно будет создать новый экземпляр для customer_Field. Или вам нужно сохранить свой экземпляр. Я не понимаю, что проблема с простым getter или setter.

public class PurchaseOrder { 

    @Property 
    private Customer customer; 

    @Property 
    private String name; 

    pulic void setCustomer(Customer c) 
    { 
     this.customer = c; 
    } 

    public Customer getCustomer() 
    { 
     return customer; 
    } 

    // The same for name 
} 

Это выступление!Родительский код, возможно, в 14 раз быстрее, но вам действительно нужно это быстро? Java отличная. Зачем? Из-за этого независимость от платформы. И если вы собираетесь создавать родные вещи, сила Java ушла. Итак, какова разница между ожиданием одной минуты за все, что нужно делать программам, и ждать 50 секунд. «Где мой 14 раз быстрее?» Вам нужно не только получить и установить. Вам нужно что-то сделать со всеми данными.

И я не думаю, что это будет быстрее, потому что вы просто получаете и устанавливаете экземпляры объектов и примитивы. Native Java создается для:.

  • методы, которые должны вычислить то, что будет действительно быстрее в машинном коде, чем с Java Runtime Environment (Много java.lang.Math методов, как sqrt() Они могли программировать его в Java, но это было бы быть медленнее)
  • вещи, которые Java не может сделать сам, например: выход из приложения, создание сокетов, запись/чтение файлов, вызов других процессов и т. д. Это не чистая Java, которая является нативным машинным кодом, что делает ,

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

+3

Я не думаю, что вы действительно понимаете этот вопрос. То, что я запрашиваю, является независимым от платформы на 100%. Его байт-код Java! Реализация геттеров и сеттеров поражает всю идею, она должна быть динамичной и во время выполнения. См. JAXB для примера такого варианта использования. –

+0

Производительность JAXB кстати в основном не из-за генерации кода.Это распространенное заблуждение. Существуют более быстрые инструменты, которые не нуждаются в генерации байт-кода либо во время компиляции, либо во время выполнения. Процессор Jackson JSON, например, в настоящее время использует простое старое отражение, но быстрее, чем JAXB. (некоторая разница связана с XML и JSON, но большинство из них не наблюдается - это можно увидеть при сравнении «сырой» синтаксического анализа xml/json) – StaxMan

4

пример с использованием Javassist, однако это требует, чтобы ваши свойства имеют защиту на уровне пакетов, а не быть частной

public class AccessorGenerator { 

    private final ClassPool pool; 

    public PropertyGenerator() { 
     pool = new ClassPool(); 
     pool.appendSystemPath(); 
    } 

    public Map<String, PropertyAccessor> createAccessors(Class<?> klazz) throws Exception { 
     Field[] fields = klazz.getDeclaredFields(); 

     Map<String, PropertyAccessor> temp = new HashMap<String, PropertyAccessor>(); 
     for (Field field : fields) { 
      PropertyAccessor accessor = createAccessor(klazz, field); 
      temp.put(field.getName(), accessor); 
     } 

     return Collections.unmodifiableMap(temp); 
    } 

    private PropertyAccessor createAccessor(Class<?> klazz, Field field) throws Exception { 
     final String classTemplate = "%s_%s_accessor"; 
     final String getTemplate = "public Object get(Object source) { return ((%s)source).%s; }"; 
     final String setTemplate = "public void set(Object dest, Object value) { return ((%s)dest).%s = (%s) value; }"; 

     final String getMethod = String.format(getTemplate, 
               klazz.getName(), 
               field.getName()); 
     final String setMethod = String.format(setTemplate, 
               klazz.getName(), 
               field.getName(), 
               field.getType().getName()); 

     final String className = String.format(classTemplate, klazz.getName(), field.getName()); 

     CtClass ctClass = pool.makeClass(className); 
     ctClass.addMethod(CtNewMethod.make(getMethod, ctClass)); 
     ctClass.addMethod(CtNewMethod.make(setMethod, ctClass)); 
     ctClass.setInterfaces(new CtClass[] { pool.get(PropertyAccessor.class.getName()) }); 
     Class<?> generated = ctClass.toClass(); 
     return (PropertyAccessor) generated.newInstance(); 
    } 

    public static void main(String[] args) throws Exception { 
     AccessorGenerator generator = new AccessorGenerator(); 

     Map<String, PropertyAccessor> accessorsByName = generator.createAccessors(PurchaseOrder.class); 

     PurchaseOrder purchaseOrder = new PurchaseOrder("foo", new Customer()); 

     accessorsByName.get("name").set(purchaseOrder, "bar"); 
     String name = (String) accessorsByName.get("name").get(purchaseOrder); 
     System.out.println(name); 
    } 
} 
+0

Так это статический внутренний класс оригинала? Если это так, я могу оставить доступ как приватный. –

+0

Для статического внутреннего класса, который реализует частные поля внешнего класса, javac-компилятор фактически генерирует дополнительные скрытые (синтетические) методы во внешнем классе, которые получают и устанавливают эти поля. Таким образом, частное поле по-прежнему доступно только из класса-владельца. –

+0

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

 Смежные вопросы

  • Нет связанных вопросов^_^