2010-01-06 16 views
94

Quiero monitorear todos los métodos públicos de todas las clases con la anotación especificada (digamos @Monitor) (nota: la anotación es a nivel de clase). ¿Cuál podría ser un punto de corte posible para esto? Nota: Estoy usando @AspectJ style Spring AOP.@AspectJ punto de corte para todos los métodos de una clase con anotación específica

+0

El debajo de uno trabaja en una medida. @Pointcut ("execution (* (@ org.rejeev.Monitor *). * (..))") Sin embargo, ahora el aviso se está ejecutando dos veces. ¿Cualquier pista? –

+0

Otro punto es que la anotación @Monitor está en una interfaz y allí una clase implementa eso. ¿La presencia de una interfaz y clase provocará la doble ejecución de tal consejo? –

+4

Debe aceptar la excelente respuesta a continuación. Esto le da reputación. Hay muy pocas personas aquí en SO que pueden responder preguntas de AspectJ. – fool4jesus

Respuesta

10

Algo así:

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

Tenga en cuenta que no debe tener ningún otro tipo sobre la misma clase antes de ésta, debido a que las anotaciones se perderán después de proxy.

133

Debe combinar un punto de corte con un punto de corte de método.

Estos puntos de corte va a hacer el trabajo para encontrar todos los métodos públicos dentro de una clase marcada con una anotación @Monitor:

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

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

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

Consejo el último punto de corte que combina los dos primeros y ya está!

Si usted está interesado, he escrito un cheat sheet con el estilo @AspectJ aquí con un example document correspondiente aquí.

+0

Gracias. La discusión de los puntos de anotación en su Cheat Sheet es particularmente útil. – GregHNZ

+1

¿Cómo obtengo una referencia a la clase en el consejo de la manera en que lo hago con consejos de punto de corte normal? @Antes ("onObjectAction() && this (obj)") – expressions

+0

La Hoja de trucos fue muy útil, a pesar de que han pasado 5 años :) –

3

También puede definir el punto de corte como

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

Poco simple 'ejecución (público * @Monitor *. * (..))' funciona, también. – xmedeko

2

La forma más sencilla parece ser:

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

    return pjp.proceed(); 

    // perform actions after 
} 

Se interceptará la ejecución de todos los métodos anotados específicamente con '@MyHandling' en 'YourService' clase. Para interceptar todos los métodos sin excepción, simplemente coloque la anotación directamente en la clase.

No importa el alcance privado/público aquí, pero tenga en cuenta que spring-aop no puede usar el aspecto para llamadas a métodos en la misma instancia (típicamente privadas), porque no usa la clase proxy en este caso.

Utilizamos el consejo de @Around aquí, pero básicamente es la misma sintaxis con @Before, @ After o cualquier consejo.

Por cierto, anotación @MyHandling debe estar configurado de esta manera:

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

} 
+0

que no está respondiendo la instrucción original, con ElementType.Type – Alex

+0

sí, ElementType.TYPE también permitirá poner la anotación directamente en las clases, lo que supongo que dará como resultado manejar cualquier método de esta clase. ¿Soy verdad? ¿Realmente está funcionando? – Donatello

+0

El '// realizar acciones después de' nunca se llamará ya que estamos devolviendo el valor en la línea anterior. – josephpconley

43

El uso de anotaciones, como se describe en la pregunta.

Anotación: @Monitor

anotación en la clase, app/PagesController.java:

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

anotación en el método, app/PagesController.java:

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

anotación personalizada, app/Monitor.java:

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

Aspecto para la anotación, 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()); 
    } 
} 

Habilitar AspectJ, servlet-context.xml:

<aop:aspectj-autoproxy /> 

incluyen bibliotecas AspectJ, pom.xml:

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

Buen ejemplo. Una pregunta: ¿por qué el Annotation 'Monitor' tiene que ser un Spring' Component'? – mwhs

+1

La anotación 'Component' se usa para indicarle al contenedor de Spring que se aplique incluir la clase en el aspecto de weaver AspectJ. Por defecto, Spring solo mira 'Controller',' Service', y otras anotaciones específicas, pero no 'Aspect'. – Alex

+1

Ok gracias. Pero estaba hablando de la anotación '@ Component' en' @ interface' no en 'Aspect'. ¿Por qué es eso necesario? – mwhs

0

Usted podría utilizar PerformanceMonitoringInterceptor de primavera y programación registrar el consejo usando un procesador de beanpost.

@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; 
    } 
    } 
} 
3

que debería ser suficiente para marcar su método aspecto como éste:

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

echar un vistazo a this para una guía paso a paso en este sentido.

1

desde la primavera de 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 *); 
0

Uso

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