【问题标题】:How to ensure annotations execution order in java?java中如何保证注解的执行顺序?
【发布时间】:2012-12-28 09:54:02
【问题描述】:

我有 2 个自定义注释,但一个应该始终在另一个之前执行。我如何确保这一点?是否有某种排序或使用其他方法定义?

【问题讨论】:

  • 发生了什么?随机工作?
  • 看来,Class 中的注释描述存储在HashMap 中。当然,这并不能保证本地订单。所以,我认为,没有办法。
  • 执行顺序是什么意思?注释的处理方式取决于使用它们的代码。你在注释什么?您使用了哪些注释?
  • 如果是自定义注解,自定义代码可能会处理它们,因此,您可以轻松地在您的所述自定义代码中指示顺序。
  • 它们是我做的注释。如何使用自定义代码指导订单?你有什么例子吗?

标签: java spring annotations


【解决方案1】:

我知道这是一个非常古老的问题,但我只是想记录我的发现。谁能确认这些是否正确? 此页面中已经提到,Spring 文档说除非使用 @Order 注释,否则注释的执行是未定义的。 我尝试重命名Aspect类,测试了很多次,发现Aspect类是按照名字的字母顺序执行的,结果是一致的。

下面是我的示例代码:

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

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

@Aspect
public class A_Aspect {

@Around("@annotation(mypackage.A)")
public void around(ProceedingJoinPoint joinPoint) {
    System.out.println("A_Aspect");
    joinPoint.proceed();
    }
}

@Aspect
public class B_Aspect {

    @Around("@annotation(mypackage.B)")
    public void around(ProceedingJoinPoint joinPoint) {
        System.out.println("B_Aspect");
        joinPoint.proceed();
    }
}

class AdvisedClass{
    @B
    @A
    public void advisedMethod(){}
}

当我尝试执行advisedMethod() 时,以下是我收到的日志:

A_Aspect
B_Aspect

我更改了注解声明顺序:

@A
@B  
public void advisedMethod(){}

以下是日志:

A_Aspect
B_Aspect

我将注释@A 重命名为@C,以下是日志:

A_Aspect
B_Aspect

但是,当我尝试将 Aspect 类 A_Aspect 重命名为 C_Aspect 时,以下是日志:

B_Aspect
C_Aspect

正如我所说,我希望有人确认这一点,因为我找不到任何文档

【讨论】:

    【解决方案2】:

    您可以使用@Order 注解确保自定义注解的顺序。

    https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/Order.html

    例子:

    第一个注释:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface CustomAnnotation {
    }
    
    @Aspect
    @Component
    @Order(value = 1)
    public class CustomAnnotationInterceptor {
    
        @Before("@annotation(customAnnotation )")
        public void intercept(JoinPoint method, CustomAnnotation customAnnotation ) {
            //Code here
        }
    }
    

    第二个注释:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface CustomAnnotationTwo {
    }
    
    @Aspect
    @Component
    @Order(value = 2)
    public class CustomAnnotationInterceptorTwo {
    
        @Before("@annotation(customAnnotationTwo )")
        public void intercept(JoinPoint method, CustomAnnotationTwo customAnnotationTwo ) {
            //Code here
        }
    

    使用它们:

    @CustomAnnotationTwo
    @CustomAnnotation
    public void someMethod(){
    }
    

    在本例中,CustomAnnotationInterceptor 将首先执行。

    【讨论】:

      【解决方案3】:

      Checkout https://stackoverflow.com/a/30222541/810109:至少在 Java 8 中,您可以按保证的顺序检索注解,因此您只需以正确的顺序声明它们。

      【讨论】:

        【解决方案4】:

        您可以使用 EJB 拦截器来做到这一点。

        您可以简单地通过@Interceptors( { MyInterceptor.class } ) 添加拦截器,然后添加第二个 @MyInterceptorConfiguration(value=something)。

        正如 bkail 在their answer here 中所说:

        这仅适用于 CDI 构造型 注释(见拦截器 EE 6 (EJB 3.1) 中的示例绑定页面。

        【讨论】:

        【解决方案5】:

        来自http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-ataspectj-advice-ordering

        Spring AOP 遵循与 AspectJ 相同的优先级规则来确定通知执行的顺序。最高优先级的建议首先运行“在途中”(因此给定两条之前的建议,具有最高优先级的一条首先运行)。从连接点“退出”时,优先级最高的建议最后运行(因此,给定两条后建议,优先级最高的一条将运行在第二条)。

        当定义在不同方面的两条通知都需要在同一个连接点运行时,除非您另外指定,否则执行顺序是不确定的。您可以通过指定优先级来控制执行顺序。这是通过在切面类中实现 org.springframework.core.Ordered 接口或使用 Order 注释以普通 Spring 方式完成的。给定两个方面,从 Ordered.getValue()(或注释值)返回较低值的方面具有较高的优先级。

        当定义在同一方面的两条通知都需要在同一连接点运行时,排序是不确定的(因为无法通过反射来检索 javac 编译类的声明顺序)。考虑将这些建议方法合并为每个方面类中每个连接点的一个建议方法,或者将建议的片段重构为单独的方面类 - 可以在方面级别进行排序。

        【讨论】:

          【解决方案6】:

          第一个注释:

          @Retention(RetentionPolicy.RUNTIME)
          @Target(ElementType.FIELD)
          public @interface FirstAnnotation {
            String value() default "";
          }
          

          第二个注释:

          @Retention(RetentionPolicy.RUNTIME)
          @Target(ElementType.FIELD)
          public @interface SecondAnnotation {
            String value() default "";
          }
          

          使用示例:

          public class Test {
          
            @SecondAnnotation("second annotation")
            @FirstAnnotation("first annotation")
            private String annotatedField1 = "value of field 1";
          
            @SecondAnnotation("second annotation")
            @FirstAnnotation("first annotation")
            private String annotatedField2 = "value of field 2";
          
            @SecondAnnotation("second annotation")
            private String annotatedField3 = "value of field 3";
          
            @FirstAnnotation("first annotation")
            private String annotatedField4 = "value of field 4";
          
            // Sample
            public static void processAnnotatedFields( final Object obj ) throws IllegalArgumentException, IllegalAccessException {
          
              for ( final Field field : getFieldsFornAnotation( obj, FirstAnnotation.class ) ) {
                // Do something with fields that are annotated with @FirstAnnotation
                final FirstAnnotation an = field.getAnnotation( FirstAnnotation.class );
                System.out.print( "@" +an.annotationType().getSimpleName()+ "(" +an.value()+ "): " );
                System.out.println( field.getName()+ " = '" +field.get(obj)+ "'" );
              }
          
              System.out.println();
          
              for ( final Field field : getFieldsFornAnotation( obj, SecondAnnotation.class ) ) {
                // Do something with fields that are annotated with @SecondAnnotation
                final SecondAnnotation an = field.getAnnotation( SecondAnnotation.class );
                System.out.print( "@" +an.annotationType().getSimpleName()+ "(" +an.value()+ "): " );
                System.out.println( field.getName()+ " = '" +field.get(obj)+ "'" );
              }
          
            }
          
            /**
             * Collect object fields annotated with "annotationClass"
             * This can be saved in static map to increase performance.
             */
            private static final Set<Field> getFieldsFornAnotation( final Object o, final Class<? extends Annotation> annotationClass ) {
              final Set<Field> fields = new LinkedHashSet<Field>();
          
              if ( o == null || annotationClass == null )
                return fields;
          
              for (final Field field : o.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(annotationClass)) {
                  field.setAccessible( true );
                  fields.add( field );
                }
              }
              return fields;
            }
          
            public static void main(final String[] args) throws IllegalArgumentException, IllegalAccessException {
          
              processAnnotatedFields( new Test() );
          
            }
          
          }
          

          结果/输出:

          @FirstAnnotation(first annotation): annotatedField1 = 'value of field 1'
          @FirstAnnotation(first annotation): annotatedField2 = 'value of field 2'
          @FirstAnnotation(first annotation): annotatedField4 = 'value of field 4'
          
          @SecondAnnotation(second annotation): annotatedField1 = 'value of field 1'
          @SecondAnnotation(second annotation): annotatedField2 = 'value of field 2'
          @SecondAnnotation(second annotation): annotatedField3 = 'value of field 3'
          

          【讨论】:

            【解决方案7】:

            是的,我认为 Annotation 本身为 @First 和 @Second 等提供了注释,所以你可以尝试一下

            【讨论】:

            • 你是这么认为的还是你检查过的? :)
            猜你喜欢
            • 1970-01-01
            • 2011-01-13
            • 1970-01-01
            • 1970-01-01
            • 2016-04-29
            • 1970-01-01
            • 2013-11-25
            • 2021-03-07
            • 2018-01-07
            相关资源
            最近更新 更多