Hoe worden Spring Data-opslagplaatsen eigenlijk geïmplementeerd?

Ik werk al een tijdje met Spring Data JPA-repository in mijn project en ik ken de onderstaande punten:

  • In de repository-interfaces kunnen we methoden toevoegen zoals findByCustomerNameAndPhone()(ervan uitgaande dat customerNameen phonevelden zijn in het domeinobject) .
  • Vervolgens zorgt Spring voor de implementatie door de bovenstaande repository-interfacemethoden tijdens runtime (tijdens het uitvoeren van de toepassing) te implementeren.

Ik ben geïnteresseerd in hoe dit is gecodeerd en ik heb gekeken naar de Spring JPA-broncode & API’s, maar ik kon geen antwoorden vinden op de onderstaande vragen:

  1. Hoe wordt de repository-implementatieklasse gegenereerd tijdens runtime & methoden die worden geïmplementeerd en geïnjecteerd?
  2. Gebruikt Spring Data JPA CGlib of andere bytecode-manipulatiebibliotheken om de methoden te implementeren en dynamisch te injecteren?

Kunt u alstublieft helpen met de bovenstaande vragen en ook ondersteunde documentatie verstrekken?


Antwoord 1, autoriteit 100%

Ten eerste is er geen codegeneratie gaande, wat betekent: geen CGLib, helemaal geen bytecodegeneratie. De fundamentele benadering is dat een JDK-proxy-instantie programmatisch wordt gemaakt met behulp van Spring’s ProxyFactoryAPI om de interface te ondersteunen en een MethodInterceptoronderschept alle oproepen naar de instantie en leidt de methode naar de juiste plaatsen:

  1. Als de repository is geïnitialiseerd met een aangepast implementatiegedeelte (zie dat deel van de referentiedocumentatievoor details), en de aangeroepen methode is geïmplementeerd in die klasse, de aanroep wordt daarheen gerouteerd.
  2. Als de methode een querymethode is (zie DefaultRepositoryInformationvoor hoe dat wordt bepaald), treedt het winkelspecifieke mechanisme voor het uitvoeren van query’s in werking en voert de query uit die bij het opstarten voor die methode moet worden uitgevoerd. Daarvoor is er een oplossingsmechanisme dat op verschillende plaatsen expliciet gedeclareerde zoekopdrachten probeert te identificeren (met behulp van @Queryop de methode, JPA genaamde zoekopdrachten) en uiteindelijk terugvalt op het afleiden van zoekopdrachten uit de naam van de methode. Voor de detectie van het querymechanisme, zie JpaQueryLookupStrategy. De parseerlogica voor de afleiding van de query is te vinden in PartTree. De winkelspecifieke vertaling naar een daadwerkelijke zoekopdracht kan b.v. in JpaQueryCreator.
  3. Als geen van de bovenstaande punten van toepassing is, moet de uitgevoerde methode er een zijn die is geïmplementeerd door een winkelspecifieke repository-basisklasse (SimpleJpaRepositoryin geval van JPA) en de oproep wordt doorgestuurd naar een instantie daarvan.

De methode-interceptor die die routeringslogica implementeert, is QueryExecutorMethodInterceptor, de routeringslogica op hoog niveau is te vinden hier.

Het maken van die proxy’s is ingekapseld in een standaard Java-gebaseerde Factory-patroonimplementatie. Het maken van proxy’s op hoog niveau is te vinden in RepositoryFactorySupport. De winkelspecifieke implementaties voegen vervolgens de benodigde infrastructuurcomponenten toe, zodat u voor JPA door kunt gaan en code als volgt kunt schrijven:

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

De reden dat ik dat expliciet vermeld, is dat het duidelijk moet worden dat in de kern niets van die code een Spring-container vereist om in de eerste plaats te draaien. Het heeft Spring nodig als een bibliotheek op het klassenpad (omdat we het wiel liever niet opnieuw uitvinden), maar is in het algemeen container-agnostisch.

Om de integratie met DI-containers te vergemakkelijken, hebben we natuurlijk de integratie gebouwd met Spring Java-configuratie, een XML-naamruimte, maar ook een CDI-extensie, zodat Spring Data kan worden gebruikt in gewone CDI-scenario’s.

Other episodes