像@Override这样的注释在Java内部如何工作?


93

有人可以向我解释注释在Java内部如何工作吗?

我知道如何通过使用Java中的java.lang.annotation库来创建自定义注释。但是我仍然不知道它在内部如何工作,例如@Override注释。

如果有人可以详细解释,我将非常感谢。


4
“内部工作”是什么意思?编译器?运行时?
克莱里斯-cautiouslyoptimistic-

3
@chrylis在内部工作意味着如何自动识别此方法是替代方法还是该方法不是替代方法。两者都在工作。像在编译时覆盖注释工作,而spring控制器注释在运行时工作
Chirag Dasani 2013年

Answers:


138

各种注释之间的第一个主要区别是它们是在编译时使用,然后丢弃(如@Override)还是放在已编译的类文件中,并在运行时可用(如Spring的@Component)。这由注释的@Retention策略确定。如果要编写自己的注释,则需要确定该注释在运行时(可能是用于自动配置)还是仅在编译时(用于检查或代码生成)有用。

在编译带有注释的代码时,编译器会看到注释,就像在源元素上看到其他修饰符一样,例如访问修饰符(public/ private)或final。当遇到注释时,它将运行一个注释处理器,就像一个插件类,它对特定的注释感兴趣。注释处理器通常使用Reflection API来检查正在编译的元素,并且可以简单地对它们进行检查,修改它们或生成要编译的新代码。@Override是第一个例子 它使用Reflection API来确保可以在其中一个超类中找到方法签名的匹配项,如果不能找到,则使用Messager引起编译错误。

有很多关于编写注释处理器的教程。这是一个有用的。期待通过对这些方法Processor接口对于编译器是如何调用注释处理器; 主要操作发生在process方法中,每次编译器看到带有匹配注释的元素时,该方法都会被调用。


4
很高兴为我们指出Spring的批注处理器如何解析@Component并将类注入其容器中
Junchen Liu

@shanyangqu这个问题是题外话,不是特定于Spring的。您可以自己阅读后处理器类。
克莱里斯-cautiouslyoptimistic-

42

除了其他建议外,我建议您从头开始编写自定义注释及其处理器,以了解注释的工作原理。

例如,我自己编写了注释,以检查方法在编译时是否重载。

首先,创建一个名为的注释Overload。此注释应用于方法,所以我用@Target(value=ElementType.METHOD)

package gearon.customAnnotation;

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

@Target(value=ElementType.METHOD)
public @interface Overload {

}

接下来,创建相应的处理器以处理由定义的注释注释的元素。对于带有注释的方法@Overload,其签名必须出现多次。或打印错误。

package gearon.customAnnotation;

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("gearon.customAnnotation.Overload")

public class OverloadProcessor extends AbstractProcessor{

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // TODO Auto-generated method stub
        HashMap<String, Integer> map = new HashMap<String, Integer>();

        for(Element element : roundEnv.getElementsAnnotatedWith(Overload.class)){
            String signature = element.getSimpleName().toString();
            int count = map.containsKey(signature) ? map.get(signature) : 0;
            map.put(signature, ++count);
        }

        for(Entry<String, Integer> entry: map.entrySet()){
            if(entry.getValue() == 1){
                processingEnv.getMessager().printMessage(Kind.ERROR, "The method which signature is " + entry.getKey() +  " has not been overloaded");
            }
        }
        return true;
    }
}

将批注及其处理过程打包到jar文件中之后,使用创建一个类@Overload并使用javac.exe对其进行编译。

import gearon.customAnnotation.Overload;

public class OverloadTest {
    @Overload
    public static void foo(){
    }

    @Overload
    public static void foo(String s){

    }

    @Overload
    public static void nonOverloadedMethod(){

    }
} 

由于nonOverloadedMethod()实际上并未过载,因此我们将获得如下输出:

在此处输入图片说明


上面的信息非常尊重JDK6(为此为+1),但是如何也使用JDK5实现相同的功能?使用JDK6 SupportedAnnotationTypes,AbstractProcessor类,它变得更简单,但是过去发生了同样的事情(例如jdk5上的Spring FrameWork 2.5)?JVM如何检测jdk5中的类之类的注释处理器?您能否通过编辑2部分中的答案进行指导(一个用于JDK5,当前版本用于Jdk6 +)?
prajitgandhi

在您的课堂上OverloadProcessor::process为什么entry.getValue() == 1?不必在父类/接口上添加注释,这样roundEnv.getElementsAnnotatedWith(Overload.class)就不会得到父类/接口,对吗?
下雨

我对此部分感到困惑,但我认为您的回答非常有帮助。
下雨

@s̸͉̥̳̼e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼如果将某个方法视为重载,则至少应在Class中定义另一个具有相同名称的方法。
尤金(Eugene)

1
@Raining要说一个方法重载,它必须在同一个类中出现多次,但是如果它是= = 1,则它是一个错误。
KrishPrabakar

5

这是@Overridehttp : //www.docjar.com/html/api/java/lang/Override.java.html

没有什么特别的可以将它与您可能自己编写的注释区分开的。有趣的地方在注释的使用者中。对于像这样的注释@Override,它将在Java编译器本身,静态代码分析工具或您的IDE中。


1
我知道Override注释的源代码。但是它是如何在内部工作的。像如何识别此方法不是覆盖方法还是这个是覆盖方法?还是可以创建我的自定义替代注释?并且它应该具有与Java覆盖注释完全相同的行为
Chirag Dasani 2013年

3
正如我在回答中所说的,行为不是注释的一部分。解释在于消耗注释的事物。因此,除非更改使用者,否则您几乎无法创建自己的自定义版本@Override(或其他标准注释)。
马特·鲍尔

3

基本上,注释只是由编译器或应用程序读取的标记。根据它们的保留策略,它们仅在编译时可用,或者在运行时使用反射可读。

许多框架使用运行时保留,即它们反思地检查类,方法,字段等上是否存在某些注释,如果存在(或不存在)注释,则执行某些操作。另外,注释的成员可以用来传递更多信息。


3

点击此链接。这将为您的问题提供严密的答案。如果我们专注于中的注释Java,则注释是Java 5中引入的,不是特定于Spring的。通常,批注允许您将元数据添加到类,方法或变量。注释可以由编译器(例如,@ Override注释)或诸如spring之类的框架(例如,@ Component注释)解释。

另外,我添加了更多参考。

  1. http://www.codeproject.com/Articles/272736/Understanding-Annotations-in-Java
  2. http://docs.oracle.com/javase/7/docs/api/java/lang/annotation/package-summary.html
  3. http://www.coderanch.com/how-to/java/AnnotationsExample

@LuiggiMendoza添加了Java 1.7注释文档
Ruchira Gayan Ranaweera 2013年

@Ruchira我打开了所有链接,但仍然不清楚它是如何工作的。您能否详细解释一下我,例如考虑作为春季注释。我可以通过使用注释来做所有事情,而无需在spring.xml文件中编写任何配置。在内部将注释绑定到xml配置?
Chirag Dasani


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.