2012-04-03 17 views
26

Sono in procinto di sviluppare un'applicazione (programmatore Quartz) in cui abbiamo una classe di lavoro che è responsabile dell'esecuzione effettiva del lavoro e dobbiamo dire/passare il nome della classe di lavoro mentre si crea un trigger nel programmatore Quartz.Trovare tutte le classi che implementano un'interfaccia specifica

Desidero fornire un punto di estensione a tutti coloro che desiderano utilizzare l'API (oltre ad alcuni lavori generici che fornirò come parte dell'API). L'idea è di creare un'interfaccia (marcatore) e se qualcuno vuole dichiarare la propria classe come classe di lavoro schedulatore, tutto ciò che devono fare è, a (dichiarare) di implementare l'interfaccia.

Non sono sicuro di come sia possibile individuare le classi che seguono il contratto (implementando l'interfaccia) in modo che possa mostrarle all'utente che desidera pianificare un trigger nel programma di pianificazione.

Il mio requisito non è caricare le classi in fase di esecuzione ma mostrare l'elenco di utenti delle classi che implementano l'interfaccia richiesta in modo che l'utente possa selezionare la classe e il nome della classe possa essere passato allo scheduler. È lo scheduler Quartz che alla fine sarà responsabile di creare un'istanza di classe.

Qualcuno può suggerire come posso raggiungere l'obiettivo di cui sopra o c'è qualche altro modo migliore per ottenere ciò che sto cercando di fare?

Modifica

ho attraversato il doc di ServiceLoader e sembra che per l'attuazione di un servizio si deve creare un file nella cartella META-INF con il nome della classe di implementazione, che mi porta a pensate che se l'utente della mia API vuole 20 diverse implementazioni, deve inserire 20 voci nel file che per me sembra un sacco di lavoro extra per l'utente finale poiché ogni classe di lavoro verrà creata per l'esecuzione di un lavoro specifico e non ci può essere centinaia di classi di lavoro.

Per favore correggimi se la mia ipotesi è sbagliata.

+2

Un'altra prova è creare un'annotazione e quindi cercare le classi annotate. Qui http://stackoverflow.com/questions/259140/scanning-java-annotations-at-runtime dice come ottenerlo. – polypiel

+0

Questo aiuto? [collegamento] (http://www.javaworld.com/javaworld/javaqa/2003-07/02-qa-0725-classsrc2.html?page=1) – titogeo

risposta

30

ho avuto un bisogno simile in cui ho voluto fare in modo che tutte le classi create che hanno implementato un certo un'interfaccia erano sempre veramente serializzabile. Ho creato un JavaClassFinder che percorre tutte le directory nel classpath e trova tutte le classi assegnabili all'interfaccia interessata. Ecco un frammento di codice:

public <T> List<Class<? extends T>> findAllMatchingTypes(Class<T> toFind) { 
    foundClasses = new ArrayList<Class<?>>(); 
    List<Class<? extends T>> returnedClasses = new ArrayList<Class<? extends T>>(); 
    this.toFind = toFind; 
    walkClassPath(); 
    for (Class<?> clazz : foundClasses) { 
     returnedClasses.add((Class<? extends T>) clazz); 
    } 
    return returnedClasses; 
} 

Sono felice di condividere il codice con voi se aiuta. L'unico inconveniente è che questo gestirà solo i file .class: non ho aggiunto la funzione per decomprimere .jars e leggere i file di classe da lì. (Ma non sarebbe un grande progetto di aggiungere che.)

UPDATE: ho controllato il mio codice sorgente per quanto sopra, e lo abbiamo trovato dipende da un sacco di classi helper nella nostra libreria di utilità standard. Per semplificare, ho compresso tutto il codice necessario, che è possibile scaricare da JavaClassFinder.zip. Questo verrà installato direttamente in Eclipse e potrai prendere qualsiasi parte del codice che ti serve.

Nel progetto è presente un test JUnit3, denominato JavaClassFinderTest.java, che mostra le funzioni e l'utilizzo della classe JavaClassFinder. L'unica dipendenza esterna necessaria per eseguire il test Junit è Junit.

Utilizzo base di questa utility:

JavaClassFinder classFinder = new JavaClassFinder(); 
    List<Class<? extends MyTagInterface>> classes = classFinder.findAllMatchingTypes(MyTagInterface.class); 

Questo vi darà una lista che contiene tutte le classi nel classpath, che sono assegnabili dal "MyTagInterface.class" (per esempio). Spero che questo ti aiuti.

+0

Manca il metodo "walkClassPath". Puoi fornirlo per favore? –

+0

@ YvesMartin: Yves, ho aggiornato la mia risposta per includere tutti i link per scaricare il codice originale. Potrebbe essere più di quello che volevi, ma è una versione completa funzionante. –

+2

@SamGoldberg Eccellente utilità che hai progettato qui. – shashankaholic

0

Probabilmente il modo migliore (standard) per farlo utilizzando il meccanismo Java SPI, vedere Javadoc. L'inconveniente (che è anche una bella caratteristica) è che si aspetta che i Jars definiscano le estensioni per elencarli in META-INF/services/your.fully.qualified.Interface.

L'unico altro modo in cui posso pensare sarebbe quello di wallare tutti i ClassLoaders nella speranza che tu sia in grado di elencare i file, caricare i file di classe e vedere se implementano la tua interfaccia o no - che è non è una bella cosa da fare

27

È possibile trovare una risposta here.

posso suggerire utilizzando org.reflections

enter link description here

Reflections reflections = new Reflections("com.mycompany");  
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class); 
+0

grazie per l'input, stavo passando per il documento ServiceLocator, sembra per ogni impianto della mia interfaccia (servizio), l'utente finale deve inserire la voce nel file in modo che possa essere caricata, è come se lo sviluppatore volesse 20 di tale implosione dovesse inserire tutti quei nome di impianto nel file, sembra molto lavoro per l'utente finale. –

1

Penso che org.reflections è una soluzione adeguata come detto da Alex Stybaev, non è necessario fare riferimento alle classi in un file di proprietà .

Un altro approccio (che vorrei prendere) è Spring, poiché sto utilizzando Spring comunque per le mie applicazioni e quindi non avrei bisogno di ulteriori dipendenze.

Si possono trovare suggerimenti per risolvere il tuo problema con la Primavera (e alternative negli altri commenti o risposte) qui:

Nei commenti della tua domanda heikkim e polypiel collegano anche a domande con risposte per risolverlo con Spring:

0

di farlo in puro Java, la risposta più corretta è già votato (da Sam Goldberg aprile 10,2012)

codice Tuttavia piena della sua è inutilmente complessa. Ho usato la sua idea e ho spinto ClassFinder lì: http://icedtea.classpath.org/hg/icedtea-web/file/0527ad4eb2dd/netx/net/sourceforge/jnlp/controlpanel/ClassFinder.java

Nota: questo funzionerà in applicazioni standalone (o qualsiasi applicazione che utilizza classpath regolari con jars e dirs) non in ServerContainer.

Se la tua app è in esecuzione sul server web, devi conoscere la posizione del tuo war/ear e cercare lì. O devi chiedere al tuo genitore classlista dove sta ottenendo le tue (e altre) fonti.

Seconda nota: sto filtrando le classi di ubicazioni finali in modo che corrispondano solo a netx e icedtea-web, in quanto non vengono ricercate le dipendenze. Quindi, se è necessario includere rt.jar per favore, rmeove i filtri thsoe. O rimuovere bootclasspath del tutto se a contrario non ne ha affatto bisogno.

Problemi correlati