Десять вопросов о душе Spring AOP

Java

прогулочная доска с полостью отходов

Сегодня такой ссылки нет. . .

Что такое АОП?

Полное название АОП — Аспектно-ориентированное программирование, что переводится как «Аспектно-ориентированное программирование». В языке Java все является объектом, поэтому мы обычно говорим, что язык Java является «объектно-ориентированным» языком программирования. Аспектно-ориентированное программирование предназначено не для замены объектно-ориентированного программирования, а как дополнение к нему.

Проблема, которую должен решить АОП, состоит в том, чтобы использовать метод «поперечного сечения» для унифицированной обработки одних и тех же или похожих функций, требуемых многими объектами, сокращения повторяющегося кода в программе, а также сделать код чище и более ориентированным на бизнес.

Что может АОП?

АОП подходит для некоторых"более общий, менее связанный с бизнесом"иметь значение. Более распространенными являются журналы вызовов, контроль разрешений, время вызова и статистика производительности, проверка параметров и так далее.

Фактически, Spring сделал много полезных вещей, основанных на АОП, таких как транзакции, кэширование, повторные попытки, проверка и т. д., которые вы можете часто использовать.Нижний уровень использует АОП.

Есть также много потребностей в использовании АОП для реализации некоторых небольших функций. Например, у нашей команды раньше было такое требование, мы не использовали перечисление в вызовах между сервисами и сервисами, а вместо этого использовали строки, то есть перечисляли имя соответствующего поля. Но это приводит к проблеме: если вызывающая сторона вводит строку, не принадлежащую этому перечислению, она выдаст исключение при последующем использовании. Поэтому мы надеемся, что сможем выполнить эту проверку на уровне контроллера и генерировать исключение, чтобы генерировать исключение.

Это требование для проверки параметров.Однако ни проверка, предоставляемая Spring, ни пользовательская проверка не могут выполнить это требование, потому что поле является строкой, а тип перечисления, который необходимо проверять, каждый раз разный. Поэтому мы разработали такой валидатор параметров перечисления на основе аннотаций и АОП.

Какова связь между Spring AOP и AspectJ?

На самом деле, АОП не является эксклюзивным для Spring.АОП изначально был моделью программирования.Позднее, чтобы обсудить стандартизацию АОП и унифицировать спецификацию АОП, был создан альянс АОП. Помимо Spring существует множество фреймворков АОП, таких как AspectJ, AspectWerkz, JBoss-AOP.

Вначале Spring AOP и AspectJ были полностью независимы, и у Spring была собственная реализация и синтаксис использования. Но АОП Spring слишком сложен в использовании, и все сильно жалуются на него. Таким образом, Spring поддерживает хорошо принятый синтаксис AspectJ, добавляя в класс конфигурации@EnableAspectJAutoProxyЭта аннотация включает синтаксис для AspectJ.

Но весна"Поддерживаются только некоторые синтаксисы AspectJ (некоторые синтаксисы не поддерживаются), но базовая реализация по-прежнему представляет собой собственный набор вещей.". И цели этих двух фреймворков разные: AspectJ — это полное АОП-решение, более надежное, но более сложное в использовании и требующее специального синтаксиса и компиляторов. Цель Spring — объединить AOP и инфраструктуру IoC, чтобы bean-компоненты, управляемые Spring, могли легко использовать функции AOP.

Таким образом, Spring AOP не имеет ничего общего с AspectJ, но Spring заимствует синтаксис объявления Aspect.

Связь между AOP, Spring AOP и AspectJ ясно объясняется здесь. В дальнейшем я буду использовать AOP вместо Spring AOP. В конце концов, это относительно ленивый автор.

Как использовать Spring АОП?

Чтобы изучить технологию, вы должны сначала научиться ее использовать, затем изучить принцип и, наконец, глубоко подумать и сделать выводы из одного случая. и"Лучший способ узнать, как его использовать, — это прочитать официальную документацию.".

Некоторым учащимся может быть неудобно читать по-английски, поэтому они будут читать некоторые статьи или книги на китайском языке. Однако эти китайские статьи или книги на самом деле являются переведенными официальными документами, которые могут быть неполными или даже неправильными. Поэтому рекомендуется читать официальные документы напрямую, даже если вы используете программное обеспечение для автоматического перевода, вы сможете их понять. Кроме того, английский язык также является одним из обязательных навыков для программистов.

АОП имеет некоторые специализированные понятия и терминологию. Аспекты, точки соединения, советы, точечные разрезы, импорт, целевые объекты, прокси-объекты, переплетение и т. д. Многие из них напрямую переведены с английских слов, мы не будем здесь подробно вводить эти понятия, подробное введение есть в начале главы официального документа Spring AOP. Но в этой статье некоторые из этих терминов будут представлены мимоходом с точки зрения их использования.

Включите поддержку AspectJ

В этой статье не будет представлена ​​конфигурация XML, она слишком устарела. . .

использовать@EnableAspectJAutoProxyЭта аннотация включает поддержку AspectJ, и вы можете с радостью использовать синтаксис AspectJ в Spring в будущем. должны знать о том,"Если вы используете SpringBoot, эта аннотация уже добавлена ​​по умолчанию.", не нужно вручную писать его в своем коде.

объявить аспект

использовать@AspectАннотация может объявлять аспект. По сути, это класс, в котором определены pointcuts, уведомления и прочее.

объявить pointcut

Так называемая «точка отреза» — это то место, где вы хотите отрезать. Spring поддерживает только резку"Методы Spring Managed Bean-компонентов". Объявить pointcut тоже очень просто, в объявленном нами выше классе аспекта создадим метод в следующем виде:

@Pointcut("execution(* transfer(..))")
private void anyOldTransfer() {}

использовать@PointcutАннотация, которая является выражением pointcut. Обратите внимание, что возвращаемое значение этого метода должно бытьvoidиз. Что касается выражений, есть некоторые ключевые слова, поддерживаемые Spring, которые здесь подробно обсуждаться не будут.Подробное введение есть в разделе Supported Pointcut Designators официальной документации. Нашим наиболее часто используемым должно бытьexecutionи@annotationЭти двое.

В нем также есть некоторые подстановочные знаки, в том числе*, ., (), (..), (*), (*, string)И так далее, у всех разные значения и функции. Подробности можно найти в официальной документацииExamplesУзнайте в этом разделе.

Уведомление о заявлении

«Уведомление» относится к тому, когда выполнять определяемую нами логику АОП. Spring предоставляет несколько типов уведомлений:

  • @Before
  • @AfterReturning
  • @AfterThrowing
  • @After, его суть AfterFinally
  • @Around

Каждый должен знать, что означает это имя. в,@AroundВключая все вышеперечисленные функции, он более мощный и гибкий в использовании.

Полное определение АОП выглядит так:

@Aspect
@Component
public class MyAspect {

    @Pointcut("within(com.example.springbase.dao..*)")
    private void myPointcut() {}


    @Before("myPointcut()")
    public void before() {
        System.out.println("before...");
    }
}

должен быть в курсе"здесь@ComponentАннотации должны быть добавлены, иначе Spring не будет автоматически сканировать этот класс", то заданные вами аспект, точка и совет будут недействительны.

Аспект может иметь несколько точек и несколько рекомендаций, а одна и та же точка может использоваться несколькими рекомендациями.

Что использует Spring для реализации АОП?

Ранее мы упоминали AspectJ.AspectJ использует переплетение во время компиляции и загрузки класса, а Spring AOP использует"Плетение во время выполнения". А если вы используете прошивку во время выполнения, вам необходимо использовать технологию «динамического прокси».

Сначала поговорим о динамических прокси. АОП на самом деле является приложением «прокси-режима» в шаблоне проектирования, так что же такое прокси-режим? Давайте возьмем очень распространенный пример — прокачку игры.

Игровой бустер взял деньги местного тирана, попал на счет местного тирана, всю дорогу выиграл лучшего короля, а потом завершил сделку и удалился. Товарищи по команде и противники в игре, и даже официальный представитель игры не знали, что это бустер, и думали, что это сам местный тиран. . .

Режим прокси простой, то есть через"Оберните исходный объект, чтобы предоставить некоторые дополнительные возможности",но"Незаметный снаружи", не знает или не заботится о том, проксируется объект или нет.

Разница между статическим прокси и динамическим прокси заключается в том, что статический прокси уже записал тип для проксирования, а прокси может проксировать только один тип. Динамический прокси отличается, прокси может проксировать несколько типов. Возьмем, к примеру, буст игры.Статический агент может заключаться в том, что этот бустер может только прокачать игру LOL. И динамический агент, эта прокачка мощности может повысить уровень всех игр, это отличная игра для прослушивания.

Нижний уровень Spring использует два способа реализации динамического прокси: один — динамический прокси, поставляемый с Java, а другой — CGLib. Если это прокси-объект, созданный с использованием динамического прокси-сервера JDK, Debug может видетьJdkDynamicAopProxy, и если это объект, созданный CGLib, вы можете видеть, что этоEnhancerBySpringCGLIB.

Итак, какой именно метод использует Spring? В Интернете есть много статей, в которых говорится, что поведение Spring по умолчанию для создания прокси-объектов таково: если ваш Bean имеет соответствующий интерфейс, он использует динамический прокси на основе JDK, в противном случае он использует CGLIB. Но это не точно, Spring использует следующую конфигурацию для управления, если эта конфигурация неверна, то это логика, которую мы сказали выше. И если эта конфигурация верна, все bean-компоненты, которые хотят использовать AOP, используют прокси-сервер CGLIB, независимо от того, есть ли у него интерфейс или нет. И если мы используем последнюю версию SpringBoot, это значение по умолчанию равно true.

spring.aop.proxy-target-class=true

а сейчас"При использовании SpringBoot все наши прокси-объекты AOP генерируются с помощью CGLIB.".

В чем разница между динамическими прокси JDK и CGLib?

Принципы этих двух реализаций различаются.Динамический прокси-сервер JDK реализован на основе отражения Java, а динамический прокси-сервер CGLIB реализован на основе изменения байт-кода и создания подклассов.Нижний уровень - это используемая библиотека с открытым исходным кодом asm.

Оба имеют некоторые ограничения, динамический прокси JDK, Bean должен иметь интерфейс, CGLIB не может проксировать окончательные классы или методы.

Какие методы можно проксировать?

Если используется динамический прокси JDK, проксировать можно только общедоступные методы. И если используется CGLIB, все методы, кроме private, могут быть проксированы. (кроме финальных методов, конечно).

Еще один интересный вопрос,"Если два метода находятся в одном классе, один метод не будет проходить через прокси при вызове другого метода.". Только один бин вызывает метод другого бина, прокси пойдет.

Вышеуказанные две характеристики также объясняют, почему иногда ваши@TransactionalПричины неэффективности:

  • Не работает на приватных методах
  • Не работает на окончательных методах
  • Вызов методов друг друга в одном классе не действует

Когда создается прокси-объект?

В предыдущей статье мы видели, как генерируются bean-компоненты Spring. Когда Spring запустится, будет вызван метод getBean для завершения инициализации Bean. В getBan, после инициализации Бина, он вызоветBeanPostProcessor. Этот код можно найти через метод Debug getBean. Я не буду вдаваться в подробности процесса отладки, просто покажу скриншот стека вызовов.

Как видно из отладки, один из BeanPostProcessors имеет тип AnnotationAwareAspectJAutoProxyCreator.Если вы продолжите отладку, вы увидите, что прокси-объект наконец сгенерирован методом getProxy() класса CglibAopProxy.

Что делать, если один и тот же метод делегирован несколько раз?

Метод может быть проксирован несколько раз. Spring AOP основан не только на шаблоне прокси, он также использует шаблон «перехватчик». Этот перехватчик, немного похожий на веб-перехватчик, оборачивает слой за слоем целевой объект, образуя цепочку перехватчиков. Как определяется их порядок?

В предыдущем анализе исходного кода у нас есть ветка, логика заключается в том, чтобы удалить все «уведомления» текущего прокси-объекта, а затем отсортировать. Код находится в методе класса AspectJAwareAdvisorAutoProxyCreator. стек вызовов:

Этот метод сначала удаляет все уведомления, а затем добавляет к ним AspectJPrecedenceComparator. Этот компаратор извлечет компонент, в котором находится уведомление.@OrderПриоритет определений аннотаций, отсортированных в соответствии с этим приоритетом. Фактически, мы иногда используем эту аннотацию при использовании других компонентов.

Поэтому, если вы объявляете несколько уведомлений для метода, вы можете использовать аннотацию @Order, чтобы определить приоритет этих нескольких уведомлений. Чем меньше значение, определяемое @Order, тем дальше во внешнем слое цепочки вызовов будет находиться перехватчик, соответствующий определенному в нем уведомлению.

Обратите внимание, что если в одном классе аспектов определено несколько уведомлений, они будут отсортированы в соответствии с порядком объявления методов.

Как насчет АОП и циклических зависимостей?

Возможно, вы сталкивались или слышали о циклических зависимостях Spring. Spring использует «кэш третьего уровня» для решения циклической зависимости bean-компонентов, но многие люди могут не знать, почему они используют кеш третьего уровня, на самом деле это также связано с АОП.

Фактически, если нет АОП, Spring может решить проблему циклических зависимостей за счет использования кеша второго уровня. Если используется кеш второго уровня, в случае АОП то, что вводится в другие компоненты, является не конечным прокси-объектом, а исходным целевым объектом.

Потому что Spring имеет определение жизненного цикла для Bean, а прокси-объект генерируется, когда постпроцессор выполняется после завершения инициализации Bean. Поэтому прокси-объекты нельзя генерировать непосредственно в кэше второго уровня и помещать в кэш.

Каковы недостатки использования АОП?

АОП не является панацеей, и у его использования есть некоторые недостатки. Лично я считаю большим недостатком то, что"Сделать код менее читаемым"Что ж, поскольку это не явный вызов, очень вероятно, что человек, который возьмет на себя код в будущем, не знает, что этот метод проксируется АОП.

Автор уже сталкивался с проектом, который использует АОП для управления разрешениями, но это управление разрешениями не является простым доступом, а проверяет некоторые поля в базе данных, такие как статус, а также сложную логическую оценку. В этом случае, если используется АОП, читабельность кода невелика, и устранить проблему в случае ее возникновения сложнее.

Об авторе

Я Ясин, колоритный и интересный программист.

Публичный аккаунт WeChat: составлена ​​программа

Персональный сайт: https://yasinshaw.com

Подписывайтесь на мой официальный аккаунт и развивайтесь вместе со мной~

公众号
публика