JAVA注解理解

开篇定论

对于初次接触JAVA的同学,对于一个概念一定非常困惑,哪就是注解,这东西究竟是个啥,读者自身以前是C/C++开发,第一次接触到这个东西,完全是懵逼状态。总感觉这东西太牛逼了,牛逼到不需要写多少代码,居然可以实现很多牛X功能。但是当接触的多了,最终对于注解这个东西,想开篇送给所有的初学者几句话:
1,简单的注解,可以理解成配置文件。基于他的参数来决定软件的执行行为。
2,复杂的注解,也可以理解成配置文件。基于他的参数来决定编译期的行为。
可能大家看不太懂这个句话,其实总结来看,就一个词。注解即配置文件。你实际上可以这么简单的理解。
本文中,我们会先从背景知识,在从分类,最后从具体的例子程序来一步步的说清楚注解的含义。

背景知识

JDK 1.5之后引入了Annotation,翻译成中文就是注解,哪么官方是怎么解释这个的呢?

「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』。
The common interface extended by all annotation types
所有的注解类型都继承自这个普通的接口(Annotation)

JDK5开始,java增加了对元数据(MetaData)的支持,怎么支持?答:通过Annotation(注解)来实现。Annotation提供了为程序元素设置元数据的方法。元数据:描述数据的数据。

注解分类

基本Annotation

  • @Override
    • 只能作用于方法
    • 用于子类,在编译期,编译器检查这个方法,检查父类必须包含该方法,否则编译出错。
  • @Deprecated
    • 编译时读取
  • @SuppressWarnings
  • @unchecked异常
  • @SafeVarargs
  • @Functionlnterface

JDK的元Annotation(修饰注解的注解)

元注解和注解一样,只不过他是修饰注解的注解。java提供了6个元注解,分别如下:

  • @Retention
  • @Target
  • @Documented
  • @Inherited

列子程序

下面将以两个具体的列子来说明,在实际工程中,怎么使用注解来解决实际问题:

编译注解

编译注解是针对RetentionPolicy.SOURCE而言的,通过上面的介绍,我们知道他是针对编译源码期起作用的。我们经常用到的 lombok组件就是一个典型的编译源码期的注解,下面我们以自己的方式来实现这个注解,让你从例子中了解整个原理。

运行注解

  • 注解定义
package com.platform.lmob;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
}

*实现AbstractProcessor

package com.platform.lmob;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

import java.util.Set;

@SupportedAnnotationTypes("com.platform.lmob.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends AbstractProcessor {
    private Messager messager;
    private JavacTrees trees;
    private TreeMaker treeMaker;
    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);
        set.forEach(element -> {
            JCTree jcTree = trees.getTree(element);
            jcTree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();

                    for (JCTree tree : jcClassDecl.defs) {
                        if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                            JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                            jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                        }
                    }

                    jcVariableDeclList.forEach(jcVariableDecl -> {
                        messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
                        jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                    });
                    super.visitClassDef(jcClassDecl);
                }

            });
        });
        return true;
    }

    private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null);
    }

    private Name getNewMethodName(Name name) {
        String s = name.toString();
        return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
    }
}

*定义AbstractProcessor调用

  • META-INF目录下新增services目录
  • 创建文件javax.annotation.processing.Processor文件内容如下:
com.platform.lmob.GetterProcessor
  • pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.platform</groupId>
    <artifactId>lmob</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>lmob</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>${java.home}/../lib/tools.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>META-INF/**/*</exclude>
                </excludes>
            </resource>
        </resources>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>process-META</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/classes</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${basedir}/src/main/resources/</directory>
                                    <includes>
                                        <include>**/*</include>
                                    </includes>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

  • 工程测试
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文章涉及代码已放到github上annotation-study 1.Annotation为何而来 What:A...
    zlcook阅读 29,402评论 15 116
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和...
    九尾喵的薛定谔阅读 8,430评论 0 2
  • 前言   平时在开发中接触过许多的注解,如@Override,@Nullable等,但自己代码中还没怎么用过。所以...
    yizhanzjz阅读 7,929评论 0 10
  • 深入理解 Java 注解 本文内容基于 JDK8。注解是 JDK5 引入的,后续 JDK 版本扩展了一些内容,本文...
    静默虚空阅读 3,325评论 0 0
  • 在写java代码的过程中,经常会遇到注解,但是没有去理解注解背后的原理,也没有实现过注解。网上关于java注解的文...
    德彪阅读 4,402评论 0 0