Что еще вы знаете о Spring AOP помимо динамических прокси и CGLIB?

Spring

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

1. Концепция АОП

1.1 JoinPoint

Точка выполнения программы, в которой происходит операция плетения.

Общие типы:

  • Вызов метода: точка, в которой был вызван метод.

  • Выполнение вызова метода: время, когда метод начинает выполняться внутри.

    Вызов метода находится ввызывающий объектТочка выполнения при выполнении вызова метода находится ввызываемый объектМетод запускает точку выполнения.

  • Вызов конструктора: точка, в которой вызывается конструктор объекта.

  • Выполнение вызова конструктора: точка, в которой начинается выполнение внутри конструктора объекта.

  • Набор полей: Момент, когда поле устанавливается методом установки или напрямую.

  • Поле Получить: точка доступа к полю через метод получения или напрямую.

  • Выполнение обработчика исключений: Точка, в которой выполняется логика обработки исключений после создания некоторых типов исключений.

  • Инициализация класса: точка инициализации некоторого статического типа или статического блока в классе.

1.2 Pointcut

То, как выражается точка соединения.

Общие выражения:

  • Непосредственно укажите имя метода, в котором находится точка соединения.
  • регулярное выражение
  • конкретный язык представления Pointcut

1.3 Advice

Носитель единой сквозной логики концерна, вплетенной в сквозную логику Joinpoint.

Конкретные формы:

  • Before Advice: выполняется перед точкой присоединения.
  • After Advice: Выполняется после точки присоединения, подразделяется на три типа:
    • After Returning Advice: выполняется после нормального завершения точки присоединения.
    • After Throwing Advice: выполняется после возникновения исключения в точке соединения.
    • After Finally Advice: выполняется после нормального завершения точки соединения или возникновения исключения.
  • Around Advice: Оборачивает точку присоединения, выполняется до и после точки присоединения, с функциями «До консультации» и «После консультации».
  • Introduction: добавление новых свойств или поведения к существующим объектам.

1.4 Aspect

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

1.5 Плетение и плетение

Плетение: интегрируйте сквозные аспекты модульности Aspect в систему ООП.

Weaver: используется для завершения операции ткачества.

1.6 Target

Объекты, которые вплетаются в сквозную логику в процессе плетения.

Соедините вышеприведенные 6 концепций вместе, как показано на следующем рисунке:

AOP各个概念所处的场景

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

2. Реализация весной

Как упоминалось выше, существует много типов точек соединения АОП, таких как вызов метода, выполнение метода, настройка поля, получение поля и т. д. В Spring AOP поддерживает тольковыполнение методаТипа точки присоединения, но это уже может удовлетворить 80% потребностей в разработке.Если у вас есть особые потребности, вы можете обратиться к другим продуктам АОП, таким как AspectJ. Поскольку Joinpoint включает в себя процесс времени выполнения, он эквивалентен последнему шагу в сборке всех компонентов для запуска АОП. Поэтому, представив реализацию других концепций, мы, наконец, представим реализацию Joinpoint.

2.1 Pointcut

Поскольку Spring AOP поддерживает только Joinpoint из категории выполнения метода, Pointcut необходимо определить метод, который необходимо связать, а поскольку методы в Java инкапсулированы в классы, Pointcut необходимоОпределить вплетенные классы и методы, см. его реализацию ниже.

На веснуorg.springframework.aop.PointcutИнтерфейс определяет абстракцию верхнего уровня Pointcut.

public interface Pointcut {
    
   // ClassFilter用于匹配被织入的类   
   ClassFilter getClassFilter();

   // MethodMatcher用于匹配被织入的方法
   MethodMatcher getMethodMatcher();

   // TruePoincut的单例对象,默认匹配所有类和方法
   Pointcut TRUE = TruePointcut.INSTANCE;
}

Мы видим, что,Pointcutпройти черезClassFilterа такжеMethodMatcherдля определения соответствующей точки соединения.PointcutКлассы и методы определяются отдельно, чтобы иметь возможностьповторное использование. Например, есть две точки соединения типа A.fun()метод и класс Bfun()метод, два метода имеют одинаковую сигнатуру, только одинfun()методMethodMatcherобъекта, для достижения цели повторного использования,ClassFilterТо же самое справедливо.

Узнайте нижеClassFilterа такжеMethodMatcherкак соответствовать.

ClassFilterиспользовать**matchesМетод ** соответствует вплетаемому классу и определяется следующим образом:

public interface ClassFilter {
    
    // 匹配被织入的类,匹配成功返回true,失败返回false
    boolean matches(Class<?> clazz);

    // TrueClassFilter的单例对象,默认匹配所有类
    ClassFilter TRUE = TrueClassFilter.INSTANCE;
}

MethodMatcherтакже использоватьmatchesметодСопоставление вплетено в метод, определяемый следующим образом:

public interface MethodMatcher {
    
   // 匹配被织入的方法,匹配成功返回true,失败返回false
   // 不考虑具体方法参数
   boolean matches(Method method, Class<?> targetClass);
    
   // 匹配被织入的方法,匹配成功返回true,失败返回false
   // 考虑具体方法参数,对参数进行匹配检查
   boolean matches(Method method, Class<?> targetClass, Object... args);
   
   // 一个标志方法
   // false表示不考虑参数,使用第一个matches方法匹配
   // true表示考虑参数,使用第二个matches方法匹配
   boolean isRuntime();

   // TrueMethodMatcher的单例对象,默认匹配所有方法
   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

Видетьmatchesобъявление метода, как вы думаете, это немного странно, вClassFilterРазве класс уже не соответствуетMethodMatcherизmatchesСуществует также метод вClass<?> targetClassпараметр. Обратите внимание, что здесьClass<?>Параметр типа будетне будет соответствовать, но толькодля того, чтобы найти определенный метод. Например:

public boolean matches(Method method, Class<?> targetClass) {
    Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    ...
}

существуетMethodMatcherв сравнении сClassFilterособенный вдваmatchesметод. будетсогласно сisRuntime()Обратный результат решаетЧто звонить. а такжеMethodMatcherпотому чтоisRuntime()на два абстрактных классаStaticMethodMatcher(возвращает false независимо от аргументов) иDynamicMethodMatcher(возвращает true с учетом параметров).

PointcutТакже из-заMethodMathcerЕго можно разделить наStaticMethodMatcherPointcutа такжеDynamicMethodMatcherPointcut, соответствующая диаграмма классов выглядит следующим образом:

Pointcut相关类图

DynamicMethodMatcherPointcutВ этой статье мы не будем вводить, в основном представим три класса реализации, перечисленные на следующей диаграмме классов.

(1) NameMatchMethodPointcut

указавимя метода, которое затем напрямую сопоставляется с именем метода, а также поддерживается подстановочный знак «*».

public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {
    
    // 方法名称
    private List<String> mappedNames = new ArrayList<>();

    // 设置方法名称
    public void setMappedNames(String... mappedNames) {
        this.mappedNames = new ArrayList<>(Arrays.asList(mappedNames));
    }


    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        for (String mappedName : this.mappedNames) {
            // 根据方法名匹配,isMatch提供“*”通配符支持
            if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
                return true;
            }
        }
        return false;
    }
    
    // ...
}

(2) JdkRegexpMethodPointcut

Внутри находится массив паттернов, указаврегулярное выражение, а затем сопоставьте с именем метода.

(3) AnnotationMappingPointcut

В зависимости от того, существует ли целевой объект указанного типааннотациясоответствовать.

2.2 Advice

Advice является носителем сквозной логики.Диаграмма интерфейсных классов Advice в Spring AOP выглядит следующим образом:

Advice相关类图

(1) Метод Перед советом

Сквозная логика будет вМетод точки присоединения выполняется до. Может использоваться для инициализации ресурсов или подготовительных работ.

public interface MethodBeforeAdvice extends BeforeAdvice {
    
    void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
    
}

Давайте реализуемMethodBeforeAdvice, и увидеть его эффект.

public class PrepareResourceBeforeAdvice implements MethodBeforeAdvice {
    
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("准备资源");
    }
    
}

определитьITaskинтерфейс:

public interface ITask {
    
    void execute();
    
}

ITaskкласс реализацииMockTask:

public class MockTask implements ITask {
    
   @Override
   public void execute() {
      System.out.println("开始执行任务");
      System.out.println("任务完成");
   }
    
}

Основной метод заключается в следующем:ProxyFactory,AdvisorВ последующем будет введено, сначала кратко понять, черезProxyFactoryполучить класс прокси,Advisorдля упаковкиPointcutа такжеAdvice.

public class Main {
    
   public static void main(String[] args) {
      MockTask task = new MockTask();
      ProxyFactory weaver = new ProxyFactory(task);
      weaver.setInterfaces(new Class[]{ITask.class});
      // 内含一个NameMatchMethodPointcut
      NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
      // 指定NameMatchMethodPointcut的方法名
      advisor.setMappedName("execute");
      // 指定Advice
      advisor.setAdvice(new PrepareResourceBeforeAdvice());
      weaver.addAdvisor(advisor);
      ITask proxyObject = (ITask) weaver.getProxy();
      proxyObject.execute();
   }
    
}

/** output:
准备资源
开始执行任务
任务完成
**/

Видно, что прокси-объект выполняетсяproxyObjectизexecuteметод, выполнить первымPrepareResourceBeforeAdviceсерединаbeforeметод.

(2) ThrowsAdvice

Сквозная логика будет вВыполняется, когда метод Joinpoint выдает исключение. Может использоваться для нештатной работы мониторинга.

Интерфейс ThrowsAdvice не определяет никаких методов, но согласовано, что при реализации этого интерфейсаОпределенный метод должен соответствовать следующим правилам:

void afterThrowing([Method, args, target], ThrowableSubclass)

Первые три параметра — это связанная информация о точке присоединения, которую можно опустить.ThrowableSubclassУказывает тип исключения, которое необходимо перехватить.

Например, вы можете определить несколькоafterThrowingМетод перехватывает исключение:

public class ExceptionMonitorThrowsAdvice implements ThrowsAdvice {
    
    public void afterThrowing(Throwable t) {
        System.out.println("发生【普通异常】");
    }
    
    public void afterThrowing(RuntimeException e) {
        System.out.println("发生【运行时异常】");
    }
    
    public void afterThrowing(Method m, Object[] args, Object target, ApplicationException e) {
        System.out.println(target.getClass() + m.getName() + "发生【应用异常】");
    }
    
}

изменитьMockTaskСодержание:

public class MockTask implements ITask {
    
    @Override
    public void execute() {
        System.out.println("开始执行任务");
        // 抛出一个自定义的应用异常
        throw new ApplicationException();
        // System.out.println("任务完成");
    }
    
}

изменитьMainСодержание:

public class Main {
    
    public static void main(String[] args) {
        MockTask task = new MockTask();
        ProxyFactory weaver = new ProxyFactory(task);
        weaver.setInterfaces(new Class[]{ITask.class});
        NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
        advisor.setMappedName("execute");
        // 指定异常监控Advice
        advisor.setAdvice(new ExceptionMonitorThrowsAdvice());
        weaver.addAdvisor(advisor);
        ITask proxyObject = (ITask) weaver.getProxy();
        proxyObject.execute();
    }
    
}

/** output:
开始执行任务
class com.chaycao.spring.aop.MockTaskexecute发生【应用异常】
**/

когда брошеноApplicationExceptionкогда соответствующийafterThrowingметод захвачен.

(3) Совет после возврата

Сквозная логика будет вВыполняется, когда метод Joinpoint возвращается нормально. Может использоваться для очистки ресурсов.

public interface AfterReturningAdvice extends AfterAdvice {
    
   void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
    
}

Реализуйте совет по очистке ресурсов:

public class ResourceCleanAfterReturningAdvice implements AfterReturningAdvice {
    
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
      System.out.println("资源清理");
   }
    
}

ИсправлятьMockTaskДля успешного нормального выполнения изменитеMainуказан методResourceCLeanAfterReturningAdvice, эффект следующий:

/** output:
开始执行任务
任务完成
资源清理
**/

(4) МетодПерехватчик

Эквивалент Around Advice, функция очень мощная,Может выполняться до и после метода Joinpoint и даже изменять возвращаемое значение.. Он определяется следующим образом:

public interface MethodInterceptor extends Interceptor {
    
    Object invoke(MethodInvocation invocation) throws Throwable;
    
}

MethodInvocationправдаMethodпакет, черезproceed()сделать вызов метода. Вот пример:

public class AroundMethodInterceptor implements MethodInterceptor {

   @Override
   public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("准备资源");
      try {
         return invocation.proceed();
      } catch (Exception e) {
         System.out.println("监控异常");
         return null;
      } finally {
         System.out.println("资源清理");
      }
   }
   
}

Реализованный выше метод invoke реализует сразу все три упомянутые выше функции.

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

(5) Введение

Весна - целевой объектДобавить новые свойства или поведения,необходимостьОбъявить интерфейс и класс его реализации, то черезперехватчикВплетает определение интерфейса и реализацию реализующего класса в целевой объект. будем знакомыDelegatingIntroductionInterceptor, который действует как перехватчик и делегирует класс реализации при вызове нового поведения.

Например, если вы хотитеMockTaskУлучшение вышеизложенного, но без изменения объявления класса, может объявить новый интерфейсIReinfore:

public interface IReinforce {
   String name = "增强器";
   void fun();
}

Затем объявите класс реализации интерфейса:

public class ReinforeImpl implements IReinforce {

    @Override
    public void fun() {
        System.out.println("我变强了,能执行fun方法了");
    }

}

Измените основной метод:

public class Main {
   
   public static void main(String[] args) {
      MockTask task = new MockTask();
      ProxyFactory weaver = new ProxyFactory(task);
      weaver.setInterfaces(new Class[]{ITask.class});
      // 为拦截器指定需要委托的实现类的实例
      DelegatingIntroductionInterceptor delegatingIntroductionInterceptor =
            new DelegatingIntroductionInterceptor(new ReinforeImpl());
      weaver.addAdvice(delegatingIntroductionInterceptor);
      ITask proxyObject = (ITask) weaver.getProxy();
      proxyObject.execute();
      // 使用IReinfore接口调用新的属性和行为
      IReinforce reinforeProxyObject = (IReinforce) weaver.getProxy();
      System.out.println("通过使用" + reinforeProxyObject.name);
      reinforeProxyObject.fun();
   }
   
}

/** output:
开始执行任务
任务完成
通过使用增强器
我变强了,能执行fun方法了
**/

прокси-объектproxyObjectЗатем через перехватчик вы можете использоватьReinforeImplРеализовать методы класса.

2.3 Aspect

Используется веснойAdvisorПредставляет Аспект, за исключением того, чтоAdvisorобычно держат толькоОдинPointcutа такжеОдинAdvice.Advisorсогласно сAdviceразделен наPointcutAdvisorа такжеIntroductionAdvisor.

2.3.1 PointcutAdvisor

общийPointcutAdvisorКлассы реализации:

(1) DefaultPointcutAdvisor

Самый общий класс реализации, который можно указатьлюбого типаPointcutа такжеКромеIntroductionлюбого типа, кромеAdvice.

Pointcut pointcut = ...; // 任意类型的Pointcut
Advice advice = ...; // 除了Introduction外的任意类型Advice
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(advice);

(2) NameMatchMethodPointcutAdvisor

В коде, который демонстрирует Advice, он был кратко представлен,есть один внутриNameMatchMethodPointcutэкземпляр, может содержать кромеIntroductionлюбого типа, кромеAdvice.

Advice advice = ...; // 除了Introduction外的任意类型Advice
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.setMappedName("execute");
advisor.setAdvice(advice);

(3) советник RegexpMethodPointcutAdvisor

есть один внутриRegexpMethodPointcutпример.

2.3.2 IntroductionAdvisor

может поддерживать только перехват на уровне класса, иIntroductionТипAdvice. Класс реализации имеетDefaultIntroductionAdvisor.

DelegatingIntroductionInterceptor introductionInterceptor =
				new DelegatingIntroductionInterceptor(new ReinforeImpl());
DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(introductionInterceptor, IReinforce.class);

2.4 Плетение и плетение

В коде, который демонстрирует Advice, мы используемProxyFactoryкак ткач

MockTask task = new MockTask();
// 织入器
ProxyFactory weaver = new ProxyFactory(task);
weaver.setInterfaces(new Class[]{ITask.class});
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.setMappedName("execute");
advisor.setAdvice(new PrepareResourceBeforeAdvice());
weaver.addAdvisor(advisor);
// 织入,返回代理对象
ITask proxyObject = (ITask) weaver.getProxy();
proxyObject.execute();

ProxyFactoryСпособы создания прокси-объектов следующие:

  • Если целевой класс реализует некоторый интерфейс, по умолчаниюДинамический проксигенерировать.
  • Если целевой класс не реализует интерфейс, по умолчаниюCGLIBгенерировать.
  • также можетустановить напрямуюProxyFactoryСпособ его создания, даже если интерфейс реализован, CGLIB можно использовать.

В предыдущем демонстрационном коде мы не запускали контейнер Spring, то есть не использовали функцию Spring IOC, а использовали Spring AOP самостоятельно. Так как же Spring AOP интегрируется со Spring IOC? Это наиболее часто используемый метод интеграции Spring.FactoryBean.

ProxyFactoryBeanнаследоватьProxyFactoryродительский классProxyCreatorSupport, с возможностью создания прокси-классов, при реализацииFactoryBeanинтерфейс, при прохожденииgetObjectКогда метод получает bean-компонент, он получает прокси-класс.

2.5 Target

В предыдущем демонстрационном коде мы напрямуюProxyFactoryУкажите объект в качестве цели. существуетProxyFactoryBeanможно использовать не только таким образом, но иTargetSourceуказано в форме.

TargetSourceЭто эквивалентно слою инкапсуляции для объекта,ProxyFactoryBeanпройдешьTargetSourceизgetTargetметод получения целевого объекта. Итак, мы можем пройтиgetTargetпуть кУправляйте полученным целевым объектом.TargetSourceНесколько классов реализации:

(1) SingletonTargetSource

Очень просто, только один целевой объект хранится внутри и возвращается напрямую. Эффект тот же, что и при прямом указании объекта.

(2) ПрототипТаржетСаурце

Каждый раз будет возвращаться новый экземпляр целевого объекта.

(3) HotSwappableTartgetSource

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

(4) CommonsPool2TargetSource

Возвращает ограниченное количество экземпляров целевого объекта, аналогично пулу объектов.

(5) ThreadLocalTargetSource

Предоставляйте разные целевые объекты для разных вызовов потоков

2.6 Joinpoint

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

MockTask task = new MockTask();
ProxyFactory weaver = new ProxyFactory(task);
weaver.setInterfaces(new Class[]{ITask.class});
PrepareResourceBeforeAdvice beforeAdvice = new PrepareResourceBeforeAdvice();
ResourceCleanAfterReturningAdvice afterAdvice = new ResourceCleanAfterReturningAdvice();
weaver.addAdvice(beforeAdvice);
weaver.addAdvice(afterAdvice);
ITask proxyObject = (ITask) weaver.getProxy();
proxyObject.execute();

/** output   
准备资源    
开始执行任务     
任务完成    
资源清理   
**/

мы знаемgetProxyсоздаст динамический проксиITaskкласс интерфейса, затемexecuteКак внутренности метода выполняются в первую очередьbeforeAdviceизbeforeметод, затем выполнитеtaskизexecuteметод, выполнитьafterAdviceизafterметод?

Ответ генерируетсяпрокси-класссередина. В динамическом прокси логика вызова метода класса прокси определяетсяInvocationHandlerпримерinvokeметод, ответ дополнительно заблокированinvokeметод.

В этом примереProxyFactory.getProxyпозвонюJdkDynamicAopProxy.getProxyПолучите прокси-класс.

// JdkDynamicAopProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

существуетgetProxyЧжунвэйnewProxyInstanceизInvocationHandlerввод параметровthis,СейчасJdkDynamicAopProxyтолько одинInvocationHandlerреализация, котораяinvokeМетоды, как показано ниже:

// JdkDynamicAopProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 通过advised(创建对象时初始化)获得指定的advice
    // 会将advice用相应的MethodInterceptor封装下
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    if (chain.isEmpty()) {
        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
        retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    }
    else {
        // 创建一个MethodInvocation
        MethodInvocation invocation =
            new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // 调用procced,开始进入拦截链(执行目标对象方法和MethodInterceptor的advice)
        retVal = invocation.proceed();
    }
    return retVal;
}

Сначала получите указанный совет, который содержитbeforeAdviceа такжеafterAdviceпример, но буду использоватьMethodInterceptorИнкапсулируйте слой для последующей цепочки перехвата.

создать еще одинRelectiveMethodInvocationобъект, наконец, черезproceedВойдите в цепочку перехвата.

RelectiveMethodInvocationЭто реализация Joinpoint в Spring AOP.Его диаграмма классов выглядит следующим образом:

Joinpoint类图

Первый взглядRelectiveMethodInvocationконструктор:

protected ReflectiveMethodInvocation(
			Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
			@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
    this.proxy = proxy;
    this.target = target;
    this.targetClass = targetClass;
    this.method = BridgeMethodResolver.findBridgedMethod(method);
    this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
    this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}

Выполните несколько назначений связанных свойств, а затем посмотрите наproceedметод, как вызывать целевой объект и перехватчики.

public Object proceed() throws Throwable {
    // currentInterceptorIndex从-1开始
    // 当达到已调用了所有的拦截器后,通过invokeJoinpoint调用目标对象的方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    // 获得拦截器,调用其invoke方法
    // currentInterceptorIndex加1
    Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}

currentInterceptorIndexначиная с -1,interceptorsAndDynamicMethodMatchersЕсть два перехватчика, и из-за вычитания 1 все условия для вызова метода целевого объекта равныcurrentInterceptorIndexравно 1.

Во-первых, потому что-1 != 1, будет включеноbeforeAdviceизMethodBeforeAdviceInterceptorпример,currentInterceptorIndexДобавьте 1, чтобы получить 0. назовите егоinvokeметод, потому что это предварительный совет, поэтому сначала выполнитеbeforeAdviceизbeforeметод, затем вызовитеproceedВведите следующее звено в цепочке перехвата.

// MethodBeforeAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}

назад сноваproceedметод,0 != 1, снова получая совет, на этот раз содержащийafterAdviceизAfterReturningAdviceInterceptorпример,currentInterceptorIndexДобавьте 1, чтобы стать 1. назовите егоinvokeметод, так как это After-Returning-Adivce, он будет выполнен первымproceedВведите следующее звено в цепочке перехвата.

// AfterReturningAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    Object retVal = mi.proceed();
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}

приходи ещеproceedметод,1 == 1, все перехватчики были вызваны, и будут выполнены методы целевого объекта. затем возврат возвращается, обратно вinvoke, вызовafterAdviceизafterReturning.

Итак, при реализации Joinpoint передайтеMethodInterceptorПоследовательное выполнение метода целевого объекта и Advice завершено.

3. Резюме

После понимания реализации Spring AOP у автора появилось более четкое представление об АОП. В процессе обучения меня больше всего заинтересовала цепочка перехвата от Joinpoint, я сначала не знал, как это реализовать, и думал, что это потрясающе 😲 . Наконец-то доучился, подвел итог, вроде бы очень просто, через перехватчикinvokeМетоды иMethodInvocation.proceedВзаимный вызов методов (переход к следующему перехватчику). Похоже, это так. 😛