#java #spring #jpa
Вопрос:
Я уже некоторое время работаю с репозиторием Spring Data JPA в своем проекте и знаю следующие моменты:
- В интерфейсах репозитория мы можем добавить такие методы, как
findByCustomerNameAndPhone()
(предполагаяcustomerName
, что иphone
являются полями в объекте домена). - Затем Spring обеспечивает реализацию, реализуя вышеуказанные методы интерфейса репозитория во время выполнения (во время запуска приложения).
Мне интересно, как это было закодировано, и я просмотрел исходный код Spring JPA и API, но я не смог найти ответы на вопросы ниже:
- Как создается и внедряется класс реализации репозитория во время выполнения и методы?
- Использует ли Spring Data JPA CGlib или какие-либо библиотеки для обработки байт-кода для реализации методов и динамического внедрения?
Не могли бы вы помочь с вышеуказанными запросами, а также предоставить любую поддерживаемую документацию ?
Ответ №1:
Во-первых, не происходит генерации кода, что означает: нет CGLib, вообще нет генерации байтового кода. Фундаментальный подход заключается в том, что экземпляр прокси-сервера JDK создается программно с использованием ProxyFactory
API Spring для поддержки интерфейса, и a MethodInterceptor
перехватывает все вызовы экземпляра и направляет метод в соответствующие места:
- Если репозиторий был инициализирован с помощью пользовательской части реализации (подробности см. в этой части справочной документации), и вызванный метод реализован в этом классе, вызов направляется туда.
- Если метод является методом запроса (см.
DefaultRepositoryInformation
, Как это определяется), включается механизм выполнения запроса для конкретного хранилища и выполняет запрос, определенный для выполнения для этого метода при запуске. Для этого существует механизм разрешения, который пытается идентифицировать явно объявленные запросы в различных местах (используя@Query
в методе именованные запросы JPA), в конечном итоге возвращаясь к выводу запроса из имени метода. Для определения механизма запроса см.JpaQueryLookupStrategy
раздел . Логику синтаксического анализа для вывода запроса можно найти вPartTree
. Конкретный перевод магазина в фактический запрос можно увидеть, например, вJpaQueryCreator
. - Если ничего из вышеперечисленного не применяется, выполняемый метод должен быть реализован базовым классом репозитория для конкретного хранилища (
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. Извините, что комментирую старую тему, но мне было любопытно … Являются ли прокси-серверы репозитория одноэлементными объектами? Мы видим проблему, из-за которой наш код пытается вызвать метод репо, но, похоже, он никогда не сможет вызвать прокси-сервер. Он просто висит. Интересно, ждет ли он синглтона, который занят?