Используешь ли прокси в коде
Что проверяют
Понимаете ли вы, что прокси окружает вас в любом Spring-проекте, даже если вы ни разу не вызывали Proxy.newProxyInstance. Интервьюер ищет связь паттерна с реальными механизмами фреймворка. Следом почти всегда спрашивают, почему @Transactional не срабатывает при вызове метода из того же класса.
Как отвечать
Ответьте честно, где прокси есть в вашем коде, и разделите неявные и явные случаи. Неявные встречаются почти каждый день:
- Spring AOP:
@Transactional,@Cacheable,@Async,@PreAuthorizeработают через прокси вокруг бина. Spring Boot по умолчанию строит их через CGLIB, наследником класса, а не через интерфейс; - Spring Data: у интерфейса репозитория нет вашей реализации, её подставляет прокси;
- Hibernate: ленивые связи и
getReference()возвращают прокси, наследника сущности, сгенерированного через Byte Buddy; - HTTP-клиенты: Feign и Retrofit реализуют ваш интерфейс через динамический прокси JDK.
Явные, если приходилось писать: java.lang.reflect.Proxy с InvocationHandler для логирования, метрик, повторов или проверки доступа. Ограничение: JDK-прокси работает только с интерфейсами, для классов нужны CGLIB или Byte Buddy. Статический прокси это обычный класс с тем же интерфейсом, который делегирует вызовы. От декоратора он отличается намерением: контроль доступа, ленивая инициализация, удалённый вызов, а не наращивание поведения.
Приведите свой пример: какую задачу решали и почему прокси, а не явный вызов. Если явных прокси не писали, скажите это и покажите, что понимаете, как они работают внутри Spring.
Где спотыкаются
Не могут объяснить самовызов. Метод a() вызывает this.b() с @Transactional, вызов идёт мимо прокси, и транзакции нет. Лечится выносом метода в другой бин.
Забывают про ограничения CGLIB: final-класс проксировать нельзя, final и private методы не перехватываются.
Сравнивают сущности через getClass() в equals. У ленивого прокси Hibernate класс другой, и равенство ломается, поэтому используют instanceof или Hibernate.getClass(). Рядом ещё одна ловушка: обращение к ленивому полю вне сессии даёт LazyInitializationException.