@AspectJ pointcut for all methods of a class with specific annotation

JavaAopAspectjSpring Aop

Java Problem Overview


I want to monitor all public methods of all Classes with specified annotation (say @Monitor) (note: Annotation is at class level). What could be a possible pointcut for this? Note: I am using @AspectJ style Spring AOP.

Java Solutions


Solution 1 - Java

You should combine a type pointcut with a method pointcut.

These pointcuts will do the work to find all public methods inside a class marked with an @Monitor annotation:

@Pointcut("within(@org.rejeev.Monitor *)")
public void beanAnnotatedWithMonitor() {}

@Pointcut("execution(public * *(..))")
public void publicMethod() {}

@Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void publicMethodInsideAClassMarkedWithAtMonitor() {}

Advice the last pointcut that combines the first two and you're done!

If you're interested, I have written a cheat sheet with @AspectJ style here with a corresponding example document here.

Solution 2 - Java

Using annotations, as described in the question.

Annotation: @Monitor

Annotation on class, app/PagesController.java:

package app;
@Controller
@Monitor
public class PagesController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public @ResponseBody String home() {
        return "w00t!";
    }
}

Annotation on method, app/PagesController.java:

package app;
@Controller
public class PagesController {
    @Monitor
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public @ResponseBody String home() {
        return "w00t!";
    }
}

Custom annotation, app/Monitor.java:

package app;
@Component
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Monitor {
}

Aspect for annotation, app/MonitorAspect.java:

package app;
@Component
@Aspect
public class MonitorAspect {
    @Before(value = "@within(app.Monitor) || @annotation(app.Monitor)")
    public void before(JoinPoint joinPoint) throws Throwable {
        LogFactory.getLog(MonitorAspect.class).info("monitor.before, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
    }

    @After(value = "@within(app.Monitor) || @annotation(app.Monitor)")
    public void after(JoinPoint joinPoint) throws Throwable {
        LogFactory.getLog(MonitorAspect.class).info("monitor.after, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
    }
}

Enable AspectJ, servlet-context.xml:

<aop:aspectj-autoproxy />

Include AspectJ libraries, pom.xml:

<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>

Solution 3 - Java

Something like that:

@Before("execution(* com.yourpackage..*.*(..))")
public void monitor(JoinPoint jp) {
    if (jp.getTarget().getClass().isAnnotationPresent(Monitor.class)) {
       // perform the monitoring actions
    }
}

Note that you must not have any other advice on the same class before this one, because the annotations will be lost after proxying.

Solution 4 - Java

Use

@Before("execution(* (@YourAnnotationAtClassLevel *).*(..))")
    public void beforeYourAnnotation(JoinPoint proceedingJoinPoint) throws Throwable {
}

Solution 5 - Java

it should be enough to mark your aspect method like this:

@After("@annotation(com.marcot.CommitTransaction)")
    public void after() {

have a look at this for a step by step guide on this.

Solution 6 - Java

You can also define the pointcut as

public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (@Monitor *).*(..));

Solution 7 - Java

The simplest way seems to be :

@Around("execution(@MyHandling * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
   throws Throwable {
   // perform actions before

   return pjp.proceed();

   // perform actions after
}

It will intercept execution of all methods specifically annotated with '@MyHandling' in 'YourService' class. To intercept all methods without exception, just put the annotation directly on the class.

No matter of the private / public scope here, but keep in mind that spring-aop cannot use aspect for method calls in same instance (typically private ones), because it doesn't use the proxy class in this case.

We use @Around advice here, but it's basically the same syntax with @Before, @After or any advice.

By the way, @MyHandling annotation must be configured like this :

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyHandling {

}

Solution 8 - Java

I share with you a code that can be useful, it is to create an annotation that can be used either in a class or a method.

@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Documented
public @interface AnnotationLogger {
	/**
	 * It is the parameter is to show arguments in the method or the class.
	 */
	boolean showArguments() default false;
}


@Aspect
@Component
public class AnnotationLoggerAspect {
	
	@Autowired 
	private Logger logger;	
	
	private static final String METHOD_NAME   = "METHOD NAME: {} ";
	private static final String ARGUMENTS     = "ARGS: {} ";
	
	@Before(value = "@within(com.org.example.annotations.AnnotationLogger) || @annotation(com.org.example.annotations.AnnotationLogger)")
	public void logAdviceExecutionBefore(JoinPoint joinPoint){	
		CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
		AnnotationLogger annotationLogger = getAnnotationLogger(joinPoint);
		if(annotationLogger!= null) {
			StringBuilder annotationLoggerFormat = new StringBuilder();
			List<Object> annotationLoggerArguments = new ArrayList<>();
			annotationLoggerFormat.append(METHOD_NAME);
			annotationLoggerArguments.add(codeSignature.getName());
			
			if (annotationLogger.showArguments()) {
				annotationLoggerFormat.append(ARGUMENTS);
				List<?> argumentList = Arrays.asList(joinPoint.getArgs());
				annotationLoggerArguments.add(argumentList.toString());
			}
			logger.error(annotationLoggerFormat.toString(), annotationLoggerArguments.toArray());
		}
	}
	
	private AnnotationLogger getAnnotationLogger(JoinPoint joinPoint) {
		AnnotationLogger annotationLogger = null;
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			Method method = joinPoint.getTarget().getClass().
					getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes());
			
			if (method.isAnnotationPresent(AnnotationLogger.class)){
				annotationLogger = method.getAnnotation(AnnotationLoggerAspect.class);
			}else if (joinPoint.getTarget().getClass().isAnnotationPresent(AnnotationLoggerAspect.class)){
				annotationLogger = joinPoint.getTarget().getClass().getAnnotation(AnnotationLoggerAspect.class);
			}
			return annotationLogger;
		}catch(Exception e) {
			return annotationLogger;
		}
	}
}

Solution 9 - Java

From Spring's AnnotationTransactionAspect:

/**
 * Matches the execution of any public method in a type with the Transactional
 * annotation, or any subtype of a type with the Transactional annotation.
 */
private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
	execution(public * ((@Transactional *)+).*(..)) && within(@Transactional *);

Solution 10 - Java

You could use Spring's PerformanceMonitoringInterceptor and programmatically register the advice using a beanpostprocessor.

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Monitorable
{

}


public class PerformanceMonitorBeanPostProcessor extends ProxyConfig implements BeanPostProcessor, BeanClassLoaderAware, Ordered,
    InitializingBean
{
    
  private Class<? extends Annotation> annotationType = Monitorable.class;

  private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

  private Advisor advisor;

  public void setBeanClassLoader(ClassLoader classLoader)
  {
    this.beanClassLoader = classLoader;
  }

  public int getOrder()
  {
    return LOWEST_PRECEDENCE;
  }

  public void afterPropertiesSet()
  {
    Pointcut pointcut = new AnnotationMatchingPointcut(this.annotationType, true);
    Advice advice = getInterceptor();
    this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
  }

  private Advice getInterceptor()
  {
    return new PerformanceMonitoringInterceptor();
  }

  public Object postProcessBeforeInitialization(Object bean, String beanName)
  {
    return bean;
  }

  public Object postProcessAfterInitialization(Object bean, String beanName)
  {
    if(bean instanceof AopInfrastructureBean)
    {
      return bean;
    }
    Class<?> targetClass = AopUtils.getTargetClass(bean);
    if(AopUtils.canApply(this.advisor, targetClass))
    {
      if(bean instanceof Advised)
      {
        ((Advised)bean).addAdvisor(this.advisor);
        return bean;
      }
      else
      {
        ProxyFactory proxyFactory = new ProxyFactory(bean);
        proxyFactory.copyFrom(this);
        proxyFactory.addAdvisor(this.advisor);
        return proxyFactory.getProxy(this.beanClassLoader);
      }
    }
    else
    {
      return bean;
    }
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRejeev DivakaranView Question on Stackoverflow
Solution 1 - JavaEspenView Answer on Stackoverflow
Solution 2 - JavaAlexView Answer on Stackoverflow
Solution 3 - JavaBozhoView Answer on Stackoverflow
Solution 4 - JavaDavide ConsonniView Answer on Stackoverflow
Solution 5 - JavamarcocastView Answer on Stackoverflow
Solution 6 - JavaShekharView Answer on Stackoverflow
Solution 7 - JavaDonatelloView Answer on Stackoverflow
Solution 8 - JavaJ. AbelView Answer on Stackoverflow
Solution 9 - JavaxmedekoView Answer on Stackoverflow
Solution 10 - JavaVikramView Answer on Stackoverflow