Dependendo de seus requisitos específicos, em alguns casos, o mecanismo do carregador de serviço do Java pode alcançar o que você procura.
Em resumo, ele permite que os desenvolvedores declarem explicitamente que uma classe subclasse alguma outra classe (ou implementa alguma interface) listando-a em um arquivo no META-INF/servicesdiretório do arquivo JAR / WAR . Em seguida, ele pode ser descoberto usando a java.util.ServiceLoaderclasse que, quando recebe um Classobjeto, gera instâncias de todas as subclasses declaradas dessa classe (ou, se isso Classrepresenta uma interface, todas as classes que implementam essa interface).
A principal vantagem dessa abordagem é que não há necessidade de varrer manualmente todo o caminho de classe em busca de subclasses - toda a lógica de descoberta está contida na ServiceLoaderclasse e apenas carrega as classes explicitamente declaradas no META-INF/servicesdiretório (nem todas as classes no caminho de classe) .
Existem, no entanto, algumas desvantagens:
- Ele não encontrará todas as subclasses, apenas aquelas que são explicitamente declaradas. Como tal, se você precisar realmente encontrar todas as subclasses, essa abordagem poderá ser insuficiente.
- Exige que o desenvolvedor declare explicitamente a classe no
META-INF/servicesdiretório Esse é um ônus adicional para o desenvolvedor e pode estar sujeito a erros.
- O
ServiceLoader.iterator()gera instâncias de subclasse, não seus Classobjetos. Isso causa dois problemas:
- Você não tem nenhuma opinião sobre como as subclasses são construídas - o construtor no-arg é usado para criar as instâncias.
- Como tal, as subclasses devem ter um construtor padrão ou explicitamente declarar um construtor sem argumento.
Aparentemente, o Java 9 abordará algumas dessas deficiências (em particular, as relacionadas à instanciação de subclasses).
Um exemplo
Suponha que você esteja interessado em encontrar classes que implementam uma interface com.example.Example:
package com.example;
public interface Example {
public String getStr();
}
A classe com.example.ExampleImplimplementa essa interface:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Você declararia que a classe ExampleImplé uma implementação Examplecriando um arquivo que META-INF/services/com.example.Examplecontém o texto com.example.ExampleImpl.
Em seguida, você pode obter uma instância de cada implementação de Example(incluindo uma instância de ExampleImpl) da seguinte maneira:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.