Loading...
Spring Framework Reference Documentation 7.0.2의 An AOP Example의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
이제 모든 구성 요소가 어떻게 동작하는지 보았으므로, 이들을 모아서 유용한 작업을 수행해 볼 수 있습니다.
business 서비스의 실행은 때때로 동시성 문제(예를 들어, deadlock loser)로 인해 실패할 수 있습니다. 작업을 재시도하면 다음 시도에서 성공할 가능성이 큽니다. 이러한 상황에서 재시도가 적절한 business 서비스(idempotent 작업으로서 conflict resolution을 위해 사용자에게 다시 돌아갈 필요가 없는 경우)에서는 client가 PessimisticLockingFailureException을 보지 않도록 작업을 투명하게 재시도하기를 원합니다.
이는 서비스 layer의 여러 서비스에 명백히 횡단하는 요구 사항이므로, aspect를 통해 구현하기에 이상적입니다.
작업을 재시도하기를 원하므로, proceed를 여러 번 호출할 수 있도록 around advice를 사용해야 합니다. 다음 목록은 기본 aspect 구현을 보여 줍니다:
1@Aspect 2public class ConcurrentOperationExecutor implements Ordered { 3 4 private static final int DEFAULT_MAX_RETRIES = 2; 5 6 private int maxRetries = DEFAULT_MAX_RETRIES; 7 private int order = 1; 8 9 public void setMaxRetries(int maxRetries) { 10 this.maxRetries = maxRetries; 11 } 12 13 public int getOrder() { 14 return this.order; 15 } 16 17 public void setOrder(int order) { 18 this.order = order; 19 } 20 21 @Around("com.xyz.CommonPointcuts.businessService()") 22 public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 23 int numAttempts = 0; 24 PessimisticLockingFailureException lockFailureException; 25 do { 26 numAttempts++; 27 try { 28 return pjp.proceed(); 29 } 30 catch(PessimisticLockingFailureException ex) { 31 lockFailureException = ex; 32 } 33 } while(numAttempts <= this.maxRetries); 34 throw lockFailureException; 35 } 36}
1@Aspect 2class ConcurrentOperationExecutor : Ordered { 3 4 companion object { 5 private const val DEFAULT_MAX_RETRIES = 2 6 } 7 8 var maxRetries = DEFAULT_MAX_RETRIES 9 10 private var order = 1 11 12 override fun getOrder(): Int { 13 return this.order 14 } 15 16 fun setOrder(order: Int) { 17 this.order = order 18 } 19 20 @Around("com.xyz.CommonPointcuts.businessService()") 21 fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any { 22 var numAttempts = 0 23 var lockFailureException: PessimisticLockingFailureException? 24 do { 25 numAttempts++ 26 try { 27 return pjp.proceed() 28 } catch (ex: PessimisticLockingFailureException) { 29 lockFailureException = ex 30 } 31 } while (numAttempts <= this.maxRetries) 32 throw lockFailureException!! 33 } 34}
@Around("com.xyz.CommonPointcuts.businessService()")는
Sharing Named Pointcut Definitions에 정의된 businessService라는 이름의 pointcut을 참조합니다.
aspect는 Ordered 인터페이스를 구현하여 aspect의 우선순위를 transaction advice보다 높게 설정할 수 있다는 점에 유의하십시오(재시도할 때마다 새로운 transaction을 원합니다). maxRetries와 order 프로퍼티는 둘 다 Spring에 의해 구성됩니다.
주요 동작은 doConcurrentOperation around advice에서 발생합니다. 현재로서는 각 businessService에 재시도 로직을 적용하고 있다는 점에 주목하십시오. proceed를 시도하고, PessimisticLockingFailureException으로 실패하면, 재시도 횟수를 모두 소진하지 않은 한 다시 시도합니다.
해당하는 Spring 설정은 다음과 같습니다:
1@Configuration 2@EnableAspectJAutoProxy 3public class ApplicationConfiguration { 4 5 @Bean 6 public ConcurrentOperationExecutor concurrentOperationExecutor() { 7 ConcurrentOperationExecutor executor = new ConcurrentOperationExecutor(); 8 executor.setMaxRetries(3); 9 executor.setOrder(100); 10 return executor; 11 12 } 13}
1@Configuration 2@EnableAspectJAutoProxy 3class ApplicationConfiguration { 4 5 @Bean 6 fun concurrentOperationExecutor() = ConcurrentOperationExecutor().apply { 7 maxRetries = 3 8 order = 100 9 } 10}
1<beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 https://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop 7 https://www.springframework.org/schema/aop/spring-aop.xsd"> 8 9 <aop:aspectj-autoproxy /> 10 11 <bean id="concurrentOperationExecutor" 12 class="com.xyz.service.impl.ConcurrentOperationExecutor"> 13 <property name="maxRetries" value="3"/> 14 <property name="order" value="100"/> 15 </bean> 16 17</beans>
aspect를 정교하게 만들어서 idempotent 작업만 재시도하도록 하려면, 다음과 같은 Idempotent 어노테이션을 정의할 수 있습니다:
1@Retention(RetentionPolicy.RUNTIME) 2// marker annotation 3public @interface Idempotent { 4}
1@Retention(AnnotationRetention.RUNTIME) 2// marker annotation 3annotation class Idempotent
그런 다음 이 어노테이션을 사용하여 서비스 작업의 구현에 어노테이션을 적용할 수 있습니다. idempotent 작업만 재시도하도록 aspect를 변경하는 작업은, 다음과 같이 pointcut expression을 정교하게 만들어 오직 @Idempotent 작업만 일치하도록 하는 것을 포함합니다:
1@Around("execution(* com.xyz..service.*.*(..)) && " + 2 "@annotation(com.xyz.service.Idempotent)") 3public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 4 // ... 5 return pjp.proceed(pjp.getArgs()); 6}
1@Around("execution(* com.xyz..service.*.*(..)) && " + 2 "@annotation(com.xyz.service.Idempotent)") 3fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? { 4 // ... 5 return pjp.proceed(pjp.args) 6}
Aspect Instantiation Models
Schema-based AOP Support