简体   繁体   中英

Find method level custom annotation in a Spring context

All I wanted to find out was "all the class/methods in Spring beans which are annotated as @Versioned".

I created my custom annotation as,

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

This annotation works perfectly when I use Java reflection to find methods as:

for(Method m: obj.getClass().getMethods()){
    if(m.isAnnotationPresent(Versioned.class)){
        .... // Do something
    }

But it does not work when I access Spring beans and try similar check:

public class VersionScanner implements ApplicationContextAware{
    public void setApplicationContext(ApplicationContext applicationContext){
        for(String beanName: applicationContext.getBeanDefinitionNames()){
            for(Method m: applicationContext.getBean(beanName).getClass().getDeclaredMethods()){
                if(m.isAnnotationPresent(Versioned.class){
                    // This is not WORKING as expected for any beans with method annotated
                }
            }
        }
    }
}

In fact, this code does find other annotations such as @RequestMapping. I am not sure what I am doing wrong with my custom annotation.

Going through your code, I figured out that you are using Spring AOP with CGLIB Proxying. Due to which your classes (which have methods annotated with @Versioned ) are being proxied.

I have tested this solution with your code base.

Use the following code, and it should resolve your issue. Look for more options below the code snippet:

@Configuration
public class VersionScanner implements ApplicationContextAware {

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        for (String beanName : applicationContext.getBeanDefinitionNames()) {
            Object obj = applicationContext.getBean(beanName);
            /*
             * As you are using AOP check for AOP proxying. If you are proxying with Spring CGLIB (not via Spring AOP)
             * Use org.springframework.cglib.proxy.Proxy#isProxyClass to detect proxy If you are proxying using JDK
             * Proxy use java.lang.reflect.Proxy#isProxyClass
             */
            Class<?> objClz = obj.getClass();
            if (org.springframework.aop.support.AopUtils.isAopProxy(obj)) {

                objClz = org.springframework.aop.support.AopUtils.getTargetClass(obj);
            }

            for (Method m : objClz.getDeclaredMethods()) {
                if (m.isAnnotationPresent(Versioned.class)) {
                    //Should give you expected results
                }
            }
        }
    }
}

To detect a proxy class:

  • For Spring AOP proxy using any proxying mechanism use org.springframework.aop.support.AopUtils#isAoPProxy
  • If you are proxying with Spring CGLIB (not via Spring AOP), use org.springframework.cglib.proxy.Proxy#isProxyClass
  • If you are proxying using JDK Proxy, use java.lang.reflect.Proxy#isProxyClass

I have just written one if condition which is sufficient in your case; but in case multiple proxying utilities are used, multiple if-else conditions will have to be written based on the information above.

applicationContext.getBean(beanName).getClass() gives you the proxied class that Spring creates around your target class.

What you want is to get hold of the target class, if any, from your Spring bean.

Spring provides a nice utility class for resolving this called AopUtils.class.

Below is how you would use it:

for(String beanName: applicationContext.getBeanDefinitionNames()){
    Method[] methods = AopUtils.getTargetClass(applicationContext.getBean(beanName)).getDeclaredMethods();

    for(Method m: methods){
        if(m.isAnnotationPresent(Versioned.class){
        }
    }
}

Note that you will have to import the spring-aop Maven dependency to get hold of the AopUtils class:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
</dependency>
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    for (String beanName : applicationContext.getBeanDefinitionNames()) {
        Object obj = applicationContext.getBean(beanName);
        Class<?> objClz = obj.getClass();
        if (org.springframework.aop.support.AopUtils.isAopProxy(obj)) {
            objClz = org.springframework.aop.support.AopUtils.getTargetClass(obj);
        }

        for (Method m : objClz.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Transactional.class)) {
                Transactional transactional = m.getAnnotation(Transactional.class);
                Class<? extends Throwable>[] value = transactional.rollbackFor();
                if (value == null){
                   // help !!!
                    // If value is null, I want to set a value for him like Exception.class How can I modify it?
                }

            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM