विशिष्ट एनोटेशन के साथ वर्ग के सभी तरीकों के लिए @AspectJ पॉइंटकट


127

मैं निर्दिष्ट एनोटेशन के साथ सभी वर्गों के सभी सार्वजनिक तरीकों की निगरानी करना चाहता हूं (@Monitor कहते हैं) (ध्यान दें: एनोटेशन कक्षा में है)। इसके लिए एक संभावित बिंदु क्या हो सकता है? नोट: मैं @AspectJ शैली स्प्रिंग AOP का उपयोग कर रहा हूं।


नीचे एक विस्तार करने के लिए काम करता है। @Pointcut ("निष्पादन (* (@ org.rejeev.Monitor *) *। * (..))") हालांकि अब सलाह को दो बार निष्पादित किया जा रहा है। कोई सुराग?
रीजीव दिवाकरन

एक और बिंदु यह है कि @Monitor एनोटेशन एक इंटरफ़ेस पर है और एक वर्ग लागू होता है जो। क्या एक इंटरफ़ेस और क्लास की उपस्थिति ऐसी सलाह के दोहरे निष्पादन का कारण बनेगी?
रीजीव दिवाकरन

6
आपको नीचे दिए गए उत्कृष्ट उत्तर को स्वीकार करना चाहिए। इससे उसे प्रतिष्ठा मिलती है। एसओ पर यहां बहुत कम लोग हैं जो एस्पेक्टज के सवालों का जवाब दे सकते हैं।
fool4jesus

जवाबों:


162

आपको एक विधि बिंदु के साथ एक प्रकार के बिंदु को संयोजित करना चाहिए।

ये बिंदु एक @Monitor एनोटेशन के साथ चिह्नित एक वर्ग के अंदर सभी सार्वजनिक तरीकों को खोजने के लिए काम करेंगे:

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

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

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

अंतिम बिंदु को सलाह दें जो पहले दो को जोड़ती है और आपका काम पूरा हो गया है!

यदि आप रुचि रखते हैं, तो मैंने यहाँ एक संबंधित उदाहरण दस्तावेज़ के साथ @AspectJ शैली के साथ एक धोखा पत्र लिखा है।


धन्यवाद। आपकी चीट शीट पर एनोटेशन बिंदुओं की चर्चा विशेष रूप से उपयोगी है।
ग्रेगएनजेड

1
मैं कैसे तरह से मैं सामान्य pointcut सलाह के साथ क्या सलाह में वर्ग के संदर्भ में मिलता है @Before ( "onObjectAction () && इस (obj)") है
प्रियदर्शी कुणाल

धोखा पत्रक बहुत उपयोगी था, भले ही यह 5 साल हो गया :)
यदु कृष्णन

यहाँ एक प्रश्न, अगर दो विधियाँ पदानुक्रम में हैं और दोनों बिंदु के नीचे आती हैं और एक ही वर्ग से संबंधित हैं, तो क्या यह दोनों पर अमल करेगा? यदि हाँ, तो stackoverflow.com/questions/37583539/… देखें , क्योंकि मेरे मामले में ऐसा नहीं हो रहा है।
एचवीटी 7

मुझे लगता है कि सार्वजनिक निष्पादन निरर्थक है क्योंकि आपके पास निजी तरीकों पर एक बिंदु नहीं हो सकता है
amstegraf

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 में वर्ग में शामिल हैं लागू करने के लिए स्प्रिंग कंटेनर बताने के लिए प्रयोग किया जाता है। डिफ़ॉल्ट रूप से, स्प्रिंग केवल पर लग रहा है Controller, Serviceऔर अन्य विशिष्ट एनोटेशन, लेकिन नहीं Aspect
एलेक्स

1
ठीक है धन्यवाद। लेकिन मैं @Componentएनोटेशन पर बात कर रहा था @interfaceन कि नोट पर Aspect। इसकी आवश्यकता क्यों है?
mwhs

2
@Componentएनोटेशन यह बनाता है तो वसंत AspectJ आईओसी / डि पहलू उन्मुख प्रणाली के साथ यह संकलन होगा। मुझे नहीं पता कि इसे अलग तरीके से कैसे कहा जाए। docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
एलेक्स

क्या यह केवल एनोटेट कक्षाओं में "सार्वजनिक" तरीके करता है या क्या यह सभी तरीके (कोई फर्क नहीं पड़ता कि एक्सेस स्तर क्या है) करता है?
ली मीडोर

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

आप बिंदु के रूप में भी परिभाषित कर सकते हैं

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' के साथ विशेष रूप से एनोटेट किए गए सभी तरीकों के निष्पादन को रोक देगा। अपवाद के बिना सभी तरीकों को बाधित करने के लिए, बस एनोटेशन को सीधे वर्ग पर रखें।

यहां निजी / सार्वजनिक दायरे की कोई बात नहीं है, लेकिन ध्यान रखें कि स्प्रिंग-एप एक ही उदाहरण (आमतौर पर निजी वाले) में विधि कॉल के लिए पहलू का उपयोग नहीं कर सकता है, क्योंकि यह इस मामले में प्रॉक्सी वर्ग का उपयोग नहीं करता है।

हम यहां @Around सलाह का उपयोग करते हैं, लेकिन यह मूल रूप से @Before, @After या किसी भी सलाह के साथ एक ही वाक्यविन्यास है।

वैसे, @MyHandling एनोटेशन को इस तरह कॉन्फ़िगर किया जाना चाहिए:

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

}

यह मूल कथन का उत्तर नहीं दे रहा है, ElementType.Type के साथ
एलेक्स

हाँ, ElementType.TYPE भी कक्षाओं पर सीधे एनोटेशन लगाने की अनुमति देगा, जो मुझे लगता है, इस वर्ग में किसी भी विधि को संभालने के लिए परिणाम देगा। क्या मैं सच्चा हूँ? क्या यह वास्तव में काम कर रहा है?
डोनाटेलो

// perform actions afterहम पहले लाइन में मूल्य वापस लौट रहे हैं के बाद से कभी नहीं कहा जाता हो जाएगा।
josephpconley

1

आप स्प्रिंग के परफॉरमेंसिंग इन्टरसेप्टर का उपयोग कर सकते हैं और प्रोग्राम को सेमपॉस्टप्रोसेसर का उपयोग करके सलाह को पंजीकृत कर सकते हैं।

@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 *);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.