带有自定义注释的Springbean

带有自定义注释的Springbean,spring,annotations,spring-bean,Spring,Annotations,Spring Bean,我已经用自定义注释对Springbean进行了注释,但是Spring似乎在创建bean之后删除了我的自定义注释 AnnotatedBean bean = ctx.getBean(AnnotatedBean.class); Foo.findAndDoStuffWithAnnotatedThings(bean); 第二步不起作用,我的自定义注释丢失。(可能到期的代理文件) 我的豆子 @Rule(name = "RoutePickupRule") @Transactional @Component

我已经用自定义注释对Springbean进行了注释,但是Spring似乎在创建bean之后删除了我的自定义注释

AnnotatedBean bean = ctx.getBean(AnnotatedBean.class);

Foo.findAndDoStuffWithAnnotatedThings(bean);
第二步不起作用,我的自定义注释丢失。(可能到期的代理文件)

我的豆子

@Rule(name = "RoutePickupRule")
@Transactional
@Component
public class AnnotatedBean{


    @Autowired
    private ICarpoolDoa carpoolDAO;

    @Condition
    public boolean condition(CustomLocation customLocation, String userId) {
        //snip
    }

    @Action
    public void action() {
        //snip
    }  
我的一个自定义注释的示例

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Condition {

}
在Find和Dostuff中,有注释的内容哪里出错
Bean被传递到一个类,在该类中我的自定义注释被验证,但我的验证器找不到任何注释。(Util使用isAnnotationPresent方法)。同样,当我自己使用“new”创建bean时,没有问题

public class RuleAnnotationVerifier {


    public void RuleAnnotationVerifier(final Object rule) {
        checkRuleClass(rule);
        checkConditionMethod(rule);
        checkActionMethod(rule);
    }

    private void checkRuleClass(final Object rule) {
        if (!Utils.isClassAnnotatedWith(rule.getClass(), Rule.class)) {
            throw new IllegalArgumentException(String.format("Rule '%s' is not annotated with '%s'", rule.getClass().getName(), Rule.class.getName()));
        }

        if (rule.getClass().getAnnotation(Rule.class).name().isEmpty()) {
            throw new IllegalArgumentException(String.format("Rule '%s' annotation is empty", rule.getClass().getName()));
        }
    }
    ...
有没有办法在bean上保留自定义注释?在将类更改为Bean之前,我的程序工作正常

我为什么要这样做?
我的类中的方法是通过反射调用的,但在方法中,我希望使用自动连接的DOA,它要求类是bean。:)

我尝试过但没有成功的
aopproxutils.ultimateTargetClass(bean)

在这里找到了答案


似乎没有解决办法是不可能的。答案是3年前的,所以现在可能还有另一种方法?

我在尝试做同样的事情时发现了这个问题,那就是:用我自己的注释注释bean类中的一些方法,以便以后查找并通过反射执行它们

当bean被代理时,这会带来一些复杂性,这些复杂性还受到代理是CGLib代理还是JDK动态代理的影响。我使用Spring4.3.9做了一些实验,以观察行为上的差异。(我不确定有多少是故意的,而不是代理实现的副作用,因此在未来的版本中可能会有不同的行为。我也没有试验过使用AspectJ编织的效果)

您看不到注释的原因确实是因为bean被代理了(如果您在其任何方法上使用
@Transactional
,或者如果您使用Spring基于代理的AOP特性来增强bean,就会发生这种情况)

代理将在目标类上有重复的方法,但它不会继承或复制它们的注释-因此,当您在代理的
类中检查
方法时,您将看不到原始bean方法上的注释

因此,您需要检查目标bean的
(即:代理对象在完成其工作后包装并委托调用的实际bean实例),并且
aopproxutils.ultimateTargetClass()
应该会提供此功能。它将返回原始bean的
,您可以查看它的方法并查看注释

(您也可以使用
AopUtils.getTargetClass()
,但代理对象本身可能是另一个代理。使用
ultimateTargetClass()
应该沿着代理链一直向下,而
getTargetClass()
只向下一级)

您没有详细说明以何种方式
ultimateTargetClass()
对您来说“不起作用”,但代理对找到方法后调用该方法有一定的影响

首先,由于您正在扫描目标的类中的方法,因此您得到的是一个
方法
,它来自目标的类,而不是代理的

这是否重要取决于代理是CGLib代理还是JDK代理

如果它是CGLib代理,那么代理对象将扩展目标的类,并且您可以使用目标类中的
方法
在代理对象上调用它。即:

theAnnotatedMethod.invoke(theProxyObject, args...);
但是如果它是一个JDK代理,那么它就不能工作。您将得到一个异常“对象不是声明类的实例”,因为JDK代理没有扩展bean的类,它只是实现它的所有接口。要解决这个问题,您需要在代理上找到该方法的邪恶孪生兄弟,并使用以下命令调用它:

Method methodOnTheProxy = theProxyObject.getClass().getMethod(theAnnotatedMethod.getName(),
 theAnnotatedMethod.getParameterTypes());
methodOnTheProxy.invoke(theProxyObject, args...);
这将起作用,但仅当您试图调用的方法由类实现的接口之一声明时,因为JDK代理仅在bean类实现的接口中声明的代理方法

您可以为您注释的所有方法创建接口,并声明bean来实现它们,但这就引出了一个问题:当我们可以使用接口调用它们时,为什么我们要通过反射来查找和调用它们?这就回到了为什么我们首先要使用注释而不是这些方法的接口

在我自己的案例中(这是导致我研究这个问题的一个例子),我的目标是在某些类型的bean中声明一个接口中的“生命周期”方法,而不是用一个注释——例如OnLoad、OnLead、OnDead、OnEnter等方法来标记它们。我开始得到很多这样的方法,其中很多方法的实现通常都是空的。因此,我想转向一种类似于Spring控制器如何声明其@RequestMapping方法或单元测试的@Test on测试方法的风格

JDK代理还干扰类型注释(您在类级别应用的注释)。通常,您可以使用上下文方法
getBeansWithAnnotation()
查找其类使用指定注释进行注释的任何bean,但是,如果使用JDK代理对bean进行代理,则该方法将无法找到该bean,而如果使用CGLib进行代理,则仍然可以找到该bean。(有趣的是,在这两种情况下,在代理的
类上调用
isAnnotationPresent()
返回false)。当bean具有JDK代理时,您还将看到
getBeansOfType()
的相同问题

这表明我们可能更喜欢Spring使用CGL
package com.example;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;

import javax.sql.DataSource;

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * Example of finding methods with custom annotations when the bean is proxied
 * Dependencies: org.springframework/spring-core/4.3.9.RELEASE
 * org.springframework/spring-context/4.3.9.RELEASE
 * org.springframework/spring-tx/4.3.9.RELEASE
 * org.springframework/spring-jdbc/4.3.9.RELEASE org.hsqldb/hsqldb/2.4.0 (jdbc,
 * tx, and hsqldb are just there as a quick way of including Transactional as
 * the proxy example)
 */
@MyAnnotatedBean
public class AnnotatedProxyExample {

  public static void main(String[] args) {
    try(AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
        Config.class)) {

      //Collection<?> beans = context.getBeansWithAnnotation(MyAnnotatedBean.class).values();
      //Collection<?> beans = context.getBeansOfType(AnnotatedProxyExample.class).values();
      Collection<?> beans = Arrays.asList(context.getBean("myBean"));
      if(beans.isEmpty()) {
        System.out.println("***No beans were found");
      }
      else {
        for(Object myBean : beans) {

          if(AopUtils.isAopProxy(myBean)) {
            System.out.println("myBean is an AOP proxy");
          }
          else {
            System.out.println("myBean is not an AOP proxy");
          }

          System.out.println("Using myBean instance of class "
              + myBean.getClass().getName() + " returned from Spring context");
          printAndCallMyAnnotatedMethods(myBean, myBean.getClass());

          Class<?> ultimateTargetClass = AopProxyUtils
              .ultimateTargetClass(myBean);
          if(ultimateTargetClass == myBean) {
            System.out.println("(myBean is also the ultimateTarget of myBean)");
          }
          else {
            System.out.println("\nUsing the instance of class "
                + ultimateTargetClass.getName()
                + " returned by AopProxyUtils.ultimateTargetClass(MyBean):");
            printAndCallMyAnnotatedMethods(myBean, ultimateTargetClass);
          }
          System.out.println("---------------");
        }
      }
    }
  }

  private static void printAndCallMyAnnotatedMethods(Object myBean,
      Class<?> targetClass) {
    boolean foundAny = false;
    for(Method method : targetClass.getMethods()) {
      if(method.isAnnotationPresent(MyAnnotation.class)) {
        foundAny = true;
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        System.out.println("Found MyAnnotation on " + method.getName()
            + "(), value=" + annotation.value());
        invokeMethod(myBean, method);
        System.out.println();
      }
    }
    if(!foundAny) {
      System.out.println("***Did not find any methods with MyAnnotation");
    }
  }

  private static void invokeMethod(Object object, Method annotatedMethod) {
    if(!AopUtils.isAopProxy(object)) {
      System.out.println("object to invoke method on is not an AOP proxy");
    }
    if(AopUtils.isCglibProxy(object)) {
      System.out.println("object to invoke method on is a CGLib proxy");
    }
    if(AopUtils.isJdkDynamicProxy(object)) {
      System.out.println("object to invoke method on is a JDK proxy");
    }
    String methodName = annotatedMethod.getName();
    Class<?> objectClass = object.getClass();

    if(objectClass.isAnnotationPresent(MyAnnotatedBean.class)) {
      System.out
          .println("The object's class has the MyAnnotatedBean annotation");
    }
    else {
      System.out.println(
          "***The object's class does not have the MyAnnotatedBean annotation");
    }

    try {
      //Call the method on the object, but using the Method from the target class
      System.out.println("Invoking " + methodName
          + "() on object using annotated Method from the target's class");
      annotatedMethod.invoke(object);
    } catch(Exception e) {
      System.out.println("*** Couldn't call " + methodName
          + "() on instance of " + objectClass + " because " + e.getMessage());
    }

    try {
      //Find and call a method on object's actual class with the same signature as annotatedMethod
      //nb: if object isn't a proxy this will be the same Method as the above
      Method objectMethod = objectClass.getMethod(methodName,
          annotatedMethod.getParameterTypes());
      if(objectMethod.equals(annotatedMethod)) {
        System.out.println("(The target and object methods are the same here)");
      }
      else {
        System.out.println("Invoking " + methodName
            + "() on object using a matching Method from object's class");
        objectMethod.invoke(object);
      }
    } catch(NoSuchMethodException notFound) {
      System.out.println("***Couldn't find matching " + methodName
          + "() on the instance of " + objectClass.getName());
    } catch(Exception e) {
      System.out.println("*** Couldn't call " + methodName
          + "() on instance of " + objectClass + " because " + e.getMessage());
    }

  }

  ///////////////////////////////////////////////

  public void firstMethod() {
    System.out.println("CALLED! firstMethod(), tx="
        + TransactionSynchronizationManager.isActualTransactionActive());
  }

  @MyAnnotation("roti prata")
  public void secondMethod() {
    System.out.println("CALLED! secondMethod(), tx="
        + TransactionSynchronizationManager.isActualTransactionActive());
  }

  @Transactional
  @MyAnnotation("economy bee hoon")
  public void thirdMethod() {
    System.out.println("CALLED! thirdMethod(), tx="
        + TransactionSynchronizationManager.isActualTransactionActive());
  }

}

//////////////////////////////////////////////////

interface MyInterface0 {

}

interface MyInterface1 {
  //annotation won't be found because they aren't inherited from interfaces
  @MyAnnotation("curry laksa")
  public void firstMethod();
}

interface MyInterface2 {
  public void secondMethod();
}

interface MyInterface3 {
  public void thirdMethod();
}

/**
 * Annotation that indicates which methods we want to find and call.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {
  public String value();
}

//////////////////////////////////////////////////

/**
 * Annotation that marks the classes of the beans we want to retrieve from the
 * context to search for methods having MyAnnotation
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnnotatedBean {
  ;
}

//////////////////////////////////////////////////

//@EnableTransactionManagement(proxyTargetClass=true)
@EnableTransactionManagement
@Configuration
class Config {

  @Bean
  public AnnotatedProxyExample myBean() {
    return new AnnotatedProxyExample();
  }

  @Bean
  public PlatformTransactionManager transactionManager() {
    DataSource ds = new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.HSQL).build();
    return new DataSourceTransactionManager(ds);
  }
}
myBean is an AOP proxy Using myBean instance of class com.example.AnnotatedProxyExample$$EnhancerBySpringCGLIB$$367d5296 returned from Spring context ***Did not find any methods with MyAnnotation Using the instance of class com.example.AnnotatedProxyExample returned by AopProxyUtils.ultimateTargetClass(MyBean): Found MyAnnotation on secondMethod(), value=roti prata object to invoke method on is a CGLib proxy ***The object's class does not have the MyAnnotatedBean annotation Invoking secondMethod() on object using annotated Method from the target's class CALLED! secondMethod(), tx=false Invoking secondMethod() on object using a matching Method from object's class CALLED! secondMethod(), tx=false Found MyAnnotation on thirdMethod(), value=economy bee hoon object to invoke method on is a CGLib proxy ***The object's class does not have the MyAnnotatedBean annotation Invoking thirdMethod() on object using annotated Method from the target's class CALLED! thirdMethod(), tx=true Invoking thirdMethod() on object using a matching Method from object's class CALLED! thirdMethod(), tx=true myBean is not an AOP proxy Using myBean instance of class com.example.AnnotatedProxyExample returned from Spring context Found MyAnnotation on secondMethod(), value=roti prata object to invoke method on is not an AOP proxy The object's class has the MyAnnotatedBean annotation Invoking secondMethod() on object using annotated Method from the target's class CALLED! secondMethod(), tx=false (The target and object methods are the same here) ... ... Found MyAnnotation on thirdMethod(), value=economy bee hoon object to invoke method on is a JDK proxy ***The object's class does not have the MyAnnotatedBean annotation Invoking thirdMethod() on object using annotated Method from the target's class *** Couldn't call thirdMethod() on instance of class com.example.$Proxy17 because object is not an instance of declaring class Invoking thirdMethod() on object using a matching Method from object's class CALLED! thirdMethod(), tx=true