Java自定义Annotation注解开发详解

本文详细讲解了Java中自定义注解的开发,包括运行期和编译期的使用。在运行期,通过反射获取并处理注解信息;在编译期,利用Annotation Processor生成动态代码或进行源文件分析。文中给出了具体示例,如检查类中字段是否为final的编译期注解处理器。

目录

介绍

一、运行期的自定义注解

1. Class Level Annotation

2. Method Level Annotation

3. Field Level Annotation

4. 使用自定义注解

5. 处理自定义注解的逻辑

二、编译期的自定义注解

1. 创建自定义注解

2. 实现一个Processor

3. 注册你的Processor

4. 测试你的自定义注解


介绍

Java中的注解是每个开发都会遇到的,但是如果要自定义自己的注解,则需要遵循一些基本的步骤,一般注解的开发有2个基本方法:

  1. 在运行期,通过反射获得当前类,方法,变量上的注解信息来实现自定义注解的功能
  2. 在编译期,通过Annotation Processer预编译生成想要的任何内容或者逻辑

下面将通过2个例子来说明开发一个自定义注解需要哪些步骤。首先我们将看到一个非常简单的例子,我们用这个例子来说明开发自定义annotation的一些基本步骤,我们的第二个例子将介绍自定义注解以及Annotation Processor的一些用法

一、运行期的自定义注解

在下面的例子中,我们将创建3种不同类型自定义注解,以收集所有有自定义注解的类和方法

1. Class Level Annotation

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

2. Method Level Annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodAnnotation {
    public String alias() default "";
}

3. Field Level Annotation

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

@Retention注解解释:

  • @Retention(RetentionPolicy.SOURCE): 该注解只在编译期生效,生成的class文件并不包含该注解
  • @Retention(RetentionPolicy.CLASS): 该注解会被保留在class文件中,但是运行期不会生效
  • @Retention(RetentionPolicy.RUNTIME): 该注解会被保留在class文件中,并且会在运行期生效

@Target注解解释如下,这里只列举了部分@Target 类型,更多的类型请参看JavaDoc。

  • @Target(ElementType.TYPE): 该注解只能运用到Class, Interface, enum上
  • @Target(ElementType.FIELD): 该注解只能运用到Field上
  • @Target(ElementType.METHOD): 该注解只能运用到方法上

注解中还有一个alias的string类型参数,缺省值是空字符串,在下一节我们将看到如何使用这个string类型的参数

4. 使用自定义注解

@ClassAnnotation(alias = "test")
public class Test {
    @FieldAnnotation
    private String name;

    @MethodAnnotation(alias="debug")
    public String getName() {
        return name;
    }
}

我们在class, field, method上分别运用我们的自定义注解,并且在method上开启debug日志

5. 处理自定义注解的逻辑

我们已经介绍了如何定义自己的注解,以及如何使用我们的注解,接下来我们将用Java的Reflection API来实现我们自定义注解的逻辑

public void gatherAnnotations {
        Map<String, Class> classMap = new HashMap<>();
        Class<Test> obj = Test.class;
        if(obj.isAnnotationPresent(ClassAnnotation.class)) {
            ClassAnnotation classAnnotation = obj.getAnnotation(ClassAnnotation.class);
            if ("".equals(classAnnotation.alias())) {
                classMap.put(obj.getName(), obj);
            }else{
                classMap.put(classAnnotation.alias(), obj);
            }
        }

        Map<String, Method> methodMap = new HashMap<>();
        for (Method method : obj.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MethodAnnotation.class)) {
                MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
                if ("".equals(methodAnnotation.alias())) {
                    methodMap.put(method.getName(), method);
                }else{
                    methodMap.put(methodAnnotation.alias(), method);
                }
            }
        }
    }

这里我们运用Java反射收集了所有有我们自定义注解的类和方法,并放到相应的Map中。

至此,我们完成了一个简单的运行期自定义注解的例子,这个例子看上去没有实际的用处,但是在真正的业务场景中,有很多应用都是基于此类逻辑,例如Spring中的@Service和@Autowired注解大都基于这样的逻辑,来进行后续的初始化和注入。

二、编译期的自定义注解

Annotation Processor是代码级别的注解处理器,所以它一般在编译期帮助我们生成我们想要的动态代码,配置文件,文档等,它的使用场景相当广泛,一般包含以下几种,

  • 生成代码或者properties文件
  • 修改源文件,例如为Pojo生成getter和setter方法
  • 一些源文件分析诊断的案例,本文就属于这一类

在这个例子中,我们将创建一个Immutable的类级别的注解,该注解将在编译期检查class中所有的field是否有final关键字修饰

1. 创建自定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Immutable {
}
  1. 定义了一个Immutable注解
  2. @Target(ElementType.TYPE)表示Immutable注解只能放在类上
  3. @Retention(RetentionPolicy.SOURCE)表示Immutable注解只在编译期生效

2. 实现一个Processor

JDK中已经为我们实现了一个AbstractProcessor, 所以我们要做的是扩展这个Abstract类,并且实现里面的process方法

@SupportedAnnotationTypes("annotation.Immutable")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
@AutoService(Processor.class)
public class ImmutableProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        List<String> nonPublicFields = new LinkedList<>();
        for ( Element element : roundEnv.getElementsAnnotatedWith(Immutable.class)) {
            if( element instanceof TypeElement ) {
                TypeElement typeElement = (TypeElement) element;
                for( final Element enclosedElement: typeElement.getEnclosedElements() ) {
                    if( enclosedElement instanceof VariableElement) {
                        VariableElement variableElement = ( VariableElement )enclosedElement;
                        if( !variableElement.getModifiers().contains( Modifier.FINAL ) ) {
                            nonPublicFields.add(variableElement.getSimpleName().toString());
                        }
                    }
                }
                if (nonPublicFields.size() > 0) {
                    processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR,
                            String.format( "Class %s is not @Immutable, fields %s are not declared as final",
                            typeElement.getSimpleName(), String.join(",", nonPublicFields)
                            )
                    );
                }
            }
        }
        return true;
    }
}
  1. @SupportedAnnotationTypes("annotation.Immutable")表示ImmutableProcessor 只用于annotation.Immutable注解
  2. @SupportedSourceVersion(SourceVersion.RELEASE_11)表示这个processor支持的JDK最低版本是11
  3. @AutoService(Processor.class)表示使用Google auto-service library注册这个processor,下一节将讨论如何注册你的processor
  4. process方法轮训找到所有有Immutable注解的类,然后遍历所有的方法,查找是否有final关键字,如果没有记录该方法,最后抛出异常

3. 注册你的Processor

Java实际上提供了好几种选择来帮助我们注册自己的Processor使我们的自定义注解生效,这里我们只介绍最常用的方法来注册Processor

  1. 通过Google auto-service library来注册你的processor
@AutoService(Processor.class)
public class ImmutableProcessor extends AbstractProcessor {
    // ...
}
  • 首先你需要引入auto-service library
  • 在你的processor上加上@AutoService(Processor.class)注解
  • 编译后,你将在Jar包的META-INF/services下看到javax.annotation.processing.Processor文件,文件里包含了你的processor

      2. 通过Maven plugin来注册你的processor

       使用maven前你的processor必须已经编译,通过其他jar文件的形式加到了dependencies里

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessors>
            <annotationProcessor>
                annotation.ImmutableProcessor
            </annotationProcessor>
        </annotationProcessors>
    </configuration>
</plugin>

4. 测试你的自定义注解

@Immutable
public class Test {
    public String name;
}

在编译期,会收到 Class Test is not @Immutable, fields name are not declared as final 的报错。至此一个简单的使用annotation processor的例子已经完成。

后续文章会继续深入分析介绍Java自定义注解在各个framework中的使用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值