Pregunta

Solo por curiosidad, ¿existen proyectos de código abierto (estables) para la generación de código Java en tiempo de ejecución que no sean cglib?¿Y por qué debería usarlos?

¿Fue útil?

Solución

MAPE

CGLIB y casi todas las demás bibliotecas están construidas sobre ASM, que a su vez actúa en un nivel muy bajo.Esto es un obstáculo para la mayoría de las personas, ya que es necesario comprender el código de bytes y un poco del JVMS para utilizarlo correctamente.Pero dominar el ASM es ciertamente muy interesante.Sin embargo, tenga en cuenta que si bien existe una excelente guía ASM 4, en alguna parte de la API, la documentación javadoc puede ser muy concisa, si es que está presente, pero se está mejorando.Sigue de cerca las versiones de JVM para admitir nuevas funciones.

Sin embargo, si necesitas un control total, ASM es tu arma preferida.

Este proyecto recibe actualizaciones periódicas;En el momento de esta edición, la versión 5.0.4 se lanzó el 15 de mayo de 2015.

Compañero de bytes

Byte Buddy es una biblioteca bastante nueva, pero proporciona cualquier funcionalidad que proporcione CGLIB o Javassist y mucho más.Byte Buddy se puede personalizar completamente hasta el nivel de código de bytes y viene con un lenguaje expresivo específico de dominio que permite un código muy legible.

  • Es compatible con todas las versiones de códigos de bytes de JVM, incluidos los cambios semánticos de Java 8 de algunos códigos de operación con respecto a los métodos predeterminados.
  • ByteBuddy no parece sufrir los inconvenientes que tienen otras bibliotecas
  • Altamente configurable
  • Bastante rapido (punto de referencia código)
  • Escriba API fluida y segura
  • Escribe devoluciones de llamada seguras

    Los consejos de Javassist o el código de instrumentación personalizado se basan en código simple String por lo tanto, la verificación de tipos y la depuración son imposibles dentro de este código, mientras que ByteBuddy permite escribirlos con Java puro, por lo tanto, impone verificaciones de tipos y permite la depuración.

  • Basado en anotaciones (flexible)

    Las devoluciones de llamada del usuario se pueden configurar con anotaciones que permitan recibir los parámetros deseados en la devolución de llamada.

  • Disponible como agente

    El ingenioso creador de agentes permite utilizar ByteBuddy como agente puro o como agente adjunto.Permite diferentes tipos

  • Muy bien documentado
  • muchos ejemplos
  • Código limpio, ~94% de cobertura de prueba
  • Soporte Android DEX

Quizás la principal desventaja es que la API es un poco detallada para un principiante, pero está diseñada como una API opcional con forma de DSL de generación de proxy;no hay magia ni valores predeterminados cuestionables.Cuando se manipula código de bytes, esta es probablemente la opción más segura y razonable.Además, con múltiples ejemplos y un gran tutorial, esto no es un problema real.

En octubre de 2015 este proyecto recibió el Premio a la elección de Oracle Duke.En este momento acaba de llegar al hito 1.0.0, lo cual es todo un logro.

Tenga en cuenta que ha reemplazado CGLIB de Byte Buddy en la versión 2.1.0.

Javassist

El javadoc de Javassist es mucho mejor que el de CGLIB.La API de ingeniería de clases está bien, pero Javassist tampoco es perfecto.En particular, el ProxyFactory que es el equivalente al CGLIB Enhancer También sufren algunos inconvenientes, sólo por enumerar algunos:

  • El método puente no es totalmente compatible (es decir, el que se genera para tipos de retorno covariantes)
  • ClassloaderProvider es un campo estático, entonces se aplica a todas las instancias dentro del mismo cargador de clases
  • Los nombres personalizados podrían haber sido bienvenidos (con cheques para los frascos firmados)
  • No existe un punto de extensión y casi todos los métodos de interés son privados, lo cual resulta engorroso si queremos cambiar algún comportamiento.
  • Si bien Javassist ofrece soporte para atributos de anotación en clases, no son compatibles con ProxyFactory.

En el lado orientado a aspectos, se puede inyectar código en un proxy, pero este enfoque en Javassist es limitado y un poco propenso a errores:

  • El código de aspecto está escrito en una cadena Java simple que es compilado en códigos de operación
  • sin verificación de tipo
  • sin genéricos
  • sin lambda
  • sin auto-(des)boxing

También se reconoce que Javassist es más lento que Cglib.Esto se debe principalmente a su enfoque de leer archivos de clase en lugar de leer clases cargadas como lo hace CGLIB.Y el implementación En sí mismo es difícil de leer para ser justo;Si es necesario realizar cambios en el código Javassist, hay muchas posibilidades de romper algo.

Javassist también sufrió de inactividad, su traslado a github alrededor de 2013 Parece haber demostrado ser útil ya que muestra confirmaciones periódicas y solicitudes de extracción de la comunidad.

Estas limitaciones siguen vigentes en la versión 3.17.1.La versión pasó a la versión 3.20.0, pero parece que Javassist aún puede tener problemas con la compatibilidad con Java 8.

JiteScript

JiteScript parece una nueva pieza para dar forma a DSL para ASM, esto se basa en la última versión de ASM (4.0).El código parece limpio.

Pero el proyecto aún se encuentra en su etapa temprana, por lo que la API/el comportamiento pueden cambiar, además la documentación es terrible.Y las actualizaciones son escasas, si no abandonadas.

Proxetta

Esta es una herramienta bastante nueva pero ofrece, con diferencia, la mejor humano API.Permite diferentes tipos de proxies, como proxies de subclase (enfoque cglib) o tejido o delegación.

Aunque este es bastante raro, no existe información sobre si funciona bien.Hay muchos casos extremos con los que lidiar cuando se trata de código de bytes.

AspectoJ

AspectJ es una herramienta muy poderosa para Programación Orientada a Aspectos (solo).AspectJ manipula el código de bytes para lograr sus objetivos de modo que usted pueda lograr sus objetivos con él.Sin embargo, esto requiere manipulación en tiempo de compilación;oferta de primavera tejiendo en el momento de la carga a través de un agente desde la versión 2.5, 4.1.x.

CGLIB

Unas palabras sobre CGLIB que se han actualizado desde que se hizo esa pregunta.

CGLIB es bastante rápido, es una de las razones principales por las que todavía existe, junto con el hecho de que CGLIB funcionó casi mejor que cualquier alternativa hasta ahora (2014-2015).

En términos generales, las bibliotecas que permiten la reescritura de clases en tiempo de ejecución deben evitar cargar cualquier tipo antes de que se reescriba la clase correspondiente.Por lo tanto, no pueden hacer uso de la API de reflexión de Java, que requiere que se cargue cualquier tipo utilizado en la reflexión.En cambio, tienen que leer los archivos de clase a través de IO (lo cual afecta el rendimiento).Esto hace que, por ejemplo, Javassist o Proxetta sean significativamente más lentos que Cglib, que simplemente lee los métodos a través de la API de reflexión y los anula.

Sin embargo, CGLIB ya no se encuentra en desarrollo activo.Hubo lanzamientos recientes, pero muchos consideraron que esos cambios eran insignificantes y la mayoría de las personas nunca actualizaron a la versión 3 desde que CGLIB introdujo algunos errores graves en los últimos lanzamientos lo que realmente no generó confianza. La versión 3.1 solucionó muchos de los problemas de la versión 3.0 (desde la versión 4.0.3 Spring Framework vuelve a empaquetar versión 3.1).

Además, el código fuente CGLIB es bastante mala calidad de modo que no vemos nuevos desarrolladores uniéndose al proyecto CGLIB.Para tener una idea de la actividad del CGLIB, consulte su lista de correo.

Tenga en cuenta que siguiendo un propuesta en la lista de correo de guice, CGLIB ya está disponible en github Para permitir que la comunidad ayude mejor al proyecto, parece estar funcionando (múltiples confirmaciones y solicitudes de extracción, ci, maven actualizado), pero la mayoría de las preocupaciones aún persisten.

En este momento están trabajando en la versión 3.2.0 y están centrando sus esfuerzos en Java 8, pero hasta ahora los usuarios que quieren que Java 8 sea compatible tienen que usar trucos en el momento de la compilación.Pero el progreso es muy lento.

Y todavía se sabe que CGLIB está plagado de pérdidas de memoria de PermGen.Pero es posible que otros proyectos no hayan sido probados en batalla durante tantos años.

Procesamiento de anotaciones en tiempo de compilación

Este no es tiempo de ejecución, por supuesto, pero es una parte importante del ecosistema, y ​​la mayor parte del uso de generación de código no necesita creación en tiempo de ejecución.

Esto comenzó con Java 5 que venía con una herramienta de línea de comandos separada para procesar anotaciones: apt, Y a partir de Java 6, el procesamiento de anotaciones se integra en el compilador de Java.

En algún momento se le requería pasar explícitamente el procesador, ahora con el ServiceLoader enfoque (simplemente agregue este archivo META-INF/services/javax.annotation.processing.Processor al jar) el compilador puede detectar automáticamente el procesador de anotaciones.

Este enfoque en la generación de código también tiene desventajas: requiere mucho trabajo y comprensión del lenguaje Java, no del código de bytes.Esta API es un poco engorrosa y, como se trata de un complemento en el compilador, se debe tener mucho cuidado para que este código sea el mensaje de error más resistente y fácil de usar.

La mayor ventaja aquí es que evita otra dependencia en tiempo de ejecución, lo que puede evitar la pérdida de memoria permanente.Y uno tiene control total sobre el código generado.

Conclusión

En 2002 CGLIB definió un nuevo estándar para manipular el código de bytes con facilidad.Muchas herramientas y metodologías (CI, cobertura, TDD, etc.) que tenemos hoy en día no estaban disponibles o no estaban maduras en ese momento.CGLIB logró ser relevante durante más de una década;Ese es un logro bastante decente.Fue rápido y con una API fácil de usar que manipular códigos de operación directamente.

Definió un nuevo estándar con respecto a la generación de código, pero hoy en día ya no lo es porque el entorno y los requisitos han cambiado, al igual que los estándares y objetivos.

La JVM cambió y cambiará en las versiones recientes y futuras de Java (7/8/9/10) (invokedynamic, métodos predeterminados, tipos de valores, etc.).ASM actualizó su API y sus componentes internos periódicamente para seguir estos cambios, pero CGLIB y otros aún no los han utilizado.

Si bien el procesamiento de anotaciones está ganando terreno, no es tan flexible como la generación en tiempo de ejecución.

A partir de 2015, Compañero de bytesaunque es bastante nuevo en la escena - ofrecer lo más atractivo venta puntos para la generación de tiempo de ejecución.Una tasa de actualización decente y el autor tiene un conocimiento profundo de los aspectos internos del código de bytes de Java.

Otros consejos

Javassist .

Si usted necesita para hacer proxies, echar un vistazo a Commons-proxy - se utiliza tanto CGLIB y Javassit.

ASM , que creo que es utilizado por cglib de todos modos. Es de bajo nivel, pero la documentación es brillante , y una vez que se acostumbre a ella le va a volar.

Para responder a su segunda pregunta, se debe utilizar la generación de código cuando su reflexión y proxies dinámicos están empezando a sentir un poco improvisado y que necesita una solución sólida como una roca. En el pasado he incluso agregaron una etapa de generación de código en el proceso de construcción en Eclipse, dando efectivamente me compilar informes en tiempo de nada y todo.

Creo que es mejor no usar los Javassist en lugar de cglib. P.ej. javasist funciona perfectamente con los tarros firmados a diferencia cglib. Además, tal como gran proyecto Hibernate decidió dejar de usar cglib a favor de Javassist .

CGLIB fue diseñado e implementado hace más de diez años en el AOP y ORM época. Actualmente no veo razones para usarlo y no mantengo esta biblioteca más (salvo correcciones de errores para mis aplicaciones heredadas). En realidad todos CGLIB casos de uso que he visto, son anti-patrones en la programación moderna. Debe ser trivial para implementar la misma funcionalidad a través de cualquier lenguaje de scripting por ejemplo JVM maravilloso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top