Яма Spring AOP: полностью понять порядок выполнения совета

Spring

Основные концепции АОП

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

  • Аспект: грань, модульный объект, состоящий из последовательности точечных сокращений, улучшений и импортов, которые определяют приоритеты, влияющие на порядок выполнения улучшений и импортов. Управление транзакциями — хороший пример аспекта в корпоративных приложениях Java.
  • Точка присоединения: точка доступа, момент во время выполнения программы, такой как выполнение метода, инициализация класса и обработка исключений. В Spring AOP точка доступа всегда представляет выполнение метода.
  • Совет: Улучшение, действие выполнения аспекта в конкретной точке доступа, включая «вокруг», «до» и «после» и другие типы. Многие фреймворки АОП, включая Spring, часто используют перехватчики для реализации улучшений, поддерживая цепочку перехватчиков вокруг точек доступа.
  • Pointcut: точка разреза, предикат (выражение), используемый для сопоставления с определенной точкой доступа, расширение будет связано с выражением pointcut и будет выполняться на любой точке доступа, соответствующей pointcut. Сопоставление точек доступа с помощью выражений pointcut является ядром АОП, и Spring по умолчанию использует выражения pointcut AspectJ.
  • Введение: введение, объявляет дополнительные методы и поля для типа. Spring AOP позволяет вам представить любой интерфейс и его реализацию по умолчанию для расширенного объекта.
  • Целевой объект: целевой объект, объект, дополненный одной или несколькими гранями. Также называется расширенным объектом. Поскольку Spring AOP использует прокси-серверы времени выполнения, целевой объект всегда является прокси-объектом.
  • Прокси-сервер AOP: прокси-сервер AOP, чтобы реализовать функцию аспекта, объект будет создан платформой АОП. Режим прокси-сервера AOP по умолчанию в среде Spring: если есть интерфейс, используйте динамический прокси-сервер JDK на основе интерфейса, в противном случае используйте динамический прокси-сервер CGLIB на основе классов. Но мы можем установитьproxy-target-class="true", полностью используя динамический прокси CGLIB.
  • Ткачество: Ткачество, который связывает один или несколько аспектов с классом или объектом для создания расширенного объекта. Переплетение может происходить во время компиляции (с использованием компилятора AspectJ), во время загрузки или во время выполнения. Spring AOP по умолчанию работает во время выполнения, доступ к которому можно получить через枚举AdviceModeустанавливать.

Моделирование выполнения рекомендации по аспекту

мы тутБольше не показывать тестовый код, а простым кодом дляМоделирование выполнения рекомендации по аспекту.

Хотя Spring АОП动态代理Чтобы добиться этого, но мы можем обойти прокси и напрямую имитировать процесс его выполнения, пример кода:

package doubt;
public class AspectAdviceInvokeProcess {
	public static void main(String[] args){
		try {
		    //正常执行
			AspectInvokeProcess(false);
			System.out.println("=====分割线=====");
			//异常执行
			AspectInvokeProcess(true);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 切面执行过程
	 * @param isException
	 * @throws Exception
	 */
	public static void AspectInvokeProcess(boolean isException) throws Exception{
		try {
			try {
				aroundAdvice(isException);
			} finally {
				afterAdvice();
			}
		    afterReturningAdvice();
		    return;
		} catch (Exception e) {
			afterThrowingAdvice(e);
			throw e;
			return;
		}	
	}
	
	/**
	 * 环绕增强
	 * @param isException
	 * @throws Exception
	 */
	private static void aroundAdvice(boolean isException) throws Exception {
		System.out.println("around before advice");
		try {
			JoinPoint_Proceed(isException);
		} finally {
			System.out.println("around after advice");
		}
	}
	
	/**
	 * 编织后的接入点执行过程
	 * @param isException
	 */
	public static void JoinPoint_Proceed(boolean isException){
		beforeAdvice();
		targetMethod(isException);
	}
	
	/**
	 * 前置增强
	 */
	private static void beforeAdvice() {
		System.out.println("before advice");
	}

	/**
	 * 目标方法
	 * @param isException
	 */
	private static void targetMethod(boolean isException) {
		System.out.println("target method 执行");
		if(isException)
			throw new RuntimeException("异常发生");
	}
	
	/**
	 * 后置增强
	 */
	private static void afterAdvice() {
		System.out.println("after advice");
	}

	/**
	 * 正常返回增强
	 */
	private static void afterReturningAdvice() {
		System.out.println("afterReturning");
	}

	/**
	 * 异常返回增强
	 * @param e
	 * @throws Exception
	 */
	private static void afterThrowingAdvice(Exception e) throws Exception {
		System.out.println("afterThrowing:"+e.getMessage());
	}
}

Тот же аспект, порядок выполнения разных советов

Результат выполнения приведенного выше кода напрямую отражает同一apsect中不同advice的Последовательность выполнения, результат следующий:

around before advice
before advice
target method 执行
around after advice
after advice
afterReturning
===============分割线==============
around before advice
before advice
target method 执行
around after advice
after advice
afterThrowing:异常发生
java.lang.RuntimeException: 异常发生

получить заключение:

这里写图片描述


Порядок выполнения различных аспектов и советы

Подробнее см. в "Официальной документации Spring"docs.spring.IO/весна/документы…

Spring AOP, указавaspectприоритет управления不同aspect,advice的执行顺序, есть два пути:

  • Добавлен класс аспектааннотация: org.springframework.core.annotation.Order с использованием аннотацийvalueАтрибуты определяют приоритет.

  • Реализация класса аспектаинтерфейс: org.springframework.core.Ordered, который реализует интерфейс Ordered.getOrder()метод.

Среди них чем ниже значение, тем выше приоритет,@OrderПо умолчанию используется самый низкий приоритет, который является наибольшим значением:

    /**
	 * Useful constant for the lowest precedence value.
	 * @see java.lang.Integer#MAX_VALUE
	 */
	int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

наконец-то,不同aspect,advice的执行顺序:

  • Ввод операций (Вокруг (до выполнения точки доступа), До), чем выше приоритет, тем раньше выполняется;
  • После завершения операции входа одного аспекта наступает очередь следующего аспекта.После завершения всех операций входа аспекта выполняется точка доступа;
  • Операции Out (Вокруг (после выполнения точки доступа), After, AfterReturning, AfterThrowing), чем ниже приоритет, тем раньше она выполняется.
  • После выполнения операции выхода одного аспекта наступает очередь следующего аспекта, пока он не вернется в точку вызова;

Как показано ниже:

这里写图片描述

Первый пришел, последний вышел, последний пришел первым вышел


Тот же аспект, порядок исполнения того же совета

同一aspect,相同advice的执行顺序не может быть определено непосредственно и@OrderсуществуетadviceЭто также недопустимо в методе, но есть два обходных пути:

  • Объедините два совета в один совет, тогда порядок выполнения можно будет контролировать с помощью кода
  • Разделите два совета на их собственные аспекты и укажите порядок выполнения аспектов.

Приоритет транзакционного аспекта

Управление транзакциями Spring (Управление транзакциями) также основано на Spring AOP.

При использовании Spring AOP иногда нам приходится четко указывать, что приоритет пользовательского аспекта ниже или выше, чем аспект транзакции (Transaction Aspect), поэтому нам нужно знать:

  • Приоритет аспекта транзакции: по умолчанию самый низкий приоритет
LOWEST_PRECEDENCE = Integer.MAX_VALUE
  • Улучшение типа транзакции: Вокруг совета, на самом деле, нетрудно понять.Ввод метода запускает транзакцию, а выход из метода фиксирует или откатывает, поэтому требуется объемное улучшение.
public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport implements DisposableBean {

   protected AbstractTransactionAspect(TransactionAttributeSource tas) {
   	setTransactionAttributeSource(tas);
   }

   @SuppressAjWarnings("adviceDidNotMatch")
   Object around(final Object txObject): transactionalMethodExecution(txObject) {
   	MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
   	// Adapt to TransactionAspectSupport's invokeWithinTransaction...
   	try {
   		return invokeWithinTransaction(methodSignature.getMethod(), txObject.getClass(), new InvocationCallback() {
   			public Object proceedWithInvocation() throws Throwable {
   				return proceed(txObject);
   			}
   		});
   	}
   	catch (RuntimeException ex) {
   		throw ex;
   	}
   	catch (Error err) {
   		throw err;
   	}
   	catch (Throwable thr) {
   		Rethrower.rethrow(thr);
   		throw new IllegalStateException("Should never get here", thr);
   	}
   }
}
  • Как изменить приоритет аспекта транзакции: При открытии сделки, установив@EnableTransactionManagementи<tx:annotation-driven/>середина,orderсвойство для изменения приоритета аспекта транзакции. Подробнее см. в "Официальной документации Spring"docs.spring.IO/весна/документы…