此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

AOP 示例

现在您已经了解了所有组成部分的工作原理,我们可以将它们放在一起来做 有用的东西。spring-doc.cadn.net.cn

业务服务的执行有时会由于并发问题而失败(对于 例如,死锁失败者)。如果重试作,则很可能会成功 在下一次尝试中。对于适合重试的业务服务 条件(冪等作,不需要返回给用户进行冲突 resolution),我们希望透明地重试作,以避免客户端看到PessimisticLockingFailureException.这是一个明显跨越的要求 服务层中的多个服务,因此非常适合通过 方面。spring-doc.cadn.net.cn

因为我们想重试作,所以我们需要使用 around 建议,以便我们可以 叫proceed多次。以下列表显示了基本方面实现:spring-doc.cadn.net.cn

@Aspect
public class ConcurrentOperationExecutor implements Ordered {

	private static final int DEFAULT_MAX_RETRIES = 2;

	private int maxRetries = DEFAULT_MAX_RETRIES;
	private int order = 1;

	public void setMaxRetries(int maxRetries) {
		this.maxRetries = maxRetries;
	}

	public int getOrder() {
		return this.order;
	}

	public void setOrder(int order) {
		this.order = order;
	}

	@Around("com.xyz.CommonPointcuts.businessService()")
	public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
		int numAttempts = 0;
		PessimisticLockingFailureException lockFailureException;
		do {
			numAttempts++;
			try {
				return pjp.proceed();
			}
			catch(PessimisticLockingFailureException ex) {
				lockFailureException = ex;
			}
		} while(numAttempts <= this.maxRetries);
		throw lockFailureException;
	}
}
@Aspect
class ConcurrentOperationExecutor : Ordered {

	companion object {
		private const val DEFAULT_MAX_RETRIES = 2
	}

	var maxRetries = DEFAULT_MAX_RETRIES

	private var order = 1

	override fun getOrder(): Int {
		return this.order
	}

	fun setOrder(order: Int) {
		this.order = order
	}

	@Around("com.xyz.CommonPointcuts.businessService()")
	fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
		var numAttempts = 0
		var lockFailureException: PessimisticLockingFailureException?
		do {
			numAttempts++
			try {
				return pjp.proceed()
			} catch (ex: PessimisticLockingFailureException) {
				lockFailureException = ex
			}
		} while (numAttempts <= this.maxRetries)
		throw lockFailureException!!
	}

@Around("com.xyz.CommonPointcuts.businessService()")引用businessService共享命名切入点定义中定义的命名切入点。spring-doc.cadn.net.cn

请注意,该方面实现了Ordered接口,以便我们可以设置 高于交易建议的方面(我们每次都希望有新的交易 重试)。这maxRetriesorder属性都由 Spring 配置。这 主要作发生在doConcurrentOperation围绕建议。请注意,对于 moment,我们将重试逻辑应用于每个businessService.我们尝试继续, 如果我们失败了PessimisticLockingFailureException,我们重试,除非 我们已经用尽了所有的重试尝试。spring-doc.cadn.net.cn

相应的 Spring 配置如下:spring-doc.cadn.net.cn

@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfiguration {

	@Bean
	public ConcurrentOperationExecutor concurrentOperationExecutor() {
		ConcurrentOperationExecutor executor = new ConcurrentOperationExecutor();
		executor.setMaxRetries(3);
		executor.setOrder(100);
		return executor;

	}
}
@Configuration
@EnableAspectJAutoProxy
class ApplicationConfiguration {

	@Bean
	fun concurrentOperationExecutor() = ConcurrentOperationExecutor().apply {
		maxRetries = 3
		order = 100
	}
}
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
			https://www.springframework.org/schema/beans/spring-beans.xsd
			http://www.springframework.org/schema/aop
			https://www.springframework.org/schema/aop/spring-aop.xsd">

	<aop:aspectj-autoproxy />

	<bean id="concurrentOperationExecutor"
		  class="com.xyz.service.impl.ConcurrentOperationExecutor">
		<property name="maxRetries" value="3"/>
		<property name="order" value="100"/>
	</bean>

</beans>

为了优化切面,使其仅重试幂等作,我们可以定义以下内容Idempotent注解:spring-doc.cadn.net.cn

@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}
@Retention(AnnotationRetention.RUNTIME)
// marker annotation
annotation class Idempotent

然后,我们可以使用注释来注释服务作的实现。变化 到仅重试幂等作的方面涉及细化切入点 表达式,以便仅@Idempotent作匹配,如下所示:spring-doc.cadn.net.cn

@Around("execution(* com.xyz..service.*.*(..)) && " +
		"@annotation(com.xyz.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
	// ...
	return pjp.proceed(pjp.getArgs());
}
@Around("execution(* com.xyz..service.*.*(..)) && " +
		"@annotation(com.xyz.service.Idempotent)")
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
	// ...
	return pjp.proceed(pjp.args)
}