Как на самом деле реализованы репозитории Spring Data?

111

Я уже некоторое время работаю с репозиторием Spring Data JPA в своем проекте и знаю следующие моменты:

  • В интерфейсах репозитория мы можем добавить такие методы, как findByCustomerNameAndPhone()(при условии, что customerNameи phoneявляются полями в объекте домена).
  • Затем Spring обеспечивает реализацию, реализуя указанные выше методы интерфейса репозитория во время выполнения (во время выполнения приложения).

Мне интересно, как это было закодировано, и я просмотрел исходный код Spring JPA и API, но не смог найти ответов на следующие вопросы:

  1. Как класс реализации репозитория создается во время выполнения, а методы внедряются и внедряются?
  2. Использует ли Spring Data JPA CGlib или какие-либо библиотеки манипуляции с байт-кодом для реализации методов и динамического внедрения?

Не могли бы вы помочь с вышеуказанными вопросами, а также предоставить любую поддерживаемую документацию?

разработчик
источник

Ответы:

144

Во-первых, не происходит генерации кода, что означает: нет CGLib, вообще нет генерации байт-кода. Фундаментальный подход заключается в том, что экземпляр прокси JDK создается программно с использованием ProxyFactoryAPI Spring для поддержки интерфейса, а MethodInterceptorперехватывает все вызовы экземпляра и направляет метод в соответствующие места:

  1. Если репозиторий был инициализирован с помощью настраиваемой части реализации (подробности см. В этой части справочной документации ) и вызываемый метод реализован в этом классе, вызов направляется туда.
  2. Если метод является методом запроса (см., DefaultRepositoryInformationКак это определяется), срабатывает механизм выполнения запроса конкретного хранилища и выполняет запрос, определенный для выполнения для этого метода при запуске. Для этого существует механизм разрешения, который пытается идентифицировать явно объявленные запросы в различных местах (используя @Queryметод, именованные запросы JPA), в конечном итоге возвращаясь к производному запросу из имени метода. Для обнаружения механизма запроса см JpaQueryLookupStrategy.. Логику синтаксического анализа для вывода запроса можно найти в PartTree. Перевод конкретного магазина в фактический запрос можно увидеть, например, в JpaQueryCreator.
  3. Если ничего из вышеперечисленного не применимо, выполняемый метод должен быть реализован базовым классом репозитория для конкретного хранилища ( SimpleJpaRepositoryв случае JPA), и вызов перенаправляется в его экземпляр.

Перехватчик метода, реализующий эту логику маршрутизации, - логику маршрутизации QueryExecutorMethodInterceptorвысокого уровня можно найти здесь .

Создание этих прокси инкапсулировано в стандартную реализацию шаблона Factory на основе Java. Создание прокси высокого уровня можно найти в RepositoryFactorySupport. Затем реализации для конкретного магазина добавляют необходимые компоненты инфраструктуры, чтобы для JPA вы могли просто написать такой код:

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

Причина, по которой я упоминаю это явно, заключается в том, что должно стать ясно, что по сути ничто из этого кода не требует запуска контейнера Spring в первую очередь. Ему нужна Spring как библиотека в пути к классам (потому что мы предпочитаем не изобретать велосипед), но в целом он не зависит от контейнера.

Для того, чтобы облегчить интеграцию с DI контейнеров мы, конечно , затем построил интеграции с конфигурацией Spring Java, XML - пространство имен, но и расширение CDI , так что Spring Data может использоваться в простых сценариях CDI.

Оливер Дротбом
источник
3
Привет, Оливер, не могли бы вы подробнее рассказать о том, как Spring обнаруживает @Repositoryаннотированные интерфейсы в первую очередь? Глядя на RepositoryFactorySupport#getRepository()show, он принимает класс интерфейса в качестве параметра, поэтому его нужно найти где-нибудь еще. Я особенно пытаюсь выяснить, как найти аннотированный интерфейс и автоматически создать прокси-компонент JDK, который реализует интерфейс, очень похоже на spring-data, но для конкретной цели приложения, не связанной с репозиториями.
Крис Райс
1
Возможно, вы захотите взглянуть на RepositoryComponentProvider. Не происходит никаких автоматических действий, кроме сканирования компонентов для определенных типов (аннотированных или несущих аннотацию) и FactoryBeanнастройки для каждого из них.
Оливер Дротбом
2
Извините, что комментирую старую ветку, но было любопытно ... Являются ли репозиторий прокси одноэлементными объектами? Мы наблюдаем проблему, при которой наш код пытается вызвать метод репо, но, похоже, он никогда не может выполнить вызов через прокси. Просто зависает. Интересно, ждет ли он занятого синглтона.
iu.david