1. Alguns conceitos básicos
Antes de começarmos, precisamos declarar uma coisa importante: não estamos discutindo as anotações que são processadas por meio de mecanismos de reflexão em tempo de execução, mas discutindo as anotações que são processadas no momento da compilação.
Qual é a diferença entre anotações de tempo de compilação e anotações em tempo de execução? De fato, não é grande, principalmente por causa de problemas de desempenho. As anotações de tempo de execução dependem principalmente inteiramente da reflexão, e a eficiência da reflexão é mais lenta que a dos nativos; portanto, haverá algum atraso nas máquinas com menos memória e CPU ruim. No entanto, a anotação não terá esse problema, porque em nosso processo de compilação (classe Java->), algumas marcas de anotação são usadas para gerar dinamicamente algumas classes ou arquivos, por isso não tem nada a ver com nossa operação APK, portanto, naturalmente não há problema de desempenho. Portanto, se um projeto de código aberto mais famoso usa a função de anotação, geralmente usa anotação no tempo de compilação.
O processador de anotação é uma ferramenta que vem com o JAVAC, usado para digitalizar e processar informações de anotação durante o período de compilação. Você pode registrar seu próprio processador de anotação para determinadas anotações. Aqui, presumo que você já entenda o que são anotações e como personalizá -las. Se você ainda não entendeu as anotações, pode verificar a documentação oficial. Os processadores de anotação já existiam no Java 5, mas não havia API disponível até o Java 6 (lançado em dezembro de 2006). Demorou um pouco para que os usuários do Java percebessem o poder do processador de anotação. Portanto, só se tornou popular nos últimos anos.
Um processador com uma anotação específica pega o código -fonte Java (ou bytecode compilado) como entrada e, em seguida, gera alguns arquivos (geralmente arquivos .java) como saída. O que isso significa? Você pode gerar código Java! Esses códigos Java estão no arquivo .java gerado. Portanto, você não pode alterar a classe Java existente, como adicionar um método. Esses arquivos Java gerados serão compilados pelo JAVAC como outros código -fonte Java escrito manualmente.
O processamento da anotação é executado no estágio de compilação. Seu princípio é ler o código -fonte Java, analisar as anotações e, em seguida, gerar um novo código Java. O código Java recém -gerado é finalmente compilado no Java Bytecode. O processador de anotação não pode alterar a classe de leitura Java, como adicionar ou excluir métodos Java.
2. AbstractProcessor
Vamos dar uma olhada na API do processador. Todos os processadores herdam o abstratoProcessor, como mostrado abaixo:
pacote com.example; importar java.util.LinkedHashSet; importar java.util.set; importar javax.annotation.processing.abstractprocessor; importar javax.annotation.processing.processingenvironment; import javax.annotation.processing.mesturenvironment; javax.annotation.processing.supportedannotationTypes; import javax.annotation.processing.supporttedsourceversion; importar javax.lang.model.sourceversion; importRonstion; estende o typeElement> anúncios, RoundEnvironment Env) {return false; } @Override Public Set <String> getSupportEdannotationTypes () {set <string> ANNOTATAIONS = new LinkedHashSet <String> (); AnoTataions.add ("com.example.myannotation"); Retornar Annotataions; } @Override public SourceVersion getSupportDSourCeverSion () {return sourceverversion.latestSupported (); } @Override public sincronizado void init (ProcessingEnvironment Processingenv) {super.init (Processingenv); }}init (ProcessingEnvironment Processingenv): Todas as classes do processador de anotação devem ter um construtor sem parâmetros. No entanto, existe um método especial init (), chamado pela ferramenta de processamento de anotação, tomando processamento de ambiente como um parâmetro. ProcessingEnvironment fornece alguns elementos práticos de classes de ferramentas, tipos e arquivadores. Nós os usaremos mais tarde.
process(Set<? extends TypeElement> annoations, RoundEnvironment env) : esse é semelhante ao método principal () de cada processador. Você pode codificar e implementar a digitalização, o processamento de anotações e a geração de arquivos Java neste método. Usando o parâmetro RoundEnvironment, você pode consultar elementos anotados com uma determinada anotação (texto original: você pode consultar elementos anotados com uma determinada anotação). Veremos os detalhes mais tarde.
getSupportedAnnotationTypes(): Neste método, você deve especificar quais anotações devem ser registradas pelo processador de anotação. Observe que seu valor de retorno é uma coleção de string que contém o nome completo do tipo de anotação que seu processador de anotação deseja processar. Em outras palavras, você define aqui quais anotações seu processador de anotação vai lidar.
getSupportedSourceVersion() : usado para especificar a versão Java que você usa. Geralmente, você deve devolver SourceVersion.latestSupported() . No entanto, se você tiver motivos suficientes para seguir o Java 6, também poderá devolver SourceVersion.RELEASE_6 . Eu recomendo usar SourceVersion.latestSupported() . No Java 7, você também pode usar anotações para substituir a reescrita getSupportedAnnotationTypes() e getSupportedSourceVersion() , como mostrado abaixo:
@SupporttedSourCeverSion (value = fonteversion.release_7) @supportedannotationTypes ({// conjunto de nomes completos de anotação do avaliação "com.example.myannotation", "com.example.anotherannotation"}) public MyProcesstor (extensor <socessort) abstrocessort <exemple.anotherannotation "}) public MyProcessor (extensor, abstrocessort ( @ @ @"}) public boneendats (setrendspacession), que seprossProcessort <exemple.avernotation ") seripsetprocessort ( @sepra. Anúncios, Roundenvironment Env) {return false; } @Override public sincronizado void init (ProcessingEnvironment Processingenv) {super.init (Processingenv); }} Devido a problemas de compatibilidade, especialmente para o Android, recomendo reescrever getSupportedAnnotationTypes() e getSupportedSourceVersion() em vez de usar @SupportedAnnotationTypes e @SupportedSourceVersion .
A próxima coisa que você deve saber é: o processador de anotação é executado em sua própria JVM. Sim, você leu certo. O JAVAC inicia uma máquina virtual Java completa para executar o processador de anotação. O que isso significa? Você pode usar qualquer coisa que usa em um programa Java normal. Usando goiaba! Você pode usar ferramentas de injeção de dependência, como a Dagger ou qualquer outra biblioteca de classes que deseja usar. Mas não se esqueça que, mesmo que seja apenas um pequeno processador, você deve prestar atenção ao uso de algoritmos e padrões de design eficientes, assim como você faz no desenvolvimento de outros programas Java.
3. Registre seu processador
Você pode perguntar "como registrar meu processador de anotação no JAVAC?". Você deve fornecer um arquivo .jar. Assim como outros arquivos .jar, você empacota seu processador de anotação já compilado nesse arquivo. E, no seu arquivo .jar, você deve empacotar um arquivo especial javax.annotation.processing.processor no diretório meta-inf/serviços. Então, sua estrutura de diretório de arquivos .jar se parece com o seguinte:
Myprocess.jar -com -example -myprocess.class -meta -inf -services -javax.annotation.processing.processor
O conteúdo do arquivo javax.annotation.processing.processor são uma lista e cada linha é o nome completo de um processador de anotação. Por exemplo:
com.example.myProcess
com.example.anotherprocess
4. Exemplo: modelo de fábrica
O problema que queremos resolver é: queremos implementar uma loja de pizzas, que fornece aos clientes duas pizza (Margherita e Calzone), bem como a sobremesa tiramisu (Tiramisu).
Public Interface Meal {public float getPrice ();} public class Margheritapizza implementa a refeição {@Override public float getPrice () {return 6.0f; }} classe pública calzonepizza implementa a refeição {@Override public float getPrice () {return 8.5f; }} classe pública Tiramisu implementa a refeição {@Override public float getPrice () {return 4.5f; }} classe pública Pizzastore {Public Meal Order (String Mealname) {if (null == Mealname) {tire nova ilegalArgumentException ("Nome da refeição é nulo!"); } if ("margherita" .equals (nome da refeição)) {return new margheritapizza (); } if ("calzone" .equals (nome da refeição)) {return new calzonepizza (); } if ("tiramisu" .equals (nome da refeição)) {return New Tiramisu (); } Jogue nova ilegalArgumentException ("refeição desconhecida '" + Mealname + "'"); } private static string readconsole () {scanner scanner = new scanner (system.in); String fare = scanner.NextLine (); scanner.close (); refeição de volta; } public static void main (string [] args) {System.out.println ("Welcome to Pizza Store"); Pizzastore pizzastore = new pizzastore (); Refeição refeição = pizzastore.order (readconsole ()); System.out.println ("Bill: $" + refeição.getprice ()); }}Como você pode ver, no método Order (), temos muitas declarações de julgamento da Condição. E, se adicionarmos uma nova pizza, precisamos adicionar um novo julgamento condicional. Mas espere, usando o processador de anotação e o modo de fábrica, podemos ter um processador de anotação gerá -los. Dessa forma, o código que queremos se parece com o seguinte:
classe pública pizzastore {fábrica de refeições privadas = nova refeição (); Ordem de refeição pública (nome da corda) {return factory.create (nome da refeição); } private static string readconsole () {scanner scanner = new scanner (system.in); String fare = scanner.NextLine (); scanner.close (); refeição de volta; } public static void main (string [] args) {System.out.println ("Welcome to Pizza Store"); Pizzastore pizzastore = new pizzastore (); Refeição refeição = pizzastore.order (readconsole ()); System.out.println ("Bill: $" + refeição.getprice ()); }} public class Mealfactory {Public Meal Create (String ID) {if (id == null) {lança nova ilegalargumentException ("ID é nulo!"); } if ("calzone" .equals (id)) {return new calzonepizza (); } if ("tiramisu" .equals (id)) {return new Tiramisu (); } if ("margherita" .equals (id)) {retorna new margheritapizza (); } lança nova ilegalArgumentException ("desconhecido id =" + id); }}5. Anotação @Factory
Você pode imaginar que planejamos usar o processador de anotação para gerar a aula de refeições. De maneira mais geral, queremos fornecer uma anotação e um processador para gerar classes de fábrica.
Vamos dar uma olhada na anotação @Factory:
@Target (elementType.type) @retention (retentionpolicy.class) public @interface factory { / ** * o nome da fábrica * / classe <?> Type (); / ** * O identificador para determinar qual item deve ser instanciado */ string id ();}A idéia é a seguinte: anotemos essas classes de alimentos, usamos o tipo () para indicar a qual fábrica essa classe pertence e usamos id () para indicar o tipo específico dessa classe. Vamos aplicar a anotação @Factory a essas classes:
@Factory (type = margheritapizza.class, id = "margherita") classe pública margheritapizza implementa refeição {@Override public float getPrice () {return 6.0f; }} @Factory (type = calzonepizza.class, id = "calzone") classe pública calzonepizza implementa refeição {@Override public float getPrice () {return 8.5f; }} @Factory (type = tiramisu.class, id = "tiramisu") classe pública Tiramisu implementa refeição {@Override public float getPrice () {return 4.5f; }}Você pode perguntar: podemos apenas aplicar a anotação @Factory na interface da refeição? A resposta é não, porque a anotação não pode ser herdada. Ou seja, se houver anotações na classe X, a classe Y se estende por X, então a classe Y não herdará as anotações na classe X. Antes de escrevermos um processador, precisamos esclarecer algumas regras:
Processador de anotação:
public class FactoryProcessor estende o abstrocacessor {Tipos privados typeUtils; elementos privados elementutils; arquivador privado; Messager privado Messager; mapa privado <string, factoryGroupEdClasses> FactoryClasses = new LinkedHashmap <String, FactoryGroupedClasses> (); @Override Public sincronizado void init (ProcessingEnvironment Processingenv) {super.init (Processingenv); typeUtils = processingenv.getTypeutils (); ElementUtils = Processingenv.getElementUtils (); filer = processingenv.getfiler (); Messager = Processingenv.getMessager (); } @Override Public boolean Process (set <? Extends typeElement> arg0, redondenvironment arg1) {... return false; } @Override Public Set <String> getSupportEdannotationTypes () {set <string> ANNOTATAIONS = new LinkedHashSet <String> (); ANNOTATAIONS.ADD (Factory.class.getCanonicalName ()); Retornar Annotataions; } @Override public SourceVersion getSupportDSourCeverSion () {return sourceverversion.latestSupported (); }} No método getSupportedAnnotationTypes() , especificamos que a anotação @Factory será processada por esse processador.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.