Разница между @target и @within (Spring AOP) - PullRequest
0 голосов
/ 01 июля 2018

Пружинная инструкция гласит:

любая точка соединения (выполнение метода только в Spring AOP), где цель Объект имеет аннотацию @Transactional: @target (org.springframework.transaction.annotation .Transactional)

любая точка соединения (выполнение метода только в Spring AOP), где объявленный тип целевого объекта имеет аннотацию @Transactional: @within (org.springframework.transaction.annotation .Transactional)

Но я не вижу никакой разницы между ними!

Я пытался Google это:

Одно из различий между ними состоит в том, что @within () сопоставляется статически, требуя, чтобы соответствующий тип аннотации имел только удержание КЛАССА. Принимая во внимание, что @target () сопоставляется во время выполнения, требуя, чтобы то же самое имело удержание ВРЕМЕНИ. Кроме этого, в контексте Spring, здесь нет разницы между точек, выбранных двумя.

Поэтому я попытался добавить пользовательскую аннотацию с сохранением CLASS , но Spring выбросил исключение (поскольку аннотация должна иметь RUNTIME retention)

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

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

Итак, давайте построим очень простой пример AspectJ:

Маркерная аннотация:

package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}

Приложение драйвера:

package de.scrum_master.app;

@MyAnnotation
public class Application {
  private int nonStaticMember;
  private static int staticMember;

  public void doSomething() {
    System.out.println("Doing something");
    nonStaticMember = 11;
  }

  public void doSomethingElse() {
    System.out.println("Doing something else");
    staticMember = 22;
  }

  public static void main(String[] args) {
    Application application = new Application();
    application.doSomething();
    application.doSomethingElse();
  }
}

Формат:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MyAspect {
  @Before("@within(de.scrum_master.app.MyAnnotation) && execution(public !static * *(..))")
  public void adviceAtWithin(JoinPoint thisJoinPoint) {
    System.out.println("[@within] " + thisJoinPoint);
  }

  @Before("@target(de.scrum_master.app.MyAnnotation) && execution(public !static * *(..))")
  public void adviceAtTarget(JoinPoint thisJoinPoint) {
    System.out.println("[@target] " + thisJoinPoint);
  }
}

Обратите внимание, что здесь я эмулирую поведение Spring AOP, добавив && execution(public !static * *(..)) к обоим pointcuts.

Журнал консоли:

[@within] execution(void de.scrum_master.app.Application.doSomething())
[@target] execution(void de.scrum_master.app.Application.doSomething())
Doing something
[@within] execution(void de.scrum_master.app.Application.doSomethingElse())
[@target] execution(void de.scrum_master.app.Application.doSomethingElse())
Doing something else

Не удивительно, что здесь. Это именно то, что вы также увидите в Spring AOP. Теперь, если вы удалите часть && execution(public !static * *(..)) из обоих pointcut, в Spring AOP вывод останется прежним, но в AspectJ (например, также если вы активируете AspectJ LTW в Spring) он изменится на:

[@within] staticinitialization(de.scrum_master.app.Application.<clinit>)
[@within] execution(void de.scrum_master.app.Application.main(String[]))
[@within] call(de.scrum_master.app.Application())
[@within] preinitialization(de.scrum_master.app.Application())
[@within] initialization(de.scrum_master.app.Application())
[@target] initialization(de.scrum_master.app.Application())
[@within] execution(de.scrum_master.app.Application())
[@target] execution(de.scrum_master.app.Application())
[@within] call(void de.scrum_master.app.Application.doSomething())
[@target] call(void de.scrum_master.app.Application.doSomething())
[@within] execution(void de.scrum_master.app.Application.doSomething())
[@target] execution(void de.scrum_master.app.Application.doSomething())
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something
[@within] set(int de.scrum_master.app.Application.nonStaticMember)
[@target] set(int de.scrum_master.app.Application.nonStaticMember)
[@within] call(void de.scrum_master.app.Application.doSomethingElse())
[@target] call(void de.scrum_master.app.Application.doSomethingElse())
[@within] execution(void de.scrum_master.app.Application.doSomethingElse())
[@target] execution(void de.scrum_master.app.Application.doSomethingElse())
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something else
[@within] set(int de.scrum_master.app.Application.staticMember)

При подробном рассмотрении вы видите, что перехватывается намного больше @within() точек соединения, но также и несколько @target() точек, например, call() точек соединения, упомянутых выше, но также set() для нестатических полей и объекта initialization(), возникающих до выполнения конструктора.

Когда мы смотрим только на @target(), мы видим это:

[@target] initialization(de.scrum_master.app.Application())
[@target] execution(de.scrum_master.app.Application())
[@target] call(void de.scrum_master.app.Application.doSomething())
[@target] execution(void de.scrum_master.app.Application.doSomething())
Doing something
[@target] set(int de.scrum_master.app.Application.nonStaticMember)
[@target] call(void de.scrum_master.app.Application.doSomethingElse())
[@target] execution(void de.scrum_master.app.Application.doSomethingElse())
Doing something else

Для каждой из этих выходных строк аспекта мы также видим соответствующее @within() совпадение. Теперь давайте сконцентрируемся на том, что не то же самое, отфильтровывая выходные данные по различиям:

[@within] staticinitialization(de.scrum_master.app.Application.<clinit>)
[@within] execution(void de.scrum_master.app.Application.main(String[]))
[@within] call(de.scrum_master.app.Application())
[@within] preinitialization(de.scrum_master.app.Application())
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something
[@within] get(PrintStream java.lang.System.out)
[@within] call(void java.io.PrintStream.println(String))
Doing something else
[@within] set(int de.scrum_master.app.Application.staticMember)

Вот видите, в порядке появления

  • статическая инициализация класса,
  • статический метод выполнения,
  • вызов конструктора (еще не выполнено!),
  • предварительная инициализация построенного объекта,
  • доступ к переменной-члену в другом классе (System.out),
  • вызов метода из другого класса (PrintStream.println(String)),
  • установка статического члена класса.

Что общего у всех этих pointcut? Целевого объекта не существует, потому что мы говорим о статических методах или членах, инициализации статического класса, предварительной инициализации объекта (пока не определено this) или о вызове / доступе к вещам из других классов, не содержащих аннотацию, на которую мы нацелены.

Итак, вы видите, что в AspectJ есть существенные различия между двумя pointcut, в Spring AOP они просто не заметны из-за его ограничений.

Мой совет для вас - использовать @target(), если вы хотите перехватить нестатическое поведение в экземпляре целевого объекта. Это облегчит переключение на AspectJ, если вы когда-нибудь решите активировать режим AspectJ в Spring или даже перенести некоторый код в приложение, не поддерживающее Spring, с поддержкой аспектов.

0 голосов
/ 01 июля 2018

Информация, которую вы указали, верна, однако только @target указатели pointcut требуют аннотации с удержанием RUNTIME, тогда как @within требует только CLASS удержание.

Рассмотрим следующие две простые аннотации:

ClassRetAnnotation.java

package mypackage;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.CLASS)
public @interface ClassRetAnnotation {}

RuntimeRetAnnotation.java

package mypackage;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeRetAnnotation {}

Теперь, если вы определите аспект, подобный следующему, не будет исключения во время выполнения :

@Component
@Aspect
public class MyAspect {

    @Before("@within(mypackage.ClassRetAnnotation)")
    public void within() { System.out.println("within"); }

    @Before("@target(mypackage.RuntimeRetAnnotation)")
    public void target() { System.out.println("target"); }
}

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

Ссылка на пружину: https://docs.spring.io/spring/docs/5.0.x/spring-framework-reference/core.html#aop-pointcuts

...