JavaRush /Курси /Модуль 5. Spring /Вступ до проксі-об'єктів (Proxy) і як вони працюють у Spr...

Вступ до проксі-об'єктів (Proxy) і як вони працюють у Spring

Модуль 5. Spring
Рівень 3 , Лекція 6
Відкрита

Сьогодні заглибимося в важливу тему, без якої справжню «магію» AOP у Spring не зрозуміти: проксі-об'єкти (Proxy). Розберемо, що це таке, як вони працюють і навіщо потрібні в Spring.

Якщо ви думаєте, що «проксі» — це той підозрілий сервер, яким ви користувалися, щоб обійти блокування улюбленого сайту, то ви... праві! Частково. Проксі в програмуванні виконують схожу роль: вони виступають посередниками. Тільки замість обходу блокувань проксі-об'єкти в Spring додають потрібну додаткову функціональність до вашого коду.

Це можна порівняти з наймом помічника (проксі) для керування вашим розкладом. Коли хтось хоче зустрітися з вами, він спочатку звертається до цього помічника. Ну а він вирішує, чи варто вас зараз турбувати. Подібним чином проксі-об'єкт у Spring може перехопити виклики методу, зробити щось корисне (наприклад, логування або перевірку безпеки), а потім передати управління справжньому об'єкту.


Як працюють проксі-об'єкти?

В основі AOP у Spring лежить використання проксі-об'єктів. Якщо ви додаєте аспект (наприклад, через @Aspect), Spring динамічно створює проксі-об'єкт, який підміняє оригінальний бін. Коли викликається метод на цьому біні, виклик спочатку проходить через проксі-об'єкт, а потім уже досягає цільового методу.

Хід виконання:

  1. Ви викликаєте метод на вашому біні.
  2. Проксі-об'єкт перехоплює виклик.
  3. Проксі виконує свій "додатковий код" (наприклад, логування, перевірку транзакцій).
  4. Проксі передає управління в цільовий метод вашого біна.

Типи проксі в Spring

Spring підтримує два типи проксі:

  1. JDK Dynamic Proxy — працює з інтерфейсами.
  2. CGLIB Proxy — працює з класами.

JDK Dynamic Proxy

JDK Dynamic Proxy створює проксі-об'єкти тільки для інтерфейсів. Якщо ваш клас реалізує інтерфейс, Spring створить проксі за допомогою JDK Dynamic Proxy.

Приклад:


public interface PaymentService {
    void processPayment();
}

@Component
public class PaymentServiceImpl implements PaymentService {
    @Override
    public void processPayment() {
        System.out.println("Processing payment...");
    }
}

У цьому випадку Spring створить проксі для інтерфейсу PaymentService. Проксі буде перехоплювати виклики методу processPayment().

CGLIB Proxy

Якщо ваш бін не реалізує інтерфейсів, Spring використовує CGLIB Proxy. Цей тип проксі наслідує ваш клас і перевизначає методи, щоб додати додаткову логіку.

Приклад:


@Component
public class NotificationService {
    public void sendNotification() {
        System.out.println("Sending notification...");
    }
}

Оскільки клас NotificationService не реалізує інтерфейсів, Spring створить проксі з використанням CGLIB.

Важливо: CGLIB не працює з методами, позначеними як final. Чому? Тому що final забороняє перевизначення методу, і це ламає всю логіку проксі.


Приклад використання проксі-об'єктів в AOP

Зробимо приклад, де ми використовуємо проксі для проставлення логів при виклику методу.

Код без AOP:


@Component
public class UserService {
    public void createUser(String username) {
        System.out.println("User " + username + " created!");
    }
}

Цей код просто виводить повідомлення про створення користувача. Але якщо ми хочемо автоматично логувати кожний виклик методу createUser, нам доведеться модифікувати метод. Це порушує принцип єдиного обов'язку.


Код з AOP і проксі:

Створюємо аспект:


@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.demo.UserService.createUser(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Method " + joinPoint.getSignature().getName() + " is called");
    }
}

Тепер при виклику методу createUser Spring буде використовувати проксі-об'єкт. Проксі спочатку виконає метод logBefore, а тільки потім викличе createUser.


Як побачити "чарівний" проксі в дії?

Можна подивитися, як Spring створює проксі-об'єкти, за допомогою простого тесту. Наприклад:


@Autowired
private UserService userService;

@Test
public void checkProxy() {
    System.out.println(userService.getClass());
}

Вивід може бути таким:

  1. Для JDK Dynamic Proxy:
    
    class com.sun.proxy.$Proxy12
    
  2. Для CGLIB Proxy:
    
    class com.example.demo.UserService$$EnhancerBySpringCGLIB$$3f9b8a5f
    

Зверніть увагу, як Spring додає свої «чарівні» суфікси до імені класу.


Чому важливо розуміти проксі?

Щоб ефективно працювати з AOP і не натикатися на підводні камені, важливо розуміти, як працюють проксі.

Типові помилки:

  • Виклик методів всередині того ж класу: Якщо ви викликаєте метод з іншого методу того ж класу, AOP не спрацює. Проксі не зможе перехопити виклик, бо він іде напряму, минаючи проксі-об'єкт.

Приклад:


@Component
public class SampleService {

    public void externalMethod() {
        internalMethod(); // Проксі не спрацює!
    }

    public void internalMethod() {
        System.out.println("Internal method called");
    }
}

Рішення: розділіть методи на різні біни.

  • Final методи: Якщо ви використовуєте CGLIB Proxy, то оголошення методу як final призведе до того, що проксі не зможе його перевизначити. Це також зробить AOP марним для цього методу.

Приклад:


public final void finalMethod() {
    System.out.println("This method cannot be proxied");
}

Практичне значення

Розуміння роботи проксі допомагає уникати помилок і робить код більш прозорим. У реальних проектах проксі використовують не тільки для аспектів, але й для управління транзакціями і безпеки. Наприклад:

  • Анотація @Transactional у Spring теж базується на AOP і проксі. Якщо ви не розумієте, що проксі не спрацює при виклику методу всередині того ж класу, ви можете отримати неочікувані результати, наприклад, відсутність відкату транзакції.

Ми дізналися, що проксі-об'єкти відіграють ключову роль в реалізації AOP у Spring. Вони дозволяють додавати cross-cutting concerns (аспекти) без зміни бізнес-логіки. Spring вміло використовує динамічні проксі JDK і CGLIB Proxy, залежно від конкретного біна. Тепер ви озброєні цим знанням і готові до ще більш глибокого використання аспектів у своїх застосунках.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ