Creating Java custom annotations with Spring aspectj AOP
There are situations where you want to retry a particular method that fails. For example, retry submitting a message to an messaging queue 3 times, retry external service calls, etc. AOP (Aspect Oriented Programming) is well suited for this as this is a cross cutting concern. In this tutorial, I use aspectj, spring-aop, and Java annotation. Here are the steps.
Step 1: The pom.xml file to outline the dependency jar files.
<!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>3.1.2.RELEASE</version> </dependency> <!-- Aspectj --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.11</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.10</version> </dependency>
Step 2:Define the annotation -- RetryOnFailure.
package com.mycompany.app9; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.METHOD, ElementType.TYPE }) public @interface RetryOnFailure { int attempts() default 3; int delay() default 1000; //default 1 second }
Step 3: Define the aspect point cut implementation RetryOnFailureAspect.
package com.mycompany.app9; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.core.NestedRuntimeException; @Aspect public class RetryOnFailureAspect { public static final String RETRY_LIMIT_EXCEEDED_MSG = "Retry limit exceeded."; @Around("execution(* *(..)) && @annotation(retry)") public Object retry(ProceedingJoinPoint pjp, RetryOnFailure retry) throws Throwable { Object returnValue = null; for (int attemptCount = 1; attemptCount <= (1 + retry.attempts()); attemptCount++) { try { returnValue = pjp.proceed(); } catch (Exception ex) { handleRetryException(pjp, ex, attemptCount, retry); } } return returnValue; } private void handleRetryException(ProceedingJoinPoint pjp, Throwable ex, int attemptCount, RetryOnFailure retry) throws Throwable { if (ex instanceof NestedRuntimeException) { ex = ((NestedRuntimeException) ex).getMostSpecificCause(); } if (attemptCount == 1 + retry.attempts()) { throw new RuntimeException(RETRY_LIMIT_EXCEEDED_MSG, ex); } else { System.out.println(String .format("%s: Attempt %d of %d failed with exception '%s'. Will retry immediately. %s", pjp.getSignature(), attemptCount, retry.attempts(), ex.getClass().getCanonicalName(), ex.getMessage())); } } }
Step 4: Now wire up aspectj and the annotation via Spring xml files.
Firstly, wire up aop via spring-aop.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- Enable the @AspectJ support --> <aop:aspectj-autoproxy /> <bean id="retryOnFailureAspect" class="com.mycompany.app9.RetryOnFailureAspect" /> </beans>
Next, the application context xml file springApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Import your AspectJ config --> <import resource="classpath:spring-aop.xml" /> <!-- Scan you spring services for annotations --> <context:component-scan base-package="com.mycompany.app9" /> </beans>
Step 5: Finally, the test class that uses this annotation. Forcefully throw an exception to see if the method is retried.
package com.mycompany.app9; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; @Component public class RetryOnFailureTest { @RetryOnFailure(attempts = 3, delay = 2000) public void testRetry() { System.out.println("Entered ...................."); throw new RuntimeException("Forcing an exception"); } public static void main(String[] args) { final ApplicationContext context = new ClassPathXmlApplicationContext("springApplicationContext.xml"); // Get me my spring managed bean final RetryOnFailureTest retryFailureTest = context.getBean(RetryOnFailureTest.class); retryFailureTest.testRetry(); } }
Step 6: The output will be
Jul 24, 2013 6:41:24 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5e3974: startup date [Wed Jul 24 18:41:24 EST 2013]; root of context hierarchy Jul 24, 2013 6:41:24 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [springApplicationContext.xml] Jul 24, 2013 6:41:24 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [spring-aop.xml] Jul 24, 2013 6:41:24 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1dc423f: defining beans [org.springframework.aop.config.internalAutoProxyCreator,retryOnFailureAspect,retryOnFailureTest,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy Entered .................... void com.mycompany.app9.RetryOnFailureTest.testRetry(): Attempt 1 of 3 failed with exception 'java.lang.RuntimeException'. Will retry immediately. Forcing an exception Entered .................... void com.mycompany.app9.RetryOnFailureTest.testRetry(): Attempt 2 of 3 failed with exception 'java.lang.RuntimeException'. Will retry immediately. Forcing an exception Entered .................... void com.mycompany.app9.RetryOnFailureTest.testRetry(): Attempt 3 of 3 failed with exception 'java.lang.RuntimeException'. Will retry immediately. Forcing an exception Entered .................... Exception in thread "main" java.lang.RuntimeException: Retry limit exceeded. at com.mycompany.app9.RetryOnFailureAspect.handleRetryException(RetryOnFailureAspect.java:43) at com.mycompany.app9.RetryOnFailureAspect.retry(RetryOnFailureAspect.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622) at com.mycompany.app9.RetryOnFailureTest$$EnhancerByCGLIB$$4086c6b6.testRetry() at com.mycompany.app9.RetryOnFailureTest.main(RetryOnFailureTest.java:23) Caused by: java.lang.RuntimeException: Forcing an exception at com.mycompany.app9.RetryOnFailureTest.testRetry(RetryOnFailureTest.java:15) at com.mycompany.app9.RetryOnFailureTest$$FastClassByCGLIB$$e37240e1.invoke( ) at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191) at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80) at com.mycompany.app9.RetryOnFailureAspect.retry(RetryOnFailureAspect.java:22) ... 13 more
Labels: annotation, aspectj
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home