A seconda delle tue esigenze particolari, in alcuni casi il meccanismo del caricatore di servizi Java potrebbe raggiungere ciò che cerchi.
In breve, consente agli sviluppatori di dichiarare esplicitamente che una classe subclasse qualche altra classe (o implementa qualche interfaccia) elencandola in un file nella directory del file JAR / WAR META-INF/services. Può quindi essere scoperto usando la java.util.ServiceLoaderclasse che, quando viene dato un Classoggetto, genererà istanze di tutte le sottoclassi dichiarate di quella classe (o, se ilClass rappresenta un'interfaccia, tutte le classi che implementano quell'interfaccia).
Il vantaggio principale di questo approccio è che non è necessario scansionare manualmente l'intero percorso di classe per sottoclassi: tutta la logica di rilevamento è contenuta nella ServiceLoaderclasse e carica solo le classi dichiarate esplicitamente nella META-INF/servicesdirectory (non tutte le classi sul percorso di classe) .
Vi sono, tuttavia, alcuni svantaggi:
- Non troverà tutte le sottoclassi, solo quelle dichiarate esplicitamente. Pertanto, se è necessario trovare davvero tutte le sottoclassi, questo approccio potrebbe non essere sufficiente.
- Richiede allo sviluppatore di dichiarare esplicitamente la classe nella
META-INF/servicesdirectory. Questo è un onere aggiuntivo per lo sviluppatore e può essere soggetto a errori.
- Il
ServiceLoader.iterator()genera istanze della sottoclasse, non la loroClass oggetti. Ciò causa due problemi:
- Non si può dire nulla su come sono costruite le sottoclassi: il costruttore no-arg viene utilizzato per creare le istanze.
- Come tale, le sottoclassi devono avere un costruttore predefinito o esplicitamente dichiarare un costruttore no-arg.
Apparentemente Java 9 affronterà alcune di queste carenze (in particolare quelle relative all'istanza delle sottoclassi).
Un esempio
Supponiamo che tu sia interessato a trovare classi che implementano un'interfaccia com.example.Example:
package com.example;
public interface Example {
public String getStr();
}
La classe com.example.ExampleImplimplementa tale interfaccia:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Dichiareresti che la classe ExampleImplè un'implementazione di Examplecreando un file META-INF/services/com.example.Examplecontenente il testo com.example.ExampleImpl.
Quindi, è possibile ottenere un'istanza di ogni implementazione di Example(inclusa un'istanza di ExampleImpl) come segue:
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.