Pergunta

Apenas por curiosidade, existem projetos de código aberto (estável) para geração de código Java em tempo de execução diferente do CGLIB? E por que devo usá -los?

Foi útil?

Solução

ASM

O CGLIB e quase todas as outras bibliotecas são construídos sobre o ASM, que por si só age em um nível muito baixo. Este é um show-spopper para a maioria das pessoas, pois você precisa entender o código de bytes e um pouco do JVMS para usá -lo corretamente. Mas dominar o ASM é certamente muito interessante. Observe que, embora exista um excelente Guia ASM 4, em alguma parte da API, a documentação Javadoc pode ser muito concisa se estiver presente, mas está sendo melhorada. Ele segue de perto as versões da JVM para suportar novos recursos.

No entanto, se você precisar de controle total, o ASM é sua arma de escolha.

Este projeto vê atualizações regulares; No momento desta edição, a versão 5.0.4 foi lançada em 15 de maio de 2015.

Byte Buddy

Byte Buddy é uma biblioteca bastante nova, mas fornece qualquer funcionalidade que o CGLIB ou o Javassist fornece e muito mais. O Byte Buddy pode ser totalmente personalizado até o nível de código de bytes e vem com um idioma específico de domínio expressivo que permite um código muito legível.

  • Ele suporta todas as versões da JVM ByteCode, incluindo alterações semânticas Java 8 de alguns códigos de OPC em relação aos métodos padrão.
  • BYTEBUDDY não parece sofrer com as desvantagens que outras bibliotecas têm
  • Altamente configurável
  • Muito rápido (referência código)
  • Tipo API fluente seguro
  • Digite retornos de chamada seguros

    Conselhos de javassistas ou código de instrumentação personalizado é baseado em código em uma planície String Assim, a verificação e a depuração do tipo são impossíveis nesse código, enquanto o ByTeBuddy permite escrever aqueles com Java Pure, portanto, aplica verificações do tipo e permite a depuração.

  • Anotação acionada (flexível)

    Os retornos de chamada do usuário podem ser configurados com anotações, permitindo receber os parâmetros desejados no retorno de chamada.

  • Disponível como agente

    O Builador de Agentes Nifty permite que o BYTEBUDDY seja usado como agente puro ou como agente de anexo. Permite tipo diferente

  • Muito bem documentado
  • Muito exemplo
  • Código limpo, ~ 94% de cobertura de teste
  • Suporte Android Dex

Talvez a principal desvantagem, a API seja um pouco detalhada para um iniciante, mas foi projetado como uma API opt-in em forma de DSL de geração de proxy; Não há padrões mágicos ou questionáveis. Ao manipular o código de bytes, é provavelmente a escolha mais segura e mais razoável. Também com vários exemplos e um grande tutorial, esse não é um problema real.

Em outubro de 2015, esses projetos receberam o Prêmio Oracle Duke's Choice. Nesse momento, apenas chegou ao 1.0.0 Milestone, o que é uma conquista.

Observe que substituiu Cglib por byte buddy Na versão 2.1.0.

Javassist

O Javadoc de Javassist é muito melhor que o do CGLIB. A API de engenharia de classe está OK, mas o Javassist também não é perfeito. Em particular, o ProxyFactory qual é o equivalente ao CGLIB Enhancer Sofra de algumas desvantagens também, apenas para listar alguns:

  • O método da ponte não é totalmente suportado (ou seja, o que é gerado para os tipos de retorno covariante)
  • ClassloaderProvider é um campo estático, então se aplica a todas as instâncias dentro do mesmo carregador de classe
  • A nomeação personalizada poderia ter sido bem -vinda (com verificações de potes assinados)
  • Não há ponto de extensão e quase todos os métodos de interesse são privados, o que é complicado se queremos mudar algum comportamento
  • Enquanto o Javassist oferece suporte a atributos de anotação nas classes, eles não são apoiados em ProxyFactory.

No lado orientado para aspectos, pode-se injetar código em um proxy, mas essa abordagem no Javassist é limitada e um pouco propensa a erros:

  • O código de aspecto é escrito em uma string java simples que é compilado em códigos de opções
  • Sem verificação de tipo
  • Sem genéricos
  • Sem Lambda
  • Sem boxe automático

Javassist também é reconhecido como mais lento que o CGLIB. Isso se deve principalmente à sua abordagem de leitura de arquivos da classe, em vez de ler classes carregadas, como o CGLIB. E a implementação É difícil ler por si só para ser justo; Se alguém precisar fazer alterações no código Javassist, há muitas chances de quebrar alguma coisa.

Javassist também sofria de inatividade, sua mudança para Github por volta de 2013 Parece ter se mostrado útil, pois mostra compromissos regulares e solicitações da comunidade.

Essas limitações ainda estão na versão 3.17.1. A versão foi atingida na versão 3.20.0, mas parece que o Javassist ainda pode ter problemas com o suporte ao Java 8.

Jitescript

O Jitescript parece um novo pedaço de Modelly Modely Up DSL para o ASM, isso é baseado na versão mais recente do ASM (4.0). O código parece limpo.

Mas O projeto ainda está em sua tenra idade, para que a API / comportamento possa mudar, além da documentação é terrível. E atualiza escasso se não for abandonado.

Proxetta

Esta é uma ferramenta bastante nova, mas oferece o melhor melhor humano API. Permite diferentes tipos de proxies, como proxies de subclasse (abordagem CGLIB) ou tecelagem ou delegação.

Embora este seja bastante raro, não existe informação se funcionar bem. Há tantos casos de canto para lidar ao lidar com o bytecode.

Aspecto

Aspectoj é uma ferramenta muito poderosa para Programação Orientada a Aspectos (só). AspectJ manipula o código de bytes para atingir seus objetivos, de modo que você possa alcançar seus objetivos com ele. No entanto, isso requer manipulação em tempo de compilação; Spring Offer Weaving no tempo de carregamento por meio de um agente desde a versão 2.5, 4.1.x.

Cglib

Uma palavra sobre o CGLIB que foi atualizada desde que essa pergunta foi feita.

O CGLIB é bastante rápido, é uma das principais razões pelas quais ainda existe, junto com o fato de o CGLIB funcionar quase melhor do que qualquer alternativa até agora (2014-2015).

De um modo geral, as bibliotecas que permitem a reescrita das aulas no tempo de execução precisam evitar o carregamento de qualquer tipo antes que a classe correspondente seja reescrita. Portanto, eles não podem usar a API de reflexão Java, que exige que qualquer tipo usado na reflexão seja carregado. Em vez disso, eles precisam ler os arquivos da classe via IO (que é um quebra-desempenho). Isso torna, por exemplo, o Javassist ou o proxetta significativamente mais lento que o CGLIB, que simplesmente lê os métodos através da API de reflexão e os substitui.

No entanto, o CGLIB não está mais em desenvolvimento ativo. Houve lançamentos recentes, mas essas mudanças foram vistas como insignificantes por muitas e a maioria das pessoas nunca atualizou para a versão 3 desde que o CGLIB introduziu alguns Bugs graves No último lançamento, o que realmente não aumentou a confiança. Versão 3.1 Corrigido muitos dos problemas da versão 3.0 (desde a versão 4.0.3 Spring Framework reembalagens Versão 3.1).

Além disso, o código -fonte do CGLIB é de um pouco má qualidade de modo que não vemos novos desenvolvedores ingressando no projeto CGLIB. Para uma impressão da ativação do CGLIB, consulte o seu Lista de correspondência.

Observe que após um Proposição na lista de discussão de Guice, CGLIB está agora disponível em Github Para permitir que a comunidade ajude melhor o projeto, ele parece estar funcionando (múltiplas comissões e solicitações de puxar, IC, Maven atualizado), mas a maioria das preocupações ainda permanece.

No momento, há funcionando na versão 3.2.0 e eles estão focando nos esforços no Java 8, mas até agora os usuários que desejam que o suporte do Java 8 tenha que usar truques no tempo de construção. Mas o progresso é muito lento.

E o CGLIB ainda é conhecido por ser atormentado para vazamento de memória de permgen. Mas outros projetos podem não ter sido testados por batalha por tantos anos.

Compilar Processamento de anotação de tempo

Este não é tempo de execução, é claro, mas é uma parte importante do ecossistema, e a maioria do uso de geração de código não precisa de criação de tempo de execução.

Isso começou com o Java 5 que acompanha a ferramenta de linha de comando separada para processar anotações: apt, e a partir do processamento de anotação Java 6 é integrado ao compilador Java.

Em algum momento, você foi obrigado a passar explicitamente o processador, agora com o ServiceLoader abordagem (basta adicionar este arquivo META-INF/services/javax.annotation.processing.Processor para o frasco) o compilador pode detectar automaticamente o processador de anotação.

Essa abordagem na geração de códigos também tem desvantagens, é necessário muito trabalho e compreensão do idioma Java, não bytecode. Essa API é um pouco pesada e, como é um plug -in no compilador, é preciso tomar extremo cuidado para tornar esse código a mensagem de erro mais resiliente e amigável.

A maior vantagem aqui é que ela evita outra dependência em tempo de execução, você pode evitar o vazamento de memória permgen. E um tem controle total sobre o código gerado.

Conclusão

Dentro 2002 O CGLIB definiu um novo padrão para manipular o bytecode com facilidade. Muitas ferramentas e metodologia (IC, cobertura, TDD, etc.) que hoje não estavam disponíveis ou não estão maduras naquele momento. O CGLIB conseguiu ser relevante por mais de uma década; Essa é uma conquista bastante decente. Era rápido e com uma API fácil de usar do que manipular diretamente OPCodes.

Ele definiu um novo padrão em relação à geração de códigos, mas hoje em dia não é mais porque o ambiente e os requisitos mudaram; portanto, os padrões e objetivos também têm os padrões.

A JVM mudou e mudará nas versões recentes e futuras Java (7/8/9/10) (Invokedynamic, métodos padrão, tipos de valor etc.). O ASM atualizou sua API e internos regularmente para seguir essas mudanças, mas o CGLIB e outros ainda não os usam.

Embora o processamento da anotação esteja ficando tração, não é tão flexível quanto a geração de tempo de execução.

A partir de 2015, Byte Buddyenquanto novo em cena - Ofereça o mais atraente vendendo Pontos para a geração de tempo de execução. Uma taxa de atualização decente e o autor possui um conhecimento íntimo dos internos do código Java Bytes.

Outras dicas

Javassist.

Se você precisar fazer proxies, dê uma olhada Commons-Proxy - Ele usa CGLIB e Javassit.

Eu prefiro cru ASM, que acredito ser usado pelo CGLIB de qualquer maneira. É baixo nível, mas a documentação é brilhante, e depois de se acostumar, você estará voando.

Para responder à sua segunda pergunta, você deve usar a geração de código quando sua reflexão e proxies dinâmicos estão começando a se sentir um pouco unidos e você precisa de uma solução sólida de rocha. No passado, eu até adicionei uma etapa de geração de código no processo de construção no Eclipse, efetivamente me dando relatórios de tempo de compilação de tudo e qualquer coisa.

Eu acho que é mais senso de usar Javassist em vez de cglib. Por exemplo, o Javasist trabalha perfeitamente com frascos assinados, diferentemente do CGLIB. Além disso, um grande projeto de hibernação decidiu parar de usar o cglib a favor do Javassist.

O CGLIB foi projetado e implementado há mais de dez anos na ERA AOP e ORM. Atualmente, não vejo motivos para usá -lo e não mantenho mais essa biblioteca (exceto as correções de bugs para meus aplicativos legados). Na verdade, todos os casos de uso do CGLIB que eu já vi são anti -padrões na programação moderna. Deve ser trivial implementar a mesma funcionalidade por meio de qualquer linguagem de script JVM, por exemplo, Groovy.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top