특정 주석이있는 클래스의 모든 메소드에 대한 @AspectJ pointcut


127

지정된 주석 (예 : @Monitor)으로 모든 클래스의 모든 공용 메소드를 모니터링하고 싶습니다 (참고 : 주석은 클래스 수준입니다). 이것에 대해 가능한 포인트 컷은 무엇입니까? 참고 : @AspectJ 스타일의 Spring AOP를 사용하고 있습니다.


아래는 확장하는 것입니다. @Pointcut ( "execution (* (@ org.rejeev.Monitor *). * (..))") 그러나 이제 조언은 두 번 실행됩니다. 실마리?
Rejeev Divakaran

또 다른 요점은 @Monitor 주석이 인터페이스에 있으며 클래스가이를 구현한다는 것입니다. 인터페이스와 클래스가 있으면 이러한 조언을 두 번 실행할 수 있습니까?
Rejeev Divakaran

6
아래의 훌륭한 답변을 수락해야합니다. 이것은 그에게 명성을 제공합니다. AspectJ의 질문에 대답 할 수있는 소중한 사람은 여기에 없습니다.
fool4jesus

답변:


162

유형 포인트 컷과 메소드 포인트 컷을 결합해야합니다.

이 pointcut은 @Monitor 주석으로 표시된 클래스 내에서 모든 공개 메소드를 찾기 위해 작동합니다.

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

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

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

처음 두 개를 결합한 마지막 포인트 컷을 조언하면 끝입니다!

관심이 있다면 @AspectJ 스타일 의 치트 시트 를 여기에 해당 예제 문서 와 함께 작성 했습니다.


감사. 치트 시트의 주석 포인트 컷에 대한 설명이 특히 유용합니다.
GregHNZ

1
내가 일반적인 pointcut 조언으로하는 방법이 @Before ( "onObjectAction () && this (obj)") 인 조언에서 클래스에 대한 참조를 얻는 방법
Priyadarshi Kunal

컨닝 페이퍼는 : 오년이었다에도 불구하고, 매우 도움이되었다
야두 크리슈 난을

계층 구조에 있고 두 가지 방법이 모두 pointcut에 속하고 동일한 클래스에 속하는 경우 두 가지 모두에서 실행됩니까? 그렇다면, 내 경우에는 일어나지 않기 때문에 stackoverflow.com/questions/37583539/… 을 참조하십시오 .
HVT7

비공개 메소드에 대한 포인트 컷을 가질 수 없기 때문에 공개 공개가 중복 된 것
같습니다

58

질문에 설명 된대로 주석 사용

주석: @Monitor

수업에 대한 주석 app/PagesController.java:

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

방법에 대한 주석 app/PagesController.java:

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

맞춤 주석 app/Monitor.java:

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

주석의 측면 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());
    }
}

AspectJ, servlet-context.xml:

<aop:aspectj-autoproxy />

AspectJ 라이브러리 포함 pom.xml:

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

1
좋은 예입니다. 한 가지 질문 : 왜 주석 Monitor이 스프링이어야 Component합니까?
mwhs

1
Component주석은 것은 위버 AspectJ의에 클래스를 포함 적용 Spring 컨테이너를 얘기하는 데 사용됩니다. 기본적으로, 봄은 본다 Controller, Service하지만, 다른 특정 주석 Aspect.
Alex

1
알았어 고마워. 그러나에 대한 @Component주석에 대해 이야기 @interface하고 Aspect있었습니다. 왜 필요한가요?
mwhs

2
@Component봄은 AspectJ를 IOC의 / DI 측면 지향 시스템에 컴파일 있도록 주석을 수 있습니다. 나는 다르게 말하는 법을 모른다. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
Alex

어노테이션이있는 클래스에서 "공용"메소드 만 수행합니까, 아니면 모든 메소드 (액세스 레벨에 관계없이)를 수행합니까?
Lee Meador

14

그런 것 :

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

프록 싱 후 주석이 손실되므로이 클래스 이전에 동일한 클래스에 대한 다른 조언이 없어야 합니다.


11

사용하다

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

4

다음과 같이 Aspect 메소드를 표시하기에 충분해야합니다.

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

한 번 봐 가지고 이에 대한 단계별 가이드를 들어.


3

포인트 컷을 다음과 같이 정의 할 수도 있습니다.

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

조금 더 간단한 execution(public * @Monitor *.*(..))작업도 있습니다.
xmedeko

3

가장 간단한 방법은 다음과 같습니다.

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

   return pjp.proceed();

   // perform actions after
}

'YourService'클래스에서 '@MyHandling'으로 주석이 달린 모든 메소드의 실행을 인터셉트합니다. 예외없이 모든 메소드를 가로 채려면 주석을 클래스에 직접 넣으십시오.

여기서 private / public 범위와 상관없이 spring-aop 은이 경우 프록시 클래스를 사용하지 않기 때문에 동일한 인스턴스 (일반적으로 개인 호출)의 메소드 호출에 aspect를 사용할 수 없습니다.

여기서는 @Around 조언을 사용하지만 기본적으로 @Before, @After 또는 조언과 동일한 구문입니다.

그건 그렇고, @MyHandling 주석은 다음과 같이 구성되어야합니다 :

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

}

ElementType.Type과 함께 원래 문장에 응답하지 않습니다
Alex

예, ElementType.TYPE은 클래스에 직접 주석을 넣을 수있게 해줍니다.이 클래스의 모든 메소드를 처리하게됩니다. 내가 사실이야? 정말 작동합니까?
Donatello

// perform actions after우리가 전에 행의 값을 반환하고 있기 때문에 호출되지 얻을 않습니다.
josephpconley

1

Spring의 PerformanceMonitoringInterceptor를 사용하고 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;
    }
  }
}

1

봄부터 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 *);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.