Come vengono effettivamente implementati i repository Spring Data?


111

Lavoro da un po 'di tempo con il repository JPA Spring Data nel mio progetto e conosco i punti seguenti:

  • Nelle interfacce del repository, possiamo aggiungere i metodi come findByCustomerNameAndPhone()(assumendo customerNamee phonesono campi nell'oggetto dominio).
  • Quindi, Spring fornisce l'implementazione implementando i metodi di interfaccia del repository di cui sopra in fase di runtime (durante l'esecuzione dell'applicazione).

Sono interessato a come è stato codificato e ho esaminato il codice sorgente e le API di Spring JPA, ma non sono riuscito a trovare risposte alle domande seguenti:

  1. In che modo la classe di implementazione del repository generata in fase di runtime e i metodi vengono implementati e inseriti?
  2. Spring Data JPA utilizza CGlib o qualsiasi libreria di manipolazione del bytecode per implementare i metodi e iniettare dinamicamente?

Potresti aiutarci con le domande di cui sopra e fornire anche la documentazione supportata?

Risposte:


144

Prima di tutto, non è in corso la generazione di codice, il che significa: nessuna CGLib, nessuna generazione di byte-code. L'approccio fondamentale è che un'istanza proxy JDK viene creata a livello di codice utilizzando l' ProxyFactoryAPI di Spring per supportare l'interfaccia e MethodInterceptorintercetta tutte le chiamate all'istanza e instrada il metodo nei luoghi appropriati:

  1. Se il repository è stato inizializzato con una parte di implementazione personalizzata (vedere quella parte della documentazione di riferimento per i dettagli) e il metodo invocato è implementato in quella classe, la chiamata viene instradata lì.
  2. Se il metodo è un metodo di query (vedere DefaultRepositoryInformationcome viene determinato), il meccanismo di esecuzione della query specifico del negozio si attiva ed esegue la query determinata per essere eseguita per quel metodo all'avvio. Per questo è in atto un meccanismo di risoluzione che cerca di identificare le query dichiarate in modo esplicito in vari punti (utilizzando @Querysul metodo, query denominate JPA) che ricadono infine sulla derivazione della query dal nome del metodo. Per il rilevamento del meccanismo di query, vedere JpaQueryLookupStrategy. La logica di analisi per la derivazione della query può essere trovata in PartTree. La traduzione specifica del negozio in una query effettiva può essere vista ad esempio in JpaQueryCreator.
  3. Se nessuna delle precedenti si applica, il metodo eseguito deve essere implementato da una classe base del repository specifica del negozio ( SimpleJpaRepositoryin caso di JPA) e la chiamata viene instradata in un'istanza di tale.

L'intercettore del metodo che implementa tale logica di routing è QueryExecutorMethodInterceptor, la logica di routing di alto livello può essere trovata qui .

La creazione di questi proxy è incapsulata in un'implementazione di pattern Factory basata su Java standard. La creazione del proxy di alto livello può essere trovata in RepositoryFactorySupport. Le implementazioni specifiche del negozio aggiungono quindi i componenti dell'infrastruttura necessari in modo che per JPA tu possa andare avanti e scrivere semplicemente codice come questo:

EntityManager em =  // obtain an EntityManager
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
UserRepository repository = factory.getRepository(UserRepository.class);

Il motivo per cui lo menziono esplicitamente è che dovrebbe diventare chiaro che, nel suo nucleo, nulla di quel codice richiede un contenitore Spring per essere eseguito in primo luogo. Ha bisogno di Spring come libreria sul classpath (perché preferiamo non reinventare la ruota), ma in generale è indipendente dal contenitore.

Per facilitare l'integrazione con i contenitori DI, abbiamo ovviamente costruito l'integrazione con la configurazione Spring Java, uno spazio dei nomi XML, ma anche un'estensione CDI , in modo che Spring Data possa essere utilizzato in semplici scenari CDI.


3
Ciao Oliver, puoi spiegarci come la primavera scopre le @Repositoryinterfacce annotate in primo luogo? Guardando RepositoryFactorySupport#getRepository()mostra che prende la classe di interfaccia come parametro, quindi deve essere scoperto da qualche altra parte. In particolare, sto cercando di capire come trovare un'interfaccia annotata e generare automaticamente un bean proxy JDK che implementa l'interfaccia, molto simile a spring-data, ma per uno scopo specifico dell'applicazione non correlato ai repository.
Chris Rice

1
Potresti voler dare un'occhiata RepositoryComponentProvider. Non ci sono cose automatiche che accadono ma una scansione dei componenti per determinati tipi (annotati o portanti un'annotazione) e una FactoryBeanconfigurazione per ciascuno di essi.
Oliver Drotbohm

2
Mi dispiace commentare un vecchio thread, ma ero curioso ... I proxy del repository sono oggetti singleton? Stiamo riscontrando un problema per cui il nostro codice tenta di chiamare un metodo repo, ma non sembra mai essere in grado di effettuare la chiamata sul proxy. Si blocca e basta. Mi chiedo se stia aspettando un single che è impegnato.
iu.david
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.