Что под капотом Spring Data JPA?
Тоже впечатлены элегантностью работы с Retrofit и Spring Data JPA? Когда вы просто добавляете в интерфейс сигнатуру метода, а при вызове этого метода оказывается, что он реально работает!
Да, долой boilerplate* code!
Так это выглядит при работе с Spring Data JPA. И никаких SQL, HQL или JPQL!
interface PersonRepository extends Repository<Person, Long> { List<Person> findByLastname(String lastname); Long countByLastname(String lastname); }
Знаете что там внутри? Ничего нового – обычный Java Reflection API!
Когда библиотека получает ваш интерфейс, ей нужно решить два главных вопроса: 1. Как в процессе выполнения программы создать экземпляр класса, реализующий ваш интерфейс? Ведь библиотека должна дать вам возможность вызова этих методов так, как будто это методы самого обычного класса. 2. Как получить и проанализировать сигнатуры методов, которые вы описали в интерфейсе? Ведь на основании соглашений о наименовании метода интерфейса библиотеке необходимо обработать вызов этого метода определённым способом.
Класс java.lang.reflect.Proxy создан как раз для этого. Он позволяет создать динамический прокси-объект, реализующий указанный интерфейс.
В таком прокси-объекте все вызовы его методов перехватываются и направляются в специальный обработчик – реализацию интерфейса InvocationHandler.
Вот как это выглядит:
PersonRepository personRepository = (PersonRepository) Proxy.newProxyInstance(PersonRepository.class.getClassLoader(), new Class[] { PersonRepository.class }, (proxy, method, args) -> { String methodName = method.getName(); // Обрабатываем вызов метода интерфейса, возвращаем результат if(methodName.startsWith("find")) { // ... } return new Object(); });
Лямбда-выражение (proxy, method, args) реализует интерфейс InvocationHandler и содержит логику обработки вызовов всех методов PersonRepository.
InvocationHandler содержит все необходимые для обработки вызова метаданные: – ссылку на объект, – метод которого вызвал пользователь, – сигнатуру вызванного метода с его названием, параметрами и аннотациями, – и, конечно, значения аргументов, переданных пользователем при вызове метода.
В использовании полученный прокси-объект не отличается от обыкновенного класса:
List<Person> persons = personRepository.findByLastname("Otus");
Есть вопрос? Напишите в комментариях! *Boilerplate_code