1. Введение
Сегодня существует несколько доступных библиотек АОП, и они должны отвечать на ряд вопросов:
- Совместимо ли оно с моим существующим или новым приложением?
- Где я могу реализовать АОП?
- Как быстро он будет интегрироваться с моим приложением?
- Каковы издержки производительности?
В этой статье мы рассмотрим ответы на эти вопросы и познакомим вас со Spring AOP и AspectJ — двумя самыми популярными платформами АОП для Java.
2. Концепции АОП
Прежде чем мы начнем, давайте быстро и подробно рассмотрим термины и основные понятия:
- Аспект — стандартный код/функция, разбросанные по нескольким местам в приложении и обычно отличающиеся от фактической бизнес-логики (например, управление транзакциями). Каждый аспект фокусируется на определенной сквозной функциональности.
- Точка соединения — это конкретная точка во время выполнения программ, таких как выполнение метода, вызов конструктора или назначение поля.
- Совет — действие, предпринятое аспектом в конкретной точке соединения.
- Pointcut — регулярное выражение, соответствующее точке соединения. Каждый раз, когда любая точка соединения соответствует pointcut, выполняется указанный совет, связанный с этим pointcut.
- Плетение - процесс связывания аспектов с целевыми объектами для создания рекомендуемого объекта.
3. Spring АОП и AspectJ
Теперь давайте обсудим Spring AOP и AspectJ по нескольким направлениям, таким как возможности, цели, плетение, внутренняя структура, точки соединения и простота.
3.1. Возможности и цели
Проще говоря, Spring AOP и AspectJ преследуют разные цели.
Spring AOP стремится предоставить простую реализацию АОП в Spring IoC для решения наиболее распространенных проблем, с которыми сталкиваются программисты. Он не предназначен как полное решение АОП — его можно применять только к bean-компонентам, которыми управляет контейнер Spring.
С другой стороны, AspectJ — это оригинальная АОП-технология, целью которой является предоставление комплексного АОП-решения. Он более надежен, но и значительно сложнее, чем Spring AOP. Также стоит отметить, что AspectJ можно применять ко всем предметным объектам.
3.2. Ткачество
И AspectJ, и Spring AOP используют разные типы плетения, которые влияют на их поведение в отношении производительности и простоты использования.
AspectJ использует три разных типа плетения:
- Плетение во время компиляции : компилятор AspectJ принимает в качестве входных данных как исходный код нашего аспекта, так и наше приложение и создает файлы сплетенных классов в качестве выходных данных.
- Плетение после компиляции : это также известно как бинарное плетение. Он используется для объединения существующих файлов классов и JAR-файлов с нашими аспектами.
- Переплетение во время загрузки : это точно так же, как предыдущее бинарное переплетение, с той разницей, что переплетение откладывается до тех пор, пока загрузчик классов не загрузит файлы классов в JVM.
Для получения более подробной информации о самом AspectJ перейдите к этой статье .
Поскольку AspectJ использует переплетение времени компиляции и загрузки классов, Spring AOP использует переплетение времени выполнения .
При сплетении во время выполнения аспекты переплетаются во время выполнения приложения с использованием прокси-серверов целевого объекта — с использованием либо динамического прокси-сервера JDK, либо прокси-сервера CGLIB (которые обсуждаются в следующем пункте):
3.3. Внутренняя структура и применение
Spring AOP — это среда АОП на основе прокси. Это означает, что для реализации аспектов целевых объектов будут созданы прокси этого объекта. Это достигается одним из двух способов:
- Динамический прокси JDK — предпочтительный способ для Spring AOP. Всякий раз, когда целевой объект реализует хотя бы один интерфейс, будет использоваться динамический прокси JDK.
- Прокси-сервер CGLIB — если целевой объект не реализует интерфейс, можно использовать прокси-сервер CGLIB.
Подробнее о механизмах проксирования Spring AOP мы можем узнать из официальной документации .
AspectJ, с другой стороны, ничего не делает во время выполнения, поскольку классы компилируются непосредственно с аспектами.
И поэтому, в отличие от Spring AOP, он не требует никаких шаблонов проектирования. Чтобы вплести аспекты в код, он вводит свой компилятор, известный как компилятор AspectJ (ajc), с помощью которого мы компилируем нашу программу, а затем запускаем ее, предоставляя небольшую (< 100 КБ) библиотеку времени выполнения.
3.4. Точки соединения
В разделе 3.3 мы показали, что Spring AOP основан на шаблонах прокси. Из-за этого ему необходимо создать подкласс целевого класса Java и соответствующим образом применить сквозные аспекты.
Но это связано с ограничением. Мы не можем применять сквозные аспекты (или аспекты) к классам, которые являются «финальными», потому что их нельзя переопределить, и, следовательно, это приведет к исключению во время выполнения.
То же самое относится к статическим и окончательным методам. Аспекты Spring не могут быть применены к ним, потому что они не могут быть переопределены. Следовательно, Spring AOP из-за этих ограничений поддерживает только точки соединения выполнения метода.
Однако AspectJ вплетает сквозные проблемы непосредственно в фактический код перед выполнением. В отличие от Spring AOP, он не требует создания подкласса целевого объекта и, таким образом, поддерживает множество других точек соединения. Ниже приводится сводка поддерживаемых точек соединения:
| Точка присоединения | Поддерживается Spring АОП | Аспект J Поддерживается |
| Вызов метода | Нет | Да |
| Выполнение метода | Да | Да |
| Вызов конструктора | Нет | Да |
| Выполнение конструктора | Нет | Да |
| Выполнение статического инициализатора | Нет | Да |
| Инициализация объекта | Нет | Да |
| Ссылка на поле | Нет | Да |
| Полевое назначение | Нет | Да |
| Выполнение обработчика | Нет | Да |
| Исполнение совета | Нет | Да |
Также стоит отметить, что в Spring AOP аспекты не применяются к методу, вызываемому в том же классе.
Это очевидно, потому что, когда мы вызываем метод в том же классе, мы не вызываем метод прокси, который предоставляет Spring AOP. Если нам нужна эта функциональность, нам нужно определить отдельный метод в разных bean-компонентах или использовать AspectJ.
3.5. Простота
Spring AOP, очевидно, проще, потому что он не вводит никакого дополнительного компилятора или ткача между нашим процессом сборки. Он использует переплетение во время выполнения и поэтому легко интегрируется с нашим обычным процессом сборки. Хотя это выглядит просто, оно работает только с bean-компонентами, которыми управляет Spring.
Однако, чтобы использовать AspectJ, нам необходимо ввести компилятор AspectJ (ajc) и переупаковать все наши библиотеки (если только мы не переключимся на посткомпиляцию или переплетение во время загрузки).
Это, конечно, сложнее, чем предыдущее, потому что в нем представлены инструменты AspectJ Java (которые включают компилятор (ajc), отладчик (ajdb), генератор документации (ajdoc), браузер структуры программы (ajbrowser)), которые мы необходимо интегрировать либо с нашей IDE, либо с инструментом сборки.
3.6. Производительность
Что касается производительности, переплетение во время компиляции намного быстрее, чем переплетение во время выполнения . Spring AOP — это фреймворк на основе прокси, поэтому прокси создаются во время запуска приложения. Кроме того, есть еще несколько вызовов методов на аспект, что негативно сказывается на производительности.
С другой стороны, AspectJ вплетает аспекты в основной код перед выполнением приложения, и, таким образом, нет дополнительных накладных расходов во время выполнения, в отличие от Spring AOP.
По этим причинам тесты показывают, что AspectJ почти в 8–35 раз быстрее, чем Spring AOP.
4. Резюме
Эта краткая таблица суммирует основные различия между Spring AOP и AspectJ:
| Весенний АОП | АспектJ |
| Реализовано на чистой Java | Реализовано с использованием расширений языка программирования Java. |
| Нет необходимости в отдельном процессе компиляции | Требуется компилятор AspectJ (ajc), если не настроен LTW |
| Доступно только плетение во время выполнения | Прошивка во время выполнения недоступна. Поддерживает время компиляции, пост-компиляцию и время загрузки Weaving |
| Менее мощный — поддерживает только переплетение на уровне метода. | Более мощный — может объединять поля, методы, конструкторы, статические инициализаторы, окончательные классы/методы и т. д. |
| Может быть реализован только на bean-компонентах, управляемых контейнером Spring. | Может быть реализован на всех предметных объектах |
| Поддерживает только точки выполнения метода | Поддержка всех точечных сокращений |
| Прокси создаются из целевых объектов, и на эти прокси накладываются аспекты | Аспекты вплетаются непосредственно в код перед выполнением приложения (до выполнения) |
| Гораздо медленнее, чем AspectJ | Лучшая производительность |
| Легко учиться и применять | Сравнительно сложнее, чем Spring AOP |
5. Выбор правильного фреймворка
Если мы проанализируем все аргументы, приведенные в этом разделе, то начнем понимать, что дело вовсе не в том, что один фреймворк лучше другого.
Проще говоря, выбор сильно зависит от наших требований:
- Фреймворк: если приложение не использует фреймворк Spring, у нас нет другого выбора, кроме как отказаться от идеи использования Spring AOP, потому что он не может управлять чем-либо, что находится за пределами досягаемости контейнера Spring. Однако, если наше приложение создано полностью с использованием среды Spring, мы можем использовать Spring AOP, так как его легко изучить и применить.
- Гибкость. Учитывая ограниченную поддержку точек соединения, Spring AOP не является полным АОП-решением, но решает наиболее распространенные проблемы, с которыми сталкиваются программисты. Хотя, если мы хотим копнуть глубже и максимально использовать АОП, и нам нужна поддержка из широкого спектра доступных точек соединения, то AspectJ — это выбор.
- Производительность: если мы используем ограниченные аспекты, тогда есть тривиальные различия в производительности. Но бывают случаи, когда приложение имеет более десятков тысяч аспектов. Мы не хотели бы использовать рантайм-ткачество в таких случаях, поэтому было бы лучше выбрать AspectJ. Известно, что AspectJ в 8–35 раз быстрее, чем Spring AOP.
- Лучшее из обоих: обе эти платформы полностью совместимы друг с другом. Мы всегда можем воспользоваться преимуществами Spring AOP, когда это возможно, и по-прежнему использовать AspectJ для поддержки точек соединения, которые не поддерживаются предыдущими версиями.
6. Заключение
В этой статье мы проанализировали как Spring AOP, так и AspectJ в нескольких ключевых областях.
Мы сравнили два подхода к АОП как по гибкости, так и по тому, насколько легко они подходят для нашего приложения.