@AspectJ pointcut สำหรับวิธีการทั้งหมดของคลาสที่มีคำอธิบายประกอบเฉพาะ


127

ฉันต้องการตรวจสอบวิธีการสาธารณะทั้งหมดของทุกชั้นเรียนพร้อมคำอธิบายประกอบที่ระบุ (พูด @Monitor) (หมายเหตุ: คำอธิบายประกอบอยู่ในระดับชั้นเรียน) สิ่งที่เป็นไปได้สำหรับสิ่งนี้คืออะไร? หมายเหตุ: ฉันใช้ @AspectJ style Spring AOP


ด้านล่างทำงานเพื่อขยาย @Pointcut ("การดำเนินการ (* (@ org.rejeev.Monitor *). * (.. ))") อย่างไรก็ตามตอนนี้คำแนะนำกำลังดำเนินการสองครั้ง เบาะแสใด ๆ ?
Rejeev Divakaran

อีกประเด็นหนึ่งคือคำอธิบายประกอบ @Monitor อยู่บนอินเทอร์เฟซและมีคลาสที่ใช้ การมีอินเทอร์เฟซและคลาสจะทำให้คำแนะนำดังกล่าวดำเนินการสองครั้งหรือไม่?
Rejeev Divakaran

6
คุณควรยอมรับคำตอบที่ยอดเยี่ยมด้านล่างนี้ สิ่งนี้ทำให้เขามีชื่อเสียง มีไม่กี่คนที่มีค่าที่นี่ใน SO ที่สามารถตอบคำถาม AspectJ ได้
fool4jesus

คำตอบ:


164

คุณควรรวม pointcut กับ method pointcut

คำสั่งชี้เหล่านี้จะทำงานเพื่อค้นหาวิธีการสาธารณะทั้งหมดภายในคลาสที่มีเครื่องหมายคำอธิบายประกอบ @Monitor:

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

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

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

แนะนำจุดสุดท้ายที่รวมสองข้อแรกและเสร็จสิ้น!

หากคุณสนใจฉันได้เขียนแผ่นโกงที่มีสไตล์ @AspectJ พร้อมเอกสารตัวอย่างที่เกี่ยวข้องที่นี่


ขอบคุณ การอภิปรายเกี่ยวกับคำอธิบายประกอบชี้ทางลัดบน Cheat Sheet ของคุณมีประโยชน์อย่างยิ่ง
GregHNZ

1
ฉันจะอ้างอิงถึงชั้นเรียนได้อย่างไรในคำแนะนำวิธีที่ฉันทำกับคำแนะนำ pointcut ปกติคือ @Before ("onObjectAction () && this (obj)")
Priyadarshi Kunal

Cheat Sheet มีประโยชน์มากแม้ว่าจะเป็นเวลา 5 ปีแล้วก็ตาม :)
Yadu Krishnan

คำถามที่นี่ถ้าสองวิธีที่อยู่ในลำดับชั้นและทั้งสองตกอยู่ภายใต้ 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
ตัวอย่างที่ดี คำถามหนึ่ง: เหตุใด Annotation Monitorจึงต้องเป็น Spring Component?
mwhs

1
Componentคำอธิบายประกอบที่ใช้ในการบอกภาชนะฤดูใบไม้ผลิที่จะใช้รวมถึงชั้นใน AspectJ สิ่งทอ โดยค่าเริ่มต้นฤดูใบไม้ผลิดูเฉพาะ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
    }
}

โปรดทราบว่าคุณต้องไม่มีคำแนะนำอื่น ๆ เกี่ยวกับคลาสเดียวกันก่อนหน้านี้เนื่องจากคำอธิบายประกอบจะหายไปหลังจากการพร็อกซี



4

ควรจะเพียงพอที่จะทำเครื่องหมายวิธีการแสดงผลของคุณดังนี้:

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

มีลักษณะที่นี้สำหรับขั้นตอนตามคู่มือขั้นตอนเกี่ยวกับเรื่องนี้


3

คุณยังสามารถกำหนด pointcut เป็น

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
}

มันจะสกัดกั้นการดำเนินการของวิธีการทั้งหมดที่มีคำอธิบายประกอบเฉพาะด้วย '@MyHandling' ในคลาส 'YourService' หากต้องการสกัดกั้นวิธีการทั้งหมดโดยไม่มีข้อยกเว้นเพียงแค่ใส่คำอธิบายประกอบลงในชั้นเรียนโดยตรง

ไม่ว่าขอบเขตส่วนตัว / สาธารณะจะเป็นอย่างไรที่นี่ แต่โปรดทราบว่า spring-aop ไม่สามารถใช้มุมมองสำหรับการเรียกเมธอดในอินสแตนซ์เดียวกันได้ (โดยทั่วไปจะเป็นแบบส่วนตัว) เนื่องจากไม่ได้ใช้คลาสพร็อกซีในกรณีนี้

เราใช้คำแนะนำ @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

คุณสามารถใช้ PerformanceMonitoringInterceptor ของ Spring และลงทะเบียนคำแนะนำทางโปรแกรมโดยใช้ 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

จาก 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 *);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.