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