tabla quién que interprete extension estructura ejemplos cursos codigo caracteristicas java-bytecode-asm javassist cglib bytecode-manipulation jvm-bytecode
código fuentebcel-6.0-src.zipbcel-6.0-bin.zipasm-6.0_ALPHA-bin.zipjavassist-rel_3_22_0_cr1.zip

java-bytecode-asm - quién - tabla de codigo bytecode



Comparación dinámica del marco de manipulación de código de código de Java (3)

Análisis de librerías de bytecode.

Como puedo deducir de las respuestas que recibió aquí y de las preguntas que ha examinado, estas respuestas no abordan formalmente la pregunta de la manera explícita que ha indicado. Usted solicitó una comparación, mientras tanto estas respuestas expresaron vagamente lo que uno podría querer según su objetivo (p. Ej., ¿Necesita saber el código de bytes? [Y / n]), o es demasiado limitado.

Esta respuesta es un breve análisis de cada marco de bytecode y proporciona una comparación rápida al final.

Javassista

  • Tiny ( javassist.jar (3.21.0) es ~ 707KB / javassist-rel_3_22_0_cr1.zip es ~ 1.5MB)
  • Nivel alto (/ bajo)
  • Sencillo
  • Característica completa
  • Requiere mínimo o ningún conocimiento de formato de archivo de clase
  • Requiere un conocimiento moderado del conjunto de instrucciones de Java
  • Esfuerzo de aprendizaje mínimo
  • Tiene algunas peculiaridades en los métodos de compilación y inserción de bytecode de línea única / multilínea

Personalmente prefiero Javassist simplemente por la rapidez con la que puedes usarlo y construir y manipular clases con él. El tutorial es sencillo y fácil de seguir. El archivo jar es un pequeño 707KB, por lo que es agradable y portátil; lo hace adecuado para aplicaciones independientes.

ASM

  • Grande ( asm-6.0_ALPHA-bin.zip es ~ 2.9MB / asm-svn-latest.tar.gz (10/15/2016) es ~ 41MB)
  • Nivel bajo (/ alto)
  • Exhaustivo
  • Característica completa
  • Recomendar un conocimiento competente de formato de archivo de clase
  • Requiere dominio con el conjunto de instrucciones de Java.
  • Esfuerzo de aprendizaje moderado (algo complejo)

ASM by ObjectWeb es una biblioteca muy completa que no tiene nada que ver con la creación, generación y carga de clases. De hecho, incluso tiene herramientas de análisis de clase con analizadores predefinidos. Se dice que es el estándar de la industria para la manipulación de códigos de bytes. También es la razón por la que me alejo de ella.

Cuando veo ejemplos de ASM, parece una bestia incómoda de una tarea con el número de líneas que se necesita para modificar o cargar una clase. Incluso algunos de los parámetros de algunos métodos parecen un tanto crípticos y fuera de lugar para Java. Con cosas como ACC_PUBLIC , y muchas llamadas de método con null todas partes, honestamente, parece que es más adecuado para un lenguaje de bajo nivel como C. ¿Por qué no simplemente pasar un literal de cadena como "public", o un Modifier.PUBLIC enumeración Modifier.PUBLIC Es más amigable y fácil de usar. Esa es mi opinión, sin embargo.

Para referencia, aquí hay un tutorial de ASM (4.0): https://www.javacodegeeks.com/2012/02/manipulating-java-class-files-with-asm.html

BCEL

  • Pequeño ( bcel-6.0-bin.zip es 7.3MB / bcel-6.0-src.zip es 1.4MB)
  • Nivel bajo
  • Adecuado
  • Hace el trabajo
  • Requiere dominio con el conjunto de instrucciones de Java.
  • Fácil de aprender

Por lo que he visto, esta biblioteca es su biblioteca de clases básicas que le permite hacer todo lo que necesita, si puede dedicar algunos meses o años.

Aquí hay un tutorial de BCEL que realmente lo explica: http://www.geekyarticles.com/2011/08/manipulating-java-class-files-with-bcel.html?m=1

cglib

  • Muy pequeño ( cglib-3.2.5.jar es 295KB / código fuente )
  • Depende de ASM
  • Nivel alto
  • Característica completa (Generación Bytecode)
  • Poco o ningún conocimiento de código de bytes de Java necesario
  • Fácil de aprender
  • Biblioteca esotérica

A pesar del hecho de que puede leer la información de las clases y que puede transformar las clases, la biblioteca parece ajustada a los servidores proxy. El tutorial tiene que ver con beans para los proxies, e incluso menciona que es usado por "marcos de acceso a datos para generar objetos proxy dinámicos e interceptar el acceso a campos". Sin embargo, no veo ninguna razón por la que no pueda usarlo con el propósito más simple de la manipulación de códigos de bytes en lugar de los proxies.

ByteBuddy

  • Small bin / "Huge" src (en comparación) ( byte-buddy-dep-1.8.12.jar es ~ 2.72 MB / 1.8.12 (zip) es 124.537 MB (exacto))
  • Depende de ASM
  • Nivel alto
  • Característica completa
  • Personalmente, un nombre peculiar para una clase de patrón de servicio (ByteBuddy.class)
  • Poco o ningún conocimiento de código byte de Java necesario
  • Fácil de aprender

En pocas palabras, donde carece de BCEL, ByteBuddy es abundante. Utiliza una clase primaria llamada ByteBuddy que usa el patrón de diseño de servicio. Crea una nueva instancia de ByteBuddy, y esto representa una clase que desea modificar. Cuando haya terminado con sus modificaciones, puede hacer un DynamicType con make() .

En su página web hay un tutorial completo con documentación API. El propósito parece ser para modificaciones de alto nivel. Cuando se trata de métodos, no parece haber nada en el tutorial oficial, ni en un tutorial de terceros, sobre cómo crear un método desde cero, además de delegar un método ( EDITME si sabe dónde se explica esto).

Su tutorial se puede encontrar aquí en su página web . Algunos ejemplos se pueden encontrar here .

Advertencia: opinión por delante

Esta es una excelente biblioteca. El único problema que tengo es crear instancias de una clase llamada ByteBuddy para modificar o crear clases. Personalmente, me gustaría rediseñar esto para tener una clase llamada ByteBuddy como una clase de utilidad estática que puede ser instanciada, y tener una clase llamada ClassData o EmptyClass que le das a una instancia de ByteBuddy para su modificación, de modo que puedas separar la funcionalidad de ByteBuddy Datos que modifica o crea. Hice esto con jCLA usando ClassBuilder como un medio para modificar ClassDefinitions (clase de datos inmutables), excepto que ClassBuilder copia todos los datos de ClassDefinition para modificarlo, y el método build () crea una nueva ClassDefinition inmutable cuando termina con sus modificaciones, y para actualizar la clase anterior, debe invocar explícitamente ClassPool#update(String) donde String es el nombre de clase completo (my.package.MyClass).

Por lo tanto, en lugar de:

DynamicType.Unloaded<?> dynamicType = new ByteBuddy() .subclass(Object.class) .name("example.Type") .make();

Usted tendría:

ByteBuddy service = new ByteBuddy(); DynamicType.Unloaded<?> dynamicType = service.editing(ByteBuddy.emptyClass()) .subclass(Object.class) .name("example.Type") .getCurrentClass();

pero eso es solo yo

Asistente de clase de Java (jCLA)

Tengo mi propia biblioteca de bytecode que estoy construyendo, que se llamará Java Class Assistant, o jCLA para abreviar, debido a otro proyecto en el que estoy trabajando y debido a dichas peculiaridades con Javassist , pero no lo lanzaré a GitHub hasta está terminado, pero el proyecto está actualmente disponible para navegar en GitHub y enviar comentarios, ya que está actualmente en alfa, pero aún es lo suficientemente funcional como para ser una biblioteca de clases básica (actualmente está trabajando en los compiladores; por favor, ¡ayúdenme si pueden!) ser lanzado mucho antes!).

Será bastante simple con la capacidad de leer y escribir archivos de clase en y desde un archivo JAR, así como la capacidad de compilar y descompilar el código de bytes en y desde el código fuente y los archivos de clase.

El patrón de uso general hace que sea bastante fácil trabajar con jCLA, aunque puede tardar un poco en acostumbrarse y aparentemente es bastante similar a ByteBuddy en su estilo de métodos y parámetros de métodos para modificaciones de clase:

import jcla.ClassPool; import jcla.ClassBuilder; import jcla.ClassDefinition; import jcla.MethodBuilder; import jcla.FieldBuilder; import jcla.jar.JavaArchive; import jcla.classfile.ClassFile; import jcla.io.ClassFileOutputStream; public class JCLADemo { public static void main(String... args) { // get the class pool for this JVM instance ClassPool classes = ClassPool.getLocal(); // get a class that is loaded in the JVM ClassDefinition classDefinition = classes.get("my.package.MyNumberPrinter"); // create a class builder to modify the class ClassBuilder clMyNumberPrinter= new ClassBuilder(classDefinition); // create a new method with name printNumber MethodBuilder printNumber = new MethodBuilder("printNumber"); // add access modifiers (use modifiers() for convenience) printNumber.modifier(Modifier.PUBLIC); // set return type (void) printNumber.returns("void"); // add a parameter (use parameters() for convenience) printNumber.parameter("int", "number"); // set the body of the method (compiled to bytecode) // use body(byte[]) or insert(byte[]) for bytecode // insert(String) also compiles to bytecode printNumber.body("System.out.println(/"the number is: /" + number/");"); // add the method to the class // you can use method(MethodDefinition) or method(MethodBuilder) clMyNumberPrinter.method(printNumber.build()); // add a field to the class FieldBuilder HELLO = new FieldBuilder("HELLO"); // set the modifiers for hello; convenience method example HELLO.modifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL); // set the type of this field HELLO.type("java.lang.String"); // set the actual value of this field // this overloaded method expects a VariableInitializer production HELLO.value("/"Hello from /" + getClass().getSimpleName() + /"!/""); // add the field to the class (same overloads as clMyNumberPrinter.method()) clMyNumberPrinter.field(HELLO.build()); // redefine classDefinition = clMyNumberPrinter.build(); // update the class definition in the JVM''s ClassPool // (this updates the actual JVM''s loaded class) classes.update(classDefinition); // write to disk JavaArchive archive = new JavaArchive("myjar.jar"); ClassFile classFile = new ClassFile(classDefinition); ClassFileOutputStream stream = new ClassFileOutputStream(archive); try { stream.write(classFile); } catch(IOException e) { // print to System.out } finally { stream.close(); } } }

( Especificación de producción de VariableInitializer para su conveniencia. )

Como se puede ClassDefinition fragmento de ClassDefinition anterior, cada ClassDefinition es inmutable. Esto hace que jCLA sea más seguro, seguro para subprocesos, seguro para la red y fácil de usar. El sistema gira principalmente en torno a ClassDefinitions como el objeto de elección para consultar información sobre una clase de una manera de alto nivel, y el sistema está construido de tal manera que ClassDefinition se convierte a los tipos de destino, como ClassBuilder y ClassFile.

jCLA utiliza un sistema en niveles para los datos de clase. En la parte inferior, tiene el ClassFile inmutable: una estructura o representación de software de un archivo de clase. Luego tienes ClassDefinition s inmutables que se convierten de ClassFiles en algo menos críptico y más manejable y útil para el programador que está modificando o leyendo datos de la clase, y es comparable a la información a la que se accede a través de java.lang.Class . Finalmente, tienes ClassBuilder s mutable. El ClassBuilder es cómo se modifican o crean las clases. Permite que pueda crear una ClassDefinition directamente desde el constructor desde su estado actual. No es necesario crear un nuevo constructor para cada clase, ya que el método reset() borrará las variables.

(El análisis de esta biblioteca estará disponible tan pronto como esté listo para su lanzamiento).

Pero hasta entonces, a partir de hoy:

  • Pequeño (src: 227.704 KB exacto, 2/2/2018)
  • Autosuficiente (sin dependencias, excepto la biblioteca enviada de Java)
  • Nivel alto
  • No se requiere conocimiento del archivo de código de bytes o clase de Java (para API de nivel 1, por ejemplo, ClassBuilder, ClassDefinition, etc.)
  • Fácil de aprender (incluso más fácil si viene de ByteBuddy)

Sin embargo, todavía recomiendo aprender sobre el código de bytes de Java. Hará la depuración más fácil.

Comparación

Considerando todos estos análisis (excluyendo jCLA por ahora), el marco más amplio es ASM, el más fácil de usar es Javassist, la implementación más simple es BCEL, y el nivel más alto para la generación de códigos de bytes y proxies es cglib.

Nota: si me perdí una biblioteca, edite esta respuesta o mencione en un comentario.

Existen algunos marcos para la generación dinámica de código de bytes, la manipulación y el tejido (BCEL, CGLIB, javassist, ASM, MPS). Quiero aprender sobre ellos, pero como no tengo mucho tiempo para conocer todos los detalles acerca de todos ellos, me gustaría ver una especie de tabla comparativa que indique las ventajas y desventajas de uno frente a otro y una explicación de por qué.

Aquí en SO, encontré muchas preguntas que hacían algo similar, y las respuestas normalmente decían "puedes usar cglib o ASM", o "javassist es mejor que cglib", o "BCEL es viejo y está muriendo" o "ASM es Lo mejor porque da X e Y ". Estas respuestas son útiles, pero no responden completamente la pregunta en el ámbito que deseo, comparándolas más profundamente y brindando las ventajas y desventajas de cada una.


En primer lugar, todo depende de su tarea. ¿Desea generar el nuevo código o analizar el bytecode existente y qué tan complejo análisis puede necesitar? También cuánto tiempo desea invertir para aprender el código de bytes de Java. Puede dividir el marco de bytecode en los que proporcionan una API de alto nivel, que le permite alejarse de aprender los códigos de bajo nivel y los elementos internos de JVM (por ejemplo, javaassist y CGLIB) y los marcos de bajo nivel cuando necesita entender JVM o usar algún código de bytes. Herramientas de generación (ASM y BCEL). Para el análisis, BCEL evolucionó históricamente un poco más, pero ASM proporciona una funcionalidad decente que es fácil de ampliar. Además, tenga en cuenta que ASM es probablemente el único marco que proporciona el soporte más avanzado para la información STACK_MAP requerida por el nuevo verificador de bytecode habilitado de forma predeterminada en Java 7.


Si su interés en la generación de códigos de bytes es solo para usarlo, el cuadro de comparación se vuelve bastante simple:

¿Necesitas entender el bytecode?

para javassist: no

para todos los demás: si

Por supuesto, incluso con javassist puede enfrentarse a conceptos de código de bytes en algún momento. Del mismo modo, algunas de las otras bibliotecas (como ASM) tienen un api y / o soporte de herramientas de más alto nivel para protegerlo de muchos de los detalles del código de bytes.

Lo que realmente distingue a javassist, sin embargo, es la inclusión de un compilador java básico. Esto hace que sea muy fácil escribir transformaciones de clase complejas: solo tiene que colocar un fragmento java en una cadena y usar la biblioteca para insertarlo en puntos específicos del programa. El compilador incluido construirá el bytecode equivalente, que luego se inserta en las clases existentes.