【问题标题】:Find method level custom annotation in a Spring context在 Spring 上下文中查找方法级别的自定义注释
【发布时间】:2015-03-11 20:53:49
【问题描述】:

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

我将自定义注释创建为,

@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 bean 并尝试类似的检查时它不起作用:

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等其他注解。我不确定我的自定义注释做错了什么。

【问题讨论】:

标签: java spring spring-mvc


【解决方案1】:

通过您的代码,我发现您正在使用带有 CGLIB 代理的 Spring AOP。因此,您的类(具有使用 @Versioned 注释的方法)正在被代理。

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

使用以下代码,它应该可以解决您的问题。在代码 sn-p 下方寻找更多选项:

@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 条件。

【讨论】:

  • 只是一个旁注。那些如果条件不是必需的。 AopUtils.getTargetClass () 方法为您解决所有这些问题。
  • 你是对的,但是如果需要测试不同类型的代理的条件,如答案的后面部分所述......根据 Spring docs AopUtils#getTargetClass:Determine the target class of the given bean instance which might be an AOP proxy. Returns the target class for an AOP proxy and the plain class else.
  • 是的,实用程序方法根据您传递的内容返回所需的类(目标或非目标)。 Spring Aop 代理只不过是 jdk 或 cglib 代理。
  • 只有当它是 AOP 代理时,实用程序方法才会返回所需的类。所以基本上它必须是 jdk 或 cglib 代理以及由 Spring AOP 创建的代理。例如,在名为“server”的bean 的给定代码测试中,它由spring 使用CGLIB 代理。 AopUtils.getTargetClass 不为此解析目标类,因为它不是 AOP 代理。
  • 如果它是 CGLIB 代理,则不需要解析任何目标类。您可以直接在 bean 上调用 getClass() 并在其上调用 getDeclaredMethods() 来获取所有方法,而不必丢失方法注释信息。如果该类不是 AOP 代理,则 Util 方法会按原样返回该类。我将在这一点上离开讨论。
【解决方案2】:

applicationContext.getBean(beanName).getClass() 为您提供 Spring 在您的目标类周围创建的代理类。

您想要的是从您的 Spring bean 中获取目标类(如果有)。

Spring 提供了一个很好的实用程序类来解决这个问题,称为 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-aop Maven 依赖项才能获取 AopUtils 类:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
</dependency>

【讨论】:

    【解决方案3】:
    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?
                    }
    
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多