Announcement Announcement Module
Collapse
No announcement yet.
Capturing and binding annotations at class level using Spring AOP Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Capturing and binding annotations at class level using Spring AOP

    In the manual it says you can do stuff like this:

    Code:
    @Around(value = "@annotation(a.b.MyAnnotation) && @annotation(myAnnotation)", argNames="myAnnotation")
    which would then expect a rough method signature of the following kind.

    Code:
    static Object myMethod(final ProceedingJoinPoint pjp, MyAnnotation myAnnotation) throws Throwable {
        String value = myAnnotation.value();
        return pjp.proceed();
    }
    The above works fine and the method has access to the annotation instance and its properties. This is all great.

    My problem is that this only works at a method level. When a class is annotation with @MyAnnotation the pointcut doesn't match.

    I've also tried many of the other kinds of pointcut but they don't match class level either if I need the annotation as well. If I do not try to bind the annotation as an argument then the pointcut matches fine. However I need the annotation and its properties.

    One possible answer is that annotation binding is only supported at method level but, if so, then that would be rather strange. Why support it at method level and support basic pointcuts at class level but then NOT support annotation binding at class level?

    Any ideas would be much appreciated.

  • #2
    TestAnnotation.java

    Code:
    package com.spring.aop;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnnotation {
        String value() default "def";
    }

    AopService.java

    Code:
    package com.spring.aop;
    
    import org.springframework.stereotype.Component;
    
    @Component
    @TestAnnotation("clazz")
    public class AopService {
    
        public void service() {
            System.out.println("xxxxxxxxxx: AopService.service()");
        }
    }

    TestAspect.java

    Code:
    package com.spring.aop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class TestAspect {
    
        @Around("@target(annotation)")
        public Object advice(ProceedingJoinPoint joinPoint, TestAnnotation annotation) throws Throwable {
            System.out.println("xxxxxxxxxxxxx: TestAspect.TestAspect.advice() called with '" + annotation.value() + "'");
            return joinPoint.proceed();
        }
    }

    spring-config.xml

    HTML Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd" default-lazy-init="true">
    
        <context:component-scan base-package="com.spring.aop"/>
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    </beans>

    SpringStart.java

    Code:
    package com.spring;
    
    import com.spring.aop.AopService;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringStart {
        public static void main(String[] args) throws Exception {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    
            AopService aopService = (AopService)context.getBean("aopService");
            aopService.service();
        }
    }

    Output:

    Code:
    xxxxxxxxxxxxx: TestAspect.TestAspect.advice() called with 'clazz'
    xxxxxxxxxx: AopService.service()

    Comment


    • #3
      Denis,

      Thank you very much. That is the most comprehensive reply I've ever had. What would we do without you? :-)

      A few things to note.

      -- That pointcut expression worked fine and I used a different one for method level so now I can support both class level and method level.

      -- I had to do something like "within(blah) && @target(annotation)" to narrow it down a little bit more as only using "@target(annotation)" was causing some strange problems - perhaps catching too much.

      -- Why did you use proxy-target-class="true" by the way? Was there a reason? I am setting this to false - jdk proxies seem to work fine but cglib breaks my app due to final classes.

      -- What is the significance of the @ sign in @target by the way? Is that entirely different from 'target'?

      Many thanks.

      Comment


      • #4
        Originally posted by Narada View Post
        Denis,

        Thank you very much. That is the most comprehensive reply I've ever had. What would we do without you? :-)
        Welcome


        Originally posted by Narada View Post
        -- Why did you use proxy-target-class="true" by the way? Was there a reason? I am setting this to false - jdk proxies seem to work fine but cglib breaks my app due to final classes.
        There are benefits and drawbacks for every approach (I mean cglib- and jdk-proxying). CGLIB proxies seems to be a little bit faster and allow to advice ordinary classes (not interfaces). Jdk proxies doesn't require additional binaries and allows to use final classes. There is no ultimate answer which approach is better, it's necessary to consider the right way to go for every particular situation. As for me, I prefer aspectj weaving at all


        Originally posted by Narada View Post
        -- What is the significance of the @ sign in @target by the way? Is that entirely different from 'target'?

        Many thanks.
        These designators have much in common but there is a difference too. 'target' checks that advised bean object IS-A particular class; '@target' checks that class of the advised bean is marked by specific annotation.

        Comment

        Working...
        X