Um guia para a biblioteca de reflexões

Um guia para a biblioteca de reflexões

1. Introdução

A bibliotecaReflections funciona como um scanner de caminho de classe. Ele indexa os metadados digitalizados e nos permite consultá-los em tempo de execução. Ele também pode salvar essas informações, para que possamos coletá-las e usá-las a qualquer momento durante o projeto, sem precisar verificar novamente o caminho de classe.

Neste tutorial, mostraremos como configurar a bibliotecaReflections e usá-la em nossos projetos Java.

2. Dependência do Maven

Para usarReflections, precisamos incluir sua dependência em nosso projeto:


    org.reflections
    reflections
    0.9.11

Podemos encontrar a versão mais recente da bibliotecaon Maven Central.

3. ConfigurandoReflections

Em seguida, precisamos configurar a biblioteca. The main elements of the configuration are the URLs and scanners.

Os URLs informam à biblioteca quais partes do caminho de classe devem ser verificadas, enquanto os scanners são os objetos que examinam os URLs fornecidos.

Caso nenhum scanner esteja configurado, a biblioteca usaTypeAnnotationsScannereSubTypesScanner como os padrões.

3.1. Adicionando URLs

Podemos configurarReflections fornecendo os elementos de configuração como os parâmetros do construtor varargs ou usando o objetoConfigurationBuilder.

Por exemplo, podemos adicionar URLs instanciandoReflections usandoString representando o nome do pacote, a classe ou o carregador de classes:

Reflections reflections = new Reflections("com.example.reflections");
Reflections reflections = new Reflections(MyClass.class);
Reflections reflections = new Reflections(MyClass.class.getClassLoader());

Além disso, comoReflections tem um construtor varargs, podemos combinar todos os tipos de configurações acima para instanciá-lo:

Reflections reflections = new Reflections("com.example.reflections", MyClass.class);

Aqui, estamos adicionando URLs especificando o pacote e a classe a ser verificada.

Podemos obter os mesmos resultados usandoConfigurationBuilder:

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.example.reflections"))));

Junto com o métodoforPackage(),ClasspathHelper fornece outros métodos, comoforClass()eforClassLoader(), para adicionar URLs à configuração.

3.2. Adicionando scanners

A biblioteca Reflections vem com muitos scanners integrados:

  • FieldAnnotationsScanner – procura as anotações do campo

  • MethodParameterScanner – verifica métodos / construtores, depois indexa parâmetros e retorna anotações de tipo e parâmetro

  • MethodParameterNamesScanner – inspeciona métodos / construtores e, em seguida, indexa nomes de parâmetros

  • TypeElementsScanner – examina campos e métodos, em seguida, armazena o nome totalmente qualificado como uma chave e os elementos como valores

  • MemberUsageScanner – verifica métodos / construtores / usos de campos

  • TypeAnnotationsScanner – procura as anotações de tempo de execução da classe

  • SubTypesScanner – procura porsuper classes e interfaces de uma classe, permitindo uma pesquisa reversa para subtipos

  • MethodAnnotationsScanner – verifica as anotações do método

  • ResourcesScanner – coleta todos os recursos não pertencentes à classe em uma coleção

Podemos adicionar scanners à configuração como parâmetros do construtorReflections '.

Por exemplo, vamos adicionar os dois primeiros scanners da lista acima:

Reflections reflections = new Reflections("com.example.reflections"),
  new FieldAnnotationsScanner(),
  new MethodParameterScanner());

Novamente, os dois scanners podem ser configurados usando a classe auxiliarConfigurationBuilder:

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.example.reflections"))
  .setScanners(new FieldAnnotationsScanner(), new MethodParameterScanner()));

3.3. Adicionando oExecutorService

Além de URLs e scanners,Reflections gives us the possibility to asynchronously scan the classpath by using the ExecutorService.

Podemos adicioná-lo como um parâmetro do construtorReflections ', ou por meio doConfigurationBuilder:

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.example.reflections"))
  .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner())
  .setExecutorService(Executors.newFixedThreadPool(4)));

Outra opção é simplesmente chamar o métodouseParallelExecutor(). Este método configura umFixedThreadPoolExecutorService padrão com um tamanho igual ao número de processadores principais disponíveis.

3.4. Adicionando filtros

Outro elemento importante de configurações é um filtro. A filter tells the scanners what to include, and what to exclude, when scanning the classpath.

Como ilustração, podemos configurar o filtro para excluir a verificação do pacote de teste:

Reflections reflections = new Reflections(new ConfigurationBuilder()
  .setUrls(ClasspathHelper.forPackage("com.example.reflections"))
  .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner())
  .filterInputsBy(new FilterBuilder().excludePackage("com.example.reflections.test")));

Agora, até este ponto, fizemos uma rápida visão geral dos diferentes elementos da configuração deReflections. A seguir, veremos como usar a biblioteca.

4. Consultando usando reflexões

Depois de chamar um dos construtoresReflections, os scanners configurados varrem todos os URLs fornecidos. Então,for each scanner, the library puts the results in Multimap stores. Como resultado, para usarReflections, precisamos consultar esses armazenamentos chamando os métodos de consulta fornecidos.

Vejamos alguns exemplos desses métodos de consulta.

4.1. Subtipos

Vamos começar recuperando todos os scanners fornecidos porReflections:

public Set> getReflectionsSubTypes() {
    Reflections reflections = new Reflections(
      "org.reflections", new SubTypesScanner());
    return reflections.getSubTypesOf(Scanner.class);
}

4.2. Tipos anotados

Em seguida, podemos obter todas as classes e interfaces que implementam uma determinada anotação.

Então, vamos recuperar todas as interfaces funcionais dojava.util.function package:

public Set> getJDKFunctinalInterfaces() {
    Reflections reflections = new Reflections("java.util.function",
      new TypeAnnotationsScanner());
    return reflections.getTypesAnnotatedWith(FunctionalInterface.class);
}

4.3. Métodos anotados

Agora, vamos usarMethodAnnotationsScanner para obter todos os métodos anotados com uma determinada anotação:

public Set getDateDeprecatedMethods() {
    Reflections reflections = new Reflections(
      "java.util.Date",
      new MethodAnnotationsScanner());
    return reflections.getMethodsAnnotatedWith(Deprecated.class);
}

4.4. Construtores anotados

Além disso, podemos obter todos os construtores obsoletos:

public Set getDateDeprecatedConstructors() {
    Reflections reflections = new Reflections(
      "java.util.Date",
      new MethodAnnotationsScanner());
    return reflections.getConstructorsAnnotatedWith(Deprecated.class);
}

4.5. Parâmetros dos métodos

Além disso, podemos usarMethodParameterScanner para encontrar todos os métodos com um determinado tipo de parâmetro:

public Set getMethodsWithDateParam() {
    Reflections reflections = new Reflections(
      java.text.SimpleDateFormat.class,
      new MethodParameterScanner());
    return reflections.getMethodsMatchParams(Date.class);
}

4.6. Tipo de retorno dos métodos

Além disso, também podemos usar o mesmo scanner para obter todos os métodos com um determinado tipo de retorno.

Vamos imaginar que queremos encontrar todos os métodos deSimpleDateFormat que retornamvoid:

public Set getMethodsWithVoidReturn() {
    Reflections reflections = new Reflections(
      "java.text.SimpleDateFormat",
      new MethodParameterScanner());
    return reflections.getMethodsReturn(void.class);
}

4.7. Recursos

Por fim, vamos usarResourcesScanner para procurar um determinado nome de arquivo em nosso classpath:

public Set getPomXmlPaths() {
    Reflections reflections = new Reflections(new ResourcesScanner());
    return reflections.getResources(Pattern.compile(".*pom\\.xml"));
}

4.8. Métodos de consulta adicionais

Os exemplos acima são apenas alguns exemplos que mostram como usar os métodos de consultaReflections'. No entanto, existem outros métodos de consulta que não abordamos aqui:

  • getMethodsWithAnyParamAnnotated

  • getConstructorsMatchParams

  • getConstructorsWithAnyParamAnnotated

  • getFieldsAnnotatedWith

  • getMethodParamNames

  • getConstructorParamNames

  • getFieldUsage

  • getMethodUsage

  • getConstructorUsage

5. IntegrandoReflections em um ciclo de vida de construção

Podemos facilmente integrarReflections em nossa construção Maven usandogmavenplus-plugin.

Vamos configurá-lo para salvar o resultado das varreduras em um arquivo:


    org.codehaus.gmavenplus
    gmavenplus-plugin
    1.5
    
        
            generate-resources
            
                execute
            
            
                
                    
                
            
        
    

Mais tarde, chamando o métodocollect(),we can retrieve the saved resultsand make them available for further use, without having to perform a new scan:

Reflections reflections
  = isProduction() ? Reflections.collect() : new Reflections("com.example.reflections");

6. Conclusão

Neste artigo, exploramos a bibliotecaReflections. Abordamos diferentes elementos de configuração e seus usos. E, finalmente, vimos como integrarReflections no ciclo de vida de construção de um projeto Maven.

Como sempre, o código completo está disponível emGitHub.