Какие механизмы проксирования доступны в Spring Framework?

Ответ

Spring Framework поддерживает три основных механизма проксирования, каждый со своей областью применения.

1. JDK Dynamic Proxy (Proxy на основе интерфейсов)

  • Принцип работы: Использует встроенные возможности Java (java.lang.reflect.Proxy). Создает прокси-объект, реализующий те же интерфейсы, что и целевой объект.
  • Когда используется: По умолчанию, если бин реализует хотя бы один интерфейс.
  • Ограничение: Не может проксировать классы, не имеющие интерфейсов, или вызовы методов, объявленных в самом классе (не в интерфейсе).
public interface UserService {
    void createUser();
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    @Transactional // Для этого вызова будет создан JDK Dynamic Proxy
    public void createUser() {
        // логика
    }
}

2. CGLIB Proxy (Proxy на основе наследования)

  • Принцип работы: Создает подкласс целевого класса во время выполнения (байт-кодовая генерация) и переопределяет его методы.
  • Когда используется: Когда целевой класс не реализует интерфейсы (например, конкретный класс). Для активации необходимо установить proxyTargetClass = true.
  • Ограничение: Не может переопределить final методы или проксировать final классы.
@Service
public class ConcreteService { // Нет интерфейса
    @Transactional // Для этого вызова будет создан CGLIB Proxy
    public void process() {
        // логика
    }
}

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // Принудительное использование CGLIB
public class AppConfig {}

3. AspectJ Compile-time/Load-time Weaving

  • Принцип работы: Модифицирует байт-код класса на этапе компиляции или загрузки в JVM. Не использует стандартные прокси Spring.
  • Когда используется: Для наиболее сложных сценариев, где требуется проксирование private методов, конструкторов или полей, а также для максимальной производительности.
  • Ограничение: Требует дополнительной настройки сборки (AspectJ compiler) или агента загрузки.

Сравнение:

  • JDK Proxy: Быстрее создается, вызовы через интерфейс медленнее. Стандартная Java.
  • CGLIB: Медленнее создается, вызовы методов быстрее. Требует библиотеки CGLIB в classpath.
  • AspectJ: Наибольшая гибкость и производительность во время выполнения, но сложнее в настройке.

Ответ 18+ 🔞

Да ты посмотри, какие у нас тут, блядь, варианты нарисовались! Весь этот цирк с прокси в Spring — это ж целых три способа на свою же жопу крючок повеситься, выбирай любой, сука!

1. JDK Dynamic Proxy — классика для интеллигентов

  • Как работает: Использует стандартную явавскую подлянку (java.lang.reflect.Proxy). Берёт твой бин, смотрит ему в интерфейсы и делает его двойника-обманку, который эти интерфейсы реализует.
  • Когда втыкается: По дефолту, если твой бин хоть один интерфейс в жизни видел. Spring такой: "О, интерфейс есть? Отлично, сейчас мы тебе тут проксю на коленке сварганим".
  • Где обосрётся: А вот если класс — упрямый мудак и интерфейсов не признаёт, или ты вызываешь метод, который в интерфейсе не объявлен — всё, пиши пропало. Прокси будет молчать как рыба об лёд, потому что него этих методов-то и нету.
public interface UserService {
    void createUser();
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    @Transactional // Вот на этой хуйне Spring и подцепит JDK-шную проксю
    public void createUser() {
        // делаем вид, что работаем
    }
}

2. CGLIB Proxy — тяжёлая артиллерия для упрямых классов

  • Как работает: Тут уже по-взрослому, через чёрную магию байт-кода. Spring наследуется от твоего класса, делает ему внебрачного сына-подкласс, и в этом ублюдке все методы переопределены, чтоб добавить свою дичь (типа транзакции).
  • Когда втыкается: Когда твой класс — отбитый социопат и ни с какими интерфейсами водиться не хочет. Надо крикнуть Spring'у proxyTargetClass = true, типа "да похуй на интерфейсы, дерись как есть!".
  • Где обосрётся: Если у класса методы final или он сам final — это как бетонная стена, блядь. Наследоваться не получится, CGLIB посмотрит на это и просто развернётся, пойдёт бухать.
@Service
public class ConcreteService { // Вообще нихуя не реализует, самодостаточный мудак
    @Transactional // А вот тут уже CGLIB вступит в игру, сделает наследничка
    public void process() {
        // имитация бурной деятельности
    }
}

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // Команда "Фас!" для CGLIB
public class AppConfig {}

3. AspectJ — для полных извращенцев и перфекционистов

  • Как работает: Это уже не прокси, а, блядь, прямое вторжение в матрицу. Байт-код твоего класса пиздят и переписывают либо при компиляции, либо когда он в JVM грузится. Никаких сыночков-наследников, меняется сам оригинал!
  • Когда втыкается: Когда тебе мало этой всей прокси-дичи, и ты хочешь, чтобы даже private методы и конструкторы в транзакции участвовали. Или когда производительность — это пиздец как важно.
  • Где обосрётся: В настройке, ёпта! Тут надо или компилятор AspectJ подключать, или агента при запуске JVM цеплять. Не для слабаков, одним словом.

Итоги, блядь, на пальцах:

  • JDK Proxy: Создаётся быстро, но каждый вызов — это лишняя директива рефлексии. Зато из коробки, без лишних библиотек.
  • CGLIB: Рождается дольше, зато бегает шустрее. Но требует, чтобы эта библиотека CGLIB в classpath валялась, ещё один довесок.
  • AspectJ: Охуенная гибкость и скорость работы, потому что код меняется навсегда. Но настроить это — тот ещё геморрой, в рот меня чих-пых! Для тех, кто готов продать душу за наносекунды.