Как на самом деле реализованы хранилища данных Spring?

#java #spring #jpa

Вопрос:

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

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

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

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

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

Ответ №1:

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

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

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

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

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

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

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

Комментарии:

1. Привет, Оливер, не могли бы вы подробнее рассказать о том, как spring @Repository в первую очередь обнаруживает аннотированные интерфейсы? Рассмотрение RepositoryFactorySupport#getRepository() показывает, что он принимает класс интерфейса в качестве параметра, поэтому он должен быть обнаружен где-то еще. Я особенно пытаюсь понять, как найти аннотированный интерфейс и автоматически сгенерировать компонент прокси-сервера JDK, который реализует интерфейс, очень похожий на spring-data, но для конкретной прикладной цели, не связанной с репозиториями.

2. Возможно, вы захотите взглянуть на RepositoryComponentProvider это . Там не происходит никаких автоматических действий, кроме сканирования компонентов для определенных типов (либо с аннотациями, либо с аннотациями) и FactoryBean настройки для каждого из них.

3. Извините, что комментирую старую тему, но мне было любопытно … Являются ли прокси-серверы репозитория одноэлементными объектами? Мы видим проблему, из-за которой наш код пытается вызвать метод репо, но, похоже, он никогда не сможет вызвать прокси-сервер. Он просто висит. Интересно, ждет ли он синглтона, который занят?