No JDK, há um conjunto de APIs de compilação relacionadas que podem iniciar o processo de compilação no Java, analisar arquivos de origem Java e obter sua árvore de sintaxe. Este conjunto completo de APIs está incluído nas ferramentas. No entanto, muitos projetos usaram essa API para fazer muitas coisas. Por exemplo, o famoso Lombok usou esta API para modificar a árvore de sintaxe no código -fonte durante o estágio de processamento da anotação. O resultado final é equivalente a inserir um novo código diretamente no arquivo de origem!
Como esse conjunto de APIs não possui documentos relevantes, é difícil de usar. Por exemplo, analise todas as variáveis no código -fonte e imprima -as:
classe pública javaparsser {private estático final string path = "user.java"; Private Javacfilemanager FileManager; Javactool privado Javactool; public javaparsser () {context context = new Context (); FileManager = New Javacfilemanager (contexto, true, charset.defaultcharset ()); javactool = new javactool (); } public void parsejavafiles () {iterable <!-? estende JavafileObject-> arquivos = FileManager.getJavafileObjects (caminho); Javacompiler.compilationTask CompilationTask = javactool.gettask (null, FileManager, null, null, null, arquivos); Javactak javactak = (javactak) compilationTask; tente {iterable <!-? estende compilationunittree-> resultado = javactak.parse (); para (Tree CompilationUnittree: resultado) {Tree.accept (new SourceVisitor (), NULL); }} catch (ioexception e) {e.printStackTrace (); }} classe estática SourceVisitor estende TreesCanner <void, void = ""> {private string currentPackagename = null; @Override public void visitCompilationUnit (nó compilationUnittree, void evite) {return super.visitCompilationUnit (nó, evite); } @Override public void visitVariable (nó VariableTree, void evite) {formatPtrln ("Nome da variável: %s, tipo: %s, tipo: %s, pacote: %s", node.getName (), node.gettype (), node.getkind (), currentpackame); retornar nulo; }} public static void formatptrln (formato de string, objeto ... args) {System.out.println (string.format (formato, args)); } public static void main (string [] args) {new javaparsser (). parsejavafiles (); }} </void,>O código do user.java é o seguinte:
pacote com.ragnarok.javaparsser; importação com.sun.istack.internal.nullable; importar java.lang.Override; public class Usuário {@Nullable Private String foo = "123123"; private foo a; public void userMethod () {} classe estática foo {private string foostring = "123123"; public void foomethod () {}}}O resultado da execução do Javaparsser acima é o seguinte:
Variável: Foo, Annotaion: NullableVariable Nome: Foo, Tipo: String, Kind: Variável, Pacote: com.ragnarok.javaparsserver Variável Nome: A, Tipo: Foo, Kind: Variável, Pacote: com.ragnarok.javaparsser
Aqui, primeiro analisamos o arquivo de origem através do javacompiler.compilationTask e depois usamos o SourceVisitor personalizado (herdado do TreesCanner) para acessar a estrutura do código -fonte. Na classe SourceVisitor, sobrecarregamos o VisitVariable para analisar uma unidade de compilação (arquivo de código -fonte único) e acessamos todas as variáveis. Pode ser visto aqui que não podemos obter o nome totalmente qualificado desse tipo de variável (incluindo o nome do pacote) e só podemos obter o nome simples correspondente. Portanto, a determinação do tipo requer implementação externa para determiná -lo por si só. Por exemplo, você pode gravar o nome do pacote em que a classe está localizada, pesquise recursivamente todo o diretório de código -fonte para rastrear o nome totalmente qualificado de todas as classes e descobrir se a importação contém o tipo correspondente, etc.
Além do método VisitVariable, o TreesCanner também contém um grande número de outros métodos visitxyz. Por exemplo, você pode atravessar todas as importações, definições de método, anotação etc. Para mais específicas, você pode visualizar o código -fonte sobre isso no OpenJDK.
Aqui vamos dar outro exemplo, sobrecarregando o método VisitClass para acessar todas as classes internas e a própria classe:
@OverridePublic void visitClass (nó classTree, void evite) {formatPtrln ("Nome da classe: %s", node.getSimplename ()); para (membro da árvore: node.getMembers ()) {if (membro da instância do variableTree) {variableTree variable = (varibleTree) membro; Lista <!-? estende o anoTationTree-> anotações = variável.getModifiers (). getAnnotações (); if (anotações.size ()> 0) {formatptrln ("variável: %s, anotaion: %s", variable.getName (), anotações.get (0) .getAnnotationType ()); } else {formatptrln ("variável: %s", variable.getName ()); }}} retornar super.visitclass (nó, evite); }Aqui, simplesmente imprimimos o nome da classe e o nome da variável, tipo e tipo de anotação. Execute o código acima, e o resultado é o seguinte:
Nome da classe: UservArable: Foo, Annotaion: NullableVariable: Classe Nome: Foovariable: Foostring
Pode -se observar que imprimimos o nome da classe e as variáveis da classe. No método VisitClass, podemos obter todos os membros da classe através do método GetMembers, incluindo variáveis, métodos, anotação etc., que correspondem a diferentes tipos. Por exemplo, variáveis correspondem ao tipo de variabletree e o método corresponde ao tipo MethodTree.
Em geral, embora o uso não seja particularmente complicado, ele causou grandes obstáculos a usar devido à falta de documentação. E o que estamos introduzindo é apenas uma pequena parte desta API. Continuarei estudando as funções relacionadas desta API no futuro.
O acima é uma compilação dos dados do analisador do JDK para analisar o código -fonte Java. Continuaremos a adicionar informações relevantes no futuro. Obrigado pelo seu apoio a este site!