在Spring上下文中查找方法级别的自定义注解

20

我想找出所有被注解为@Versioned的Spring bean中的类/方法。

我创建了自定义注解,如下所示:

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

当我使用Java反射查找方法时,此注释表现完美

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

但是当我访问Spring beans并尝试类似的检查时它无法工作:

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
                }
            }
        }
    }
}

事实上,这段代码确实可以找到其他注解,比如@RequestMapping。我不确定我在自定义注解方面做错了什么。


豆子是否被代理? - Sotirios Delimanolis
1
我本想说不。但是SOF需要至少10个字符 :-) - kamoor
好的,请提供一个最小可重现的示例。 - Sotirios Delimanolis
你将 @Versioned 注解应用于哪个类类型的方法? - Angad
1
@Angad 没有界面。https://github.com/kamoor/spring-versioned/tree/master/src/main/java/com/kamoor - kamoor
显示剩余3条评论
3个回答

23

通过查看你的代码,我发现你正在使用Spring AOP与CGLIB Proxying。因此带有@Versioned注释方法的类将被代理。

我已经在你的代码库中测试了这个解决方案。

请使用以下代码,它应该解决你的问题。在代码片段下面寻找更多选项:

@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
                }
            }
        }
    }
}

检测代理类的方法:

  • 对于使用任何代理机制的Spring AOP代理,请使用 org.springframework.aop.support.AopUtils#isAoPProxy
  • 如果您正在通过Spring CGLIB进行代理(而不是通过Spring AOP),请使用 org.springframework.cglib.proxy.Proxy#isProxyClass
  • 如果您正在使用JDK代理进行代理,请使用 java.lang.reflect.Proxy#isProxyClass

在此情况下,我刚刚编写了一个足够的if条件。但是,如果使用多个代理工具,则需要根据上述信息编写多个if-else条件。


只是一点小建议。那些if条件并不是必要的。AopUtils.getTargetClass()方法会为您解决这一切。 - Angad
你说得没错,但是需要根据答案后面提到的不同代理类型进行测试。根据Spring文档中的AopUtils#getTargetClass: 确定给定bean实例的目标类,该实例可能是AOP代理。 返回AOP代理的目标类和普通类的目标类。 - owaism
是的,这个实用方法根据你传入的参数返回所需的类(目标或非目标)。Spring AOP 代理本质上是 JDK 或 CGLIB 代理。 - Angad
实用方法只有在它是AOP代理时才会返回所需的类。因此,它基本上必须是jdk或cglib代理,以及由Spring AOP创建的代理。例如,在给定的代码中,名为“server”的bean通过使用CGLIB被Spring代理。AopUtils.getTargetClass无法解析目标类,因为它不是AOP代理。 - owaism
如果它是一个CGLIB代理,您不需要解析任何目标类。您可以直接在bean上调用getClass(),然后使用getDeclaredMethods()获取所有方法,而无需失去方法注释信息。如果它不是AOP代理,则Util方法将返回原始类。我会在这一点上结束讨论。 - Angad
显示剩余2条评论

9

applicationContext.getBean(beanName).getClass()可以获取Spring在目标类周围创建的代理类。

如果要获取Spring bean的目标类(如果有),则需要使用AopUtils.class提供的实用程序类来解决此问题。

下面是如何使用它:

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

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

请注意,您需要导入 spring-aopMaven 依赖项才能获取 AopUtils 类。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
</dependency>

1
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?
                }

            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接