Аспектно-орієнтоване програмування (АОП) доповнює об'єктно-орієнтоване програмування (ООП), забезпечуючи інший спосіб осмислення структури програми. Ключовою одиницею модульності ООП є клас, у той час як в АОП одиницею модульності є аспект. Аспекти дозволяють здійснювати модульну організацію функціональності (наприклад, керування транзакціями), яка охоплює безліч типів та об'єктів. (У літературі з АОП таку функціональність часто називають "наскрізною (crosscutting)").

Одним із ключових компонентів Spring є АОП-фреймворк. Хоча IoC-контейнер Spring не залежить від АОП (тобто тобі не потрібно використовувати АОП, якщо ти цього не хочеш), АОП доповнює Spring IoC, що забезпечує отримання дуже адекватного рішення для проміжного ПЗ.

Spring AOP з використанням зрізів з AspectJ

Spring пропонує прості та повнофункціональні способи написання спеціальних аспектів із використанням або підходу на основі схем, або стилю анотацій @AspectJ. Обидва стилі підтримують повністю типізовані Advice та можливість використання мови зрізів AspectJ, але водночас використовують Spring AOP для зв'язування.

У цьому розділі розглянута підтримка АОП на основі схем та @AspectJ.

АОП використовується в Spring Framework для:

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

  • Дозволяє користувачам реалізовувати спеціальні аспекти, доповнюючи використання ООП за допомогою АОП.

Якщо тебе цікавлять тільки загальні декларативні служби, або інші декларативні служби, що не вимагають підготовки проміжного ПЗ, такі як пулінг, тобі не доведеться працювати безпосередньо з Spring AOP, тому ти сміливо можеш пропустити більшу частину цього розділу.

Поняття АОП

Давай почнемо з визначення деяких основних понять та термінології АОП. Ці терміни є специфічними для Spring. На жаль, термінологія АОП не дуже інтуїтивно зрозуміла. Однак стане ще заплутанішою, якщо Spring використовуватиме власну термінологію.

  • Аспект (aspect): Модульно організована функціональність (concern), яка охоплює кілька класів. Управління транзакціями є гарним прикладом наскрізної функціональності у корпоративних Java-додатках. У Spring AOP аспекти реалізуються за допомогою звичайних класів (підхід на основі схем) або звичайних класів, анотованих анотацією @Aspect (стиль @AspectJ).

  • Точка з'єднання: Точка під час виконання програми, наприклад, виконання методу або обробка виключення. У Spring AOP точка з'єднання завжди є виконанням методу.

  • Advice: Дія, що робиться аспектом у певній точці з'єднання. Різні види Advice включають Advice "навколо (around)", "перед (before)" та "після (after)". (Типи Advice будуть розглянуті далі). Багато АОП-фреймворків, включно зі Spring, моделюють Advice як перехоплювач і підтримують ланцюжок перехоплювачів замість точки з'єднання.

  • Зріз (pointcut): Предикат, який відповідає точкам з'єднання. Advice пов'язаний з виразом зрізу і виконується у будь-якій точці з'єднання, що збігається зі зрізом (наприклад, виконання методу з певним ім'ям). Концепція точок з'єднання, зіставлених з виразами зрізу, є центральною в АОП, а Spring за замовчуванням використовує мову виразів зрізів AspectJ. типу. Spring AOP дозволяє впроваджувати нові інтерфейси (і відповідну реалізацію) до будь-якого обладнаного Advice-ом об'єкту. Наприклад, можна використовувати вступ, щоб у примусовому порядку бін реалізовував інтерфейс IsModified для спрощення кешування. (У співтоваристві AspectJ введення відоме як міжтипове оголошення).

  • Цільовий об'єкт (Target object): Об'єкт, який забезпечується Advice-ом за одним або декількома аспектами. Також називається "об'єкт, що забезпечується Advice-ом". Оскільки Spring AOP реалізований з використанням динамічних проксі, цей об'єкт завжди є проксованим об'єктом.

  • Проксі АОП: Об'єкт, створюваний АОП-фреймворком для реалізації аспектних контрактів (забезпечує Advice виконання методів і так далі). У Spring Framework проксі АОП – це динамічний проксі JDK або проксі CGLIB.

  • Зв'язування (weaving): зв'язування аспектів з іншими типами додатків або об'єктами для створення об'єкта, обладнаного порадою. Він може бути зроблений під час компіляції (наприклад, за допомогою компілятора AspectJ), під час завантаження або під час виконання програми. Spring AOP, як і інші чисті АОП-фреймворки на Java, здійснює зв'язування під час виконання програми.

Spring AOP включає наступні типи порад:

  • Advice "перед (before)": Advice, яка виконується перед точкою з'єднання, але не має можливості запобігти потоку виконання, що йде до точки з'єднання (якщо тільки вона не генерує виняток).

  • Advice "після повернення (after returning)": Advice, яка буде виконуватися після нормального завершення роботи точкою з'єднання (наприклад, якщо метод повертається без генерації виключення).

  • Advice "після генерації виключення (throwing)": Advice, яка буде виконуватися, якщо метод завершує роботу, генеруючи виняток.

  • Advice "після (остаточно) (after (finally)": Advice повинна виконуватися незалежно від способу виходу з точки з'єднання (звичайним або винятковим поверненням).

  • Advice "навколо (around)": Advice, яка оточує точку з'єднання. Таку як, наприклад, виклик методу. Це найдієвіший вид поради. Advice "навколо" може наслідувати спеціальну логіку роботи до і після виклику методу. Він також відповідає за вибір того, чи слід перейти до точки з'єднання або скоротити виконання рекомендованого методу, повернувши власне значення, що повертається, або генеруючи виняток.

    Advice "навколо" — найпоширеніший вид поради. Оскільки Spring AOP, як і AspectJ, надає повний спектр типів порад, ми рекомендуємо використовувати найменш впливовий тип поради, який може реалізувати необхідну логіку роботи. Наприклад, якщо тобі потрібно лише оновити кеш з значенням методу, що повертається, то краще реалізувати пораду "після повернення", ніж пораду "навколо". Хоча Advice "навколо" може робити те ж саме. Використання найбільш конкретного типу ради забезпечує простішу модель програмування з меншою ймовірністю виникнення помилок. Наприклад, не потрібно викликати метод proceed() для JoinPoint, який використовується для поради "навколо", і, отже, тобі не вдасться не викликати його.

    Всі параметри порад статично типізовані, тому ти працюєш із параметрами порад відповідного типу (наприклад, типу значення, що повертається при виконанні методу), а не з масивами Object.

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

Можливості та цілі Spring AOP

Spring AOP реалізований на чистому Java. Немає потреби у спеціальному процесі компіляції. Spring AOP не вимагає керування ієрархією завантажувача класів і тому підходить для використання в контейнері сервлетів або на сервері додатків.

Spring AOP в даний час підтримує лише точки з'єднання виконання методів (постачання порадою виконання методів для бінів Spring). Перехоплення полів не реалізовано, хоча підтримка перехоплення полів може бути додана без порушення основних API-інтерфейсів Spring AOP. Якщо необхідно надавати пораду доступ до полів і оновлювати точки з'єднання, розглянь таку мову, як AspectJ.

Підхід Spring AOP до АОП відрізняється від підходу більшості інших АОП-фреймворків. Ціль не в тому, щоб надати найбільш повну реалізацію АОП (хоча Spring AOP цілком здатний на це). Швидше, мета полягає в тому, щоб забезпечити тісну інтеграцію між реалізацією АОП і Spring IoC, що допоможе вирішити загальні проблеми в корпоративних додатках. Так, наприклад, функціональність АОП в Spring Framework зазвичай використовується в поєднанні з IoC-контейнером Spring. Аспекти конфігуруються за допомогою звичайного синтаксису визначення бінів (хоча це дозволяє використовувати високопродуктивні можливості автопроксування). Це важлива відмінність від інших реалізацій АОП. У тебе не вийде легко і ефективно робити деякі речі за допомогою Spring AOP, наприклад, постачати порадою украй дрібномодульні об'єкти (як правило, об'єкти предметної області). AspectJ — найкращий вибір у таких випадках. Тим не менш, наш досвід показує, що Spring AOP забезпечує відмінне вирішення більшості проблем у корпоративних Java-додатках, які піддаються АОП.

Spring AOP в жодному разі не намагався конкурувати з AspectJ в частині надання комплексного рішення для АОП. Ми вважаємо, що і проксі-орієнтовані фреймворки, такі як Spring AOP, і повноцінні фреймворки, такі як AspectJ, є цінними і доповнюють один одного, ніж конкурують. Spring легко інтегрує Spring AOP та IoC з AspectJ, що дозволяє використовувати АОП у межах узгодженої архітектури додатків на базі Spring. Ця інтеграція не впливає на API-інтерфейс Spring AOP або API-інтерфейс AOP Alliance. Spring AOP залишається назад сумісним.

Одним із основних принципів Spring Framework є неагресивність. Ідея полягає в тому, щоб не примушувати тебе впроваджувати специфічні для фреймворку класи та інтерфейси до своєї бізнес-моделі чи моделі предметної галузі. Однак у деяких моментах Spring Framework дає можливість впровадити в твою кодову базу специфічні для Spring Framework залежності. Сенс надання таких можливостей у тому, що у певних сценаріях може бути просто простіше прочитати чи закодувати певну функціональність в такий спосіб. Однак Spring Framework (майже) завжди пропонує вибір: ти маєш змогу прийняти зважене рішення про те, який варіант найкраще підходить для конкретного випадку або сценарію використання.

Один із таких виборів, який стосується цього розділу, є вибір АОП-фреймворку (і стилю АОП). Є такий вибір: AspectJ, Spring AOP або обидва варіанти. Ти також маєте вибір: або підхід у стилі анотації @AspectJ, або підхід у стилі конфігурації Spring XML. Той факт, що в цьому розділі ми вирішили спочатку уявити підхід у стилі @AspectJ, не слід сприймати як ознаку того, що команда Spring віддає підходу в стилі анотації @AspectJ більшу перевагу, ніж підходу в стилі конфігурації Spring XML.

Проксі АОП

Spring AOP за замовчуванням використовує стандартні динамічні проксі JDK для проксі АОП. Це дозволяє проксіювати будь-який інтерфейс (або набір інтерфейсів).

Spring AOP також може використовувати проксі CGLIB. Вони необхідні проксування класів, а не інтерфейсів. За замовчуванням CGLIB використовується, якщо бізнес-об'єкт не реалізує інтерфейс. Оскільки рекомендується програмувати інтерфейси, а не класи, бізнес-класи зазвичай реалізують один або кілька бізнес-інтерфейсів. проксований об'єкт як конкретний тип.

Важливо розуміти, що Spring AOP ґрунтується на проксі.