W jaki sposób faktycznie wdrażane są repozytoria Spring Data?

111

Pracuję z repozytorium Spring Data JPA w swoim projekcie już od jakiegoś czasu i znam poniższe punkty:

  • W interfejsach repozytorium możemy dodać metody takie jak findByCustomerNameAndPhone()(zakładając customerNamei phonesą pola w obiekcie domeny).
  • Następnie Spring zapewnia implementację poprzez implementację powyższych metod interfejsu repozytorium w czasie wykonywania (podczas uruchamiania aplikacji).

Interesuje mnie, jak to zostało zakodowane i przyjrzałem się kodowi źródłowemu Spring JPA i interfejsom API, ale nie mogłem znaleźć odpowiedzi na poniższe pytania:

  1. W jaki sposób klasa implementacji repozytorium jest generowana w czasie wykonywania i metody są wdrażane i wstrzykiwane?
  2. Czy Spring Data JPA używa CGlib lub jakichkolwiek bibliotek manipulujących kodem bajtowym do implementacji metod i dynamicznego wstrzykiwania?

Czy mógłbyś odpowiedzieć na powyższe pytania, a także dostarczyć jakąkolwiek obsługiwaną dokumentację?

deweloper
źródło

Odpowiedzi:

144

Przede wszystkim nie ma miejsca na generowanie kodu, co oznacza: brak CGLib, w ogóle brak generowania kodu bajtowego. Podstawowym podejściem jest to, że instancja proxy JDK jest tworzona programowo przy użyciu ProxyFactoryinterfejsu API Springa w celu wsparcia interfejsu i MethodInterceptorprzechwytuje wszystkie wywołania do instancji i kieruje metodę do odpowiednich miejsc:

  1. Jeśli repozytorium zostało zainicjowane z niestandardową częścią implementacji (zobacz tę część dokumentacji referencyjnej, aby uzyskać szczegółowe informacje), a wywołana metoda jest zaimplementowana w tej klasie, wywołanie jest tam kierowane.
  2. Jeśli metoda jest metodą zapytania (zobacz, DefaultRepositoryInformationjak to jest określane), mechanizm wykonywania zapytań określonego magazynu włącza się i wykonuje zapytanie określone jako wykonane dla tej metody podczas uruchamiania. W tym celu istnieje mechanizm rozstrzygania, który próbuje zidentyfikować jawnie zadeklarowane zapytania w różnych miejscach (używając @Queryw metodzie zapytań nazwanych JPA), ostatecznie wracając do wyprowadzania zapytania z nazwy metody. Aby uzyskać informacje na temat wykrywania mechanizmu zapytań, zobacz JpaQueryLookupStrategy. Logikę analizowania dla wyprowadzania zapytania można znaleźć w PartTree. Tłumaczenie konkretnego sklepu na rzeczywiste zapytanie można zobaczyć np JpaQueryCreator. W.
  3. Jeśli żadna z powyższych nie ma zastosowania, wykonywana metoda musi być implementowana przez klasę bazową repozytorium specyficzną dla magazynu ( SimpleJpaRepositoryw przypadku JPA), a wywołanie jest kierowane do jej instancji.

Interceptor metody implementujący tę logikę routingu to logika routingu QueryExecutorMethodInterceptorwysokiego poziomu, którą można znaleźć tutaj .

Tworzenie tych serwerów proxy jest zawarte w standardowej implementacji wzorca Factory opartej na języku Java. Tworzenie proxy wysokiego poziomu można znaleźć w RepositoryFactorySupport. Implementacje specyficzne dla sklepu dodają następnie niezbędne komponenty infrastruktury, aby w przypadku JPA można było śmiało napisać kod w następujący sposób:

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

Powodem, dla którego to wyraźnie wspominam, jest to, że powinno być jasne, że w swej istocie żaden z tego kodu nie wymaga przede wszystkim uruchomienia kontenera Spring. Potrzebuje Springa jako biblioteki na ścieżce klas (ponieważ wolimy nie wymyślać koła na nowo), ale generalnie jest agnostykiem kontenera.

Aby ułatwić integrację z kontenerami DI, oczywiście stworzyliśmy integrację z konfiguracją Spring Java, przestrzenią nazw XML, ale także rozszerzeniem CDI , dzięki czemu Spring Data może być używany w zwykłych scenariuszach CDI.

Oliver Drotbohm
źródło
3
Cześć Oliver, czy możesz wyjaśnić, w jaki sposób Spring odkrywa @Repositoryopisane interfejsy w pierwszej kolejności? Patrząc na, RepositoryFactorySupport#getRepository()pokaż, że przyjmuje klasę interfejsu jako parametr, więc musi zostać znaleziona gdzie indziej. Szczególnie staram się dowiedzieć, jak znaleźć interfejs z adnotacjami i automatycznie wygenerować komponent bean proxy JDK, który implementuje interfejs, bardzo podobny do danych wiosennych, ale do celów specyficznych dla aplikacji, niezwiązanych z repozytoriami.
Chris Rice
1
Możesz rzucić okiem RepositoryComponentProvider. Nie dzieje się automatycznie, ale skanowanie komponentów dla niektórych typów (z adnotacjami lub z adnotacją) i FactoryBeanskonfigurowane dla każdego z nich.
Oliver Drotbohm
2
Przepraszam, że komentuję stary wątek, ale byłem zaciekawiony… Czy repozytorium proxy to pojedyncze obiekty? Widzimy problem, w wyniku którego nasz kod próbuje wywołać metodę repo, ale wydaje się, że nigdy nie jest w stanie wykonać wywołania na serwerze proxy. Po prostu wisi. Zastanawiam się, czy czeka na zajętego singletona.
iu.david