乐趣区

关于springboot:Java自定义注解实现

1. Java 注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种正文机制。

Java 语言中的类、办法、变量、参数和包等都能够被标注。

在编译器生成类文件时,标注能够被嵌入到字节码中
因而 Java 虚拟机能够在运行时能够通过反射获取到标注内容。

2. 元注解

JDK 中提供了一些根底注解,称为元注解。最常见的几种比方:
作用在代码上的:

  • @Override – 查看该办法是否是重写办法。如果发现其父类,或者是援用的接口中并没有该办法时,会报编译谬误。
  • @Deprecated – 标记过期办法。如果应用该办法,会报编译正告。
  • @SuppressWarnings – 批示编译器去疏忽注解中申明的正告。

作用在其余注解的:

  • @Retention – 标识这个注解怎么保留,是只在代码中(SOURCE),还是编译入 class 文件中(CLASS),或者是在运行时能够通过反射拜访(RUNTIME)。
  • @Documented – 标记这个注解是否蕴含在 JavaDoc 中。
  • @Target – 标记这个注解应该润饰哪种 Java 成员。
  • @Inherited – 标记如果一个类具备继承注解,那么他所有的子类也领有该注解

后续新退出的:

  • @SafeVarargs – Java 7 开始反对,疏忽任何应用参数为泛型变量的办法或结构函数调用产生的正告。
  • @FunctionalInterface – Java 8 开始反对,标识一个匿名函数或函数式接口。
  • @Repeatable – Java 8 开始反对,标识某注解能够在同一个申明上应用屡次。

这里不对元注解进行具体的介绍,有须要的能够去查阅相干的文档。

3. 自定义注解

Java 容许咱们实现自定义注解来实现某些性能。
总的来说,自定义注解须要实现 3 步:定义注解、标注注解、解决注解

(1) 定义注解

应用 @interface 来定义一个注解,上面定义了一个名为 MyAnnotation 的注解。

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

@Retention(RetentionPolicy.RUNTIME)示意该注解在运行时也无效,这样就能够应用反射的形式来获取到。
@Target(ElementType.TYPE)示意该注解是用在类或者接口上的

(2) 标注注解

@MyAnnotation("test")
public class MyClass1 { }

@MyAnnotation
public class MyClass2 {}

这里实现了两个类,别离应用 MyAnnotation 进行注解,MyClass1 的注解中配置了属性值 test,MyClass2 没有配置属性值。

(3) 解决注解

public class MyClassTest {
    @Test
    public void test() {new ApplicationContextRunner().withBean(MyClass1.class).withBean(MyClass2.class).run(context -> {
            // 获取所有被 MyAnnotation 标注的类
            Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(MyAnnotation.class);
            for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {System.out.println("BeanName is" + entry.getKey());
                // 获取类上的 MyAnnotation 标注
                MyAnnotation annotation = entry.getValue().getClass().getAnnotation(MyAnnotation.class);
                System.out.println("the value of annotation is" + Objects.requireNonNull(annotation).value());
            }
        });
    }
}

这里实现了一个测试类,依据注解的属性值的不同,打印不同的内容。

运行后果:

能够看到 MyClass1 打印出了设置的 test,MyClass2 打印出了默认的 MyAnnotation。

4. 自定义条件注解

@Conditional 是一个很罕用的注解,用来依据肯定的条件判断,是否给容器注入 Bean 的。
而 @ConditionalOnProperty、@ConditionalOnClass 等都是通过组合 @Conditional,提供了更弱小的性能。

接下来咱们通过组合 @Conditional,实现一个当属性 name 的值只有属于配置数组中的时候,就实现 Bean 的注入。

(1) 定义注解

定义一个 ConditionalOnContains 注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ContainsCondition.class)
public @interface ConditionalOnContains {int[] value();}

@Target(ElementType.METHOD)示意该注解是用在办法上的

(2) 标注注解

public class ContainsConfiguration {
    @Bean
    @ConditionalOnContains(value = {0, 1})
    public ContainsTest1 containsTest1() {return new ContainsTest1();
    }

    @Bean
    @ConditionalOnContains(value = {1, 2})
    public ContainsTest2 containsTest2() {return new ContainsTest2();
    }

    @Bean
    @ConditionalOnContains(value = {2, 3})
    public ContainsTest3 containsTest3() {return new ContainsTest3();
    }
}

这里实现了三个类,别离应用 ConditionalOnContains 进行注解,ContainsTest1 的注解中配置了 value = {0,1}, ContainsTest2 的注解中配置了 value = {1,2}, ContainsTest3 的注解中配置了 value = {2,3}。

(3) 解决注解

public class ContainsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnContains.class.getName());
        int[] values = (int[]) Objects.requireNonNull(annotationAttributes).get("value");
        for (int value : values) {if (value == 1) {return true;}
        }
        return false;
    }
}

这里对注解的属性值进行解决,如果蕴含 1,则返回 true,否则返回 false.

运行后果:

能够看到打印出了 containsTest1 和 containsTest2,而没有打印 containsTest3,因为其 value 属性值不蕴含 1.

退出移动版