AspectD: Uma Solução AOP Eficaz para Flutter - Agatetepê
Open Sourced por Alibaba Xianyu Tech Team
Alibaba Tech em HackerNoon.com Segue Jul 11 · 9 min ler
Este artigo faz parte da série Utilizando Flutter do Alibaba .
Github. AspectD para Flutter
Com o rápido desenvolvimento da estrutura Flutter, mais e mais empresas começam a usar o Flutter para refatorar ou construir novos produtos. No entanto, na prática, descobrimos que, por um lado, o Flutter tem uma alta eficiência de desenvolvimento, excelente desempenho e bom desempenho em várias plataformas. Por outro lado, o Flutter também enfrenta problemas, como plug-ins ausentes ou imperfeitos, recursos básicos e o framework subjacente.
Por exemplo, no processo de implementação de gravação e reprodução automatizadas, descobrimos que o código da estrutura Flutter (nível de dardo) precisa ser modificado para atender aos requisitos durante a reprodução automática da gravação. Isso leva ao risco de o framework se tornar vulnerável a invasões. Para resolver esse problema e reduzir o custo de manutenção no processo de iteração, a primeira solução que consideramos é a Programação Orientada a Aspectos.
AOP (Orientação Orientada a Aspectos) pode inserir dinamicamente o código em um método e posição específicos da classe em tempo de compilação (ou tempo de execução), adicionando recursos ao código existente de forma dinâmica e uniforme, sem modificar o código-fonte.
Mas a questão é, como podemos implementar o AOP para o Flutter? Este artigo enfoca o AspectD, uma estrutura de programação AOP orientada por dardos desenvolvida pela equipe de tecnologia da Xianyu.
AspectD: Framework AOP Orientado a Dardos
Se o recurso AOP é suportado em tempo de execução ou em tempo de compilação, depende das características da própria linguagem. Por exemplo, no iOS, o próprio Objective C fornece poderosos recursos de tempo de execução e dinâmicos, tornando o AOP de tempo de execução fácil de usar. No Android, Java pode não apenas implementar proxies estáticos em tempo de compilação (como AspectJ) com base na modificação de bytecode, mas também implementar proxies dinâmicos de tempo de execução (como Spring AOP) com base em aprimoramentos de tempo de execução.
E quanto ao Dart? Em primeiro lugar, o suporte à reflexão do Dart é fraco. Somente Introspection é suportado, enquanto Modification não é suportado. Em segundo lugar, o Flutter desativa a reflexão quanto ao tamanho e robustez do pacote.
Portanto, projetamos e implementamos uma solução AOP baseada na modificação em tempo de compilação, AspectD.
Cenários típicos de AOP
O código AspectD a seguir ilustra um cenário típico de aplicativo AOP:
aop.dart import 'package:example/main.dart' as app;
void main()=> app.main(); aop_impl.dart import 'package:aspectd/aspectd.dart'; @Aspect()
Projeto de API orientado a desenvolvedores
Design de PointCut
PointCut precisa ser totalmente caracterizado como adicionar a lógica AOP, por exemplo, de que forma (Call / Execute) e para qual biblioteca, qual classe (este item está vazio no caso do método de biblioteca) e qual método.
Estrutura de dados do PointCut:
Ele contém as informações do código-fonte (como o nome da biblioteca, o nome do arquivo e o número da linha), o objeto de chamada do método, o nome da função e as informações de parâmetro.
Por favor, note a anotação @pragma('vm:entry-point') aqui. Sua lógica central é o Tree-Shaking. Na compilação AOT (Ahead of Time), se não puder ser chamado pela entrada Main da aplicação, será descartado como código inútil. A lógica de injeção do código AOP não é invasiva, então, obviamente, ele não será chamado pela entrada principal. Portanto, essa anotação é necessária para dizer ao compilador para não descartar essa lógica.
O método prossar aqui é semelhante ao método ProceedingJoinPoint.proceed () em AspectJ, e a lógica original pode ser chamada chamando o método pointcut.proceed (). O corpo do método prossegue na definição original está vazio e seu conteúdo será gerado dinamicamente no tempo de execução.
Design de Conselhos
O efeito de @pragma("vm:entry-point") aqui é o mesmo descrito acima. O objeto pointCut é passado para o método AOP como um parâmetro, para que os desenvolvedores possam obter informações relevantes sobre a chamada do código-fonte para implementar sua própria lógica ou chamar a lógica original através de pointcut.proceed ().
Design do Aspecto
A anotação Aspect pode permitir que a classe de implementação AOP, como ExecuteDemo, seja facilmente identificada e extraída, e também pode ser usada como um switch. Ou seja, se quisermos desabilitar essa lógica AOP, basta remover a anotação @Aspect.
Compilação do código AOP
Contém a entrada principal no projeto original
Como podemos ver acima, import 'package:example/main.dart' as app; é introduzido em aop.dart, que permite que todo o código de todo o projeto de exemplo seja incluído ao compilar aop.dart.
Compilação no modo de depuração
A introdução da import 'aop_impl.dart'; em aop.dart permite que o conteúdo em aop_impl.dart seja compilado no modo de depuração, mesmo que não seja explicitamente dependente de aop.dart
Compilação no modo de lançamento
Na compilação AOT (no modo Release), a lógica Tree-Shaking faz com que o conteúdo em aop_impl.dart não seja compilado no Dill quando eles não são chamados pela entrada Main no aop. O impacto pode ser evitado adicionando @pragma("vm:entry-point") .
Quando usamos AspectD para escrever o código AOP e geramos produtos intermediários compilando aop.dart, para que Dill contenha o código original do projeto e o código AOP, precisamos considerar como modificá-lo. No AspectJ, as modificações são implementadas por meio de operações no arquivo de classe. No AspectD, ele é implementado ao operar o arquivo Dill.
Operação de endro
O arquivo Dill, também conhecido como Dart Intermediate Language, é um conceito na compilação da linguagem Dart. A compilação de instantâneo de script ou AOT requer Dill como intermediário.
Estrutura do Dill
Podemos usar o dump_kernel.dart fornecido pelo pacote da VM no DART SDK para imprimir a estrutura interna do Dill.
Transformação do Dill
O Dart fornece um método Transformado do Kernel para o Kernel, que pode transformar o Dill através da travessia AST recursiva do arquivo Dill
Com base na anotação AspectD compilada por desenvolvedores, as bibliotecas, classes e métodos que precisam ser adicionados com o código AOP específico podem ser extraídos da parte de transformação de AspectD e, em seguida, recursos, como Chamada / Execução, podem ser implementados por meio de operações em classes de destino durante a recursão de AST.
A seguir, parte de uma lógica típica de transformação:
Atravessando o objeto AST em Dill (a função visitMethodInvocation aqui), combinada com a anotação AspectD escrita por desenvolvedores ( aspectdInfoMap e aspectdItemInfo aqui), podemos transformar o objeto AST original (o methodInvocation aqui) para alterar a lógica original do código, o processo de transformação.
Sintaxe suportada pelo AspectD
Ao contrário dos avanços BeforeAroundAfter fornecidos no AspectJ, apenas uma abstração unificada está disponível no AspectD, que é, Around.
Em termos de modificar o método original internamente, dois tipos, Chamar e Executar, estão disponíveis. O PointCut do primeiro é o ponto de chamada, e o PointCut do segundo é o ponto de execução.
import 'package:aspectd/aspectd.dart'; @Aspect()
Somente Call e Execute são suportados, o que obviamente não é suficiente para o Flutter (Dart). Por um lado, Flutter não permite reflexão. Para dizer o mínimo, mesmo que o Flutter permita a reflexão, ainda não é suficiente e não pode atender às necessidades.
Para um cenário típico, se a classe "y" no arquivo x.dart define um método privado "m" ou uma variável de membro "p" no código de dart a ser injetado, então ele não pode ser acessado em aop_impl.dart, não para mencionar a obtenção de múltiplas propriedades contínuas de variáveis privadas. Por outro lado, pode não ser suficiente apenas operar o método inteiro. Podemos precisar inserir a lógica de processamento no meio do método.
Para resolver esse problema, uma sintaxe, Inject, é projetada em AspectD. Veja o seguinte exemplo:
A biblioteca Flutter contém o seguinte código relacionado a gestos:
Se quisermos adicionar uma lógica de processamento para a instância e o contexto após onTapCancel, Call e Execute não são viáveis. No entanto, com o Inject, apenas algumas instruções simples são necessárias para resolver o problema:
Através da lógica de processamento acima, o método GestureDetector.build em Dill após a compilação é o seguinte:
Além disso, comparado com Call / Execute, o parâmetro de entrada de Inject possui um parâmetro chamado lineNum adicional, que pode ser usado para especificar o número de linha específico da lógica de inserção.
Suporte ao processo de compilação
Embora possamos compilar aop.dart para compilar o código de engenharia original e o código AspectD no arquivo dill e implementar a transformação da hierarquia Dill por meio de Transform para implementar o AOP, o padrão Flutter (flutter tools) não suporta esse processo, então pequenas alterações no processo de construção ainda são necessárias.
No AspectJ, esse processo é implementado pelo Ajc do compilador Java não padrão. Na AspectD, o suporte para AspectD pode ser implementado anexando o Patch a flutter_tools.
Prática e consideração
Com base no AspectD, removemos com êxito todo o código invasivo para a estrutura Flutter e implementamos os mesmos recursos de quando o código intrusivo não foi removido, suportando a gravação e a reprodução de centenas de scripts e a operação estável e confiável da regressão automática .
Do ponto de vista da AspectD, Chamada / Execução pode nos ajudar a implementar facilmente recursos, como rastreamento de desempenho (duração da chamada de métodos chave), aprimoramento de log (obtenção de informações detalhadas sobre o local onde um método é especificamente chamado) e gravação e reprodução Doom. (como o registro de compilação e a reprodução de seqüências numéricas aleatórias). A sintaxe do Inject é mais poderosa. Ele pode implementar a injeção gratuita de lógica por meios semelhantes ao código-fonte e suportar cenários complexos, como gravação de aplicativos e regressão automática (por exemplo, gravação e reprodução de eventos de toque do usuário).
Além disso, o princípio AspectD é baseado na transformação Dill. Com o poder de Dill, os desenvolvedores podem operar livremente em produtos compilados Dart. Além disso, essa transformação tem como alvo objetos AST, que estão próximos ao nível do código-fonte e não são apenas poderosos, mas também confiáveis. Seja a substituição lógica ou a transformação do modelo Json ?>, ela fornece uma nova perspectiva e possibilidade.
Como uma nova estrutura AOP orientada para Flutter desenvolvida pela equipe de tecnologia da Xianyu, a AspectD suporta cenários mainstream de AOP e é de código aberto no Github. AspectD para Flutter
Se você tiver dúvidas ou sugestões durante o uso, sinta-se à vontade para enviar um problema ou uma solicitação de recebimento .
(Artigo original de Wang Kang ? ?)
Da solução de um problema para a publicação no Google Play com o Flutter
Albert Wolszon Blocked Unblock Seguir Seguindo 2 de janeiro Após o momento em que me interessei por Flutter eu estava…
Por que algumas coisas são "difíceis"? (explorando a resistência e sua solução universal)
Cathy Presland Blocked Unblock Seguir Seguindo 2 de dezembro de 2018 Por que algumas coisas parecem realmente muito difíceis? Me…