¿Cómo funcionan internamente anotaciones como @Override en Java?

93

¿Alguien puede explicarme cómo funcionan las anotaciones internamente en Java?

Sé cómo podemos crear anotaciones personalizadas usando la biblioteca java.lang.annotation en java. Pero todavía no entiendo cómo funciona internamente, por ejemplo, la anotación @Override.

Estaré realmente agradecido si alguien pudiera explicar eso en detalle.

Chirag Dasani
fuente
4
¿Qué quieres decir con "trabajar internamente"? ¿El compilador? ¿El tiempo de ejecución?
chrylis -cautelyoptimistic-
3
@chrylis Trabajar internamente significa cómo se identifica automáticamente que este método es un método de anulación o que este no es un método de anulación. Es trabajo en ambos tiempos. como anular el trabajo de anotación en tiempo de compilación y la anotación del controlador de primavera funciona en tiempo de ejecución
Chirag Dasani

Respuestas:

138

La primera distinción principal entre los tipos de anotaciones es si se usan en tiempo de compilación y luego se descartan (como @Override) o se colocan en el archivo de clase compilado y están disponibles en tiempo de ejecución (como Spring @Component). Esto está determinado por la política de @Retención de la anotación. Si está escribiendo su propia anotación, deberá decidir si la anotación es útil en tiempo de ejecución (para la configuración automática, quizás) o solo en tiempo de compilación (para verificación o generación de código).

Al compilar código con anotaciones, el compilador ve la anotación tal como ve otros modificadores en los elementos fuente, como modificadores de acceso ( public/ private) o final. Cuando encuentra una anotación, ejecuta un procesador de anotaciones, que es como una clase de complemento que dice que le interesa una anotación específica. El procesador de anotaciones generalmente usa la API de Reflection para inspeccionar los elementos que se están compilando y puede simplemente ejecutar comprobaciones en ellos, modificarlos o generar nuevo código para compilar. @Overridees un ejemplo del primero; usa la API de Reflection para asegurarse de que puede encontrar una coincidencia para la firma del método en una de las superclases y usa Messagerpara causar un error de compilación si no puede.

Hay una serie de tutoriales disponibles sobre cómo escribir procesadores de anotaciones; aquí hay uno útil . Busque en los métodos de la Processorinterfaz cómo el compilador invoca un procesador de anotaciones; la operación principal tiene lugar en el processmétodo, que se llama cada vez que el compilador ve un elemento que tiene una anotación coincidente.

chrylis -cautelosamente optimista-
fuente
4
sería bueno señalarnos exactamente cómo el procesador de anotaciones de Spring analiza el @Component e inyecta la clase en su contenedor
Junchen Liu
@shanyangqu Eso está fuera de tema para la pregunta, que no es específica de Spring. Puede leer las clases de postprocesador usted mismo.
chrylis -cautelyoptimistic-
42

Además de lo que otros sugirieron, te recomiendo que escribas una anotación personalizada y su procesador desde cero para ver cómo funciona la anotación.

En el mío, por ejemplo, he escrito una anotación para comprobar si los métodos están sobrecargados en tiempo de compilación.

En primer lugar, cree una anotación con el nombre Overload. Esta anotación se aplica al método, así que lo anoto con@Target(value=ElementType.METHOD)

package gearon.customAnnotation;

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

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

}

A continuación, cree el procesador correspondiente para manejar los elementos anotados por la anotación definida. Para el método anotado por @Overload, su firma debe aparecer más de una vez. O se imprime el error.

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;
    }
}

Después de empaquetar la anotación y su proceso en un archivo jar, cree una clase @Overloady use javac.exe para compilarla.

import gearon.customAnnotation.Overload;

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

    @Overload
    public static void foo(String s){

    }

    @Overload
    public static void nonOverloadedMethod(){

    }
} 

Dado que en nonOverloadedMethod()realidad no se ha sobrecargado, obtendremos el resultado como se muestra a continuación:

ingrese la descripción de la imagen aquí

Eugenio
fuente
La información anterior es muy buena con respecto a JDK6 (+1 para eso), pero ¿cómo lograr lo mismo usando JDK5 también? Usando JDK6 SupportedAnnotationTypes, la clase AbstractProcessor se volvió más simple, pero ¿cómo sucedió lo mismo en el pasado (como Spring FrameWork 2.5 en jdk5)? ¿Cómo JVM detecta el procesador de anotaciones como clase en jdk5? ¿Puede guiarlo editando la respuesta en la sección 2 (una para JDK5, la versión actual es para Jdk6 +)?
prajitgandhi
En tu clase, OverloadProcessor::process¿por qué es entry.getValue() == 1? No es necesario agregar una anotación en la clase / interfaz principal, por roundEnv.getElementsAnnotatedWith(Overload.class)lo que no obtendrá la clase / interfaz principal, ¿verdad?
Lloviendo
Estoy confundido en esta parte, pero creo que su respuesta es muy útil.
Lloviendo
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼ Si un método se trata como Sobrecarga, debería haber al menos otro método con el mismo nombre definido en la Clase.
Eugene
1
@Raining para que un método diga Sobrecargado, debe aparecer más de una vez en la misma clase, pero si es `== 1` entonces es un error.
KrishPrabakar
5

Aquí está @Override: http://www.docjar.com/html/api/java/lang/Override.java.html .

No tiene nada de especial que lo diferencie de una anotación que podría escribir usted mismo. Las partes interesantes están en los consumidores de las anotaciones. Para una anotación como @Override, eso estaría en el propio compilador de Java, o en una herramienta de análisis de código estático, o en su IDE.

Matt Ball
fuente
1
Conozco el código fuente de la anotación Override. Pero cómo funciona internamente. como cómo se puede identificar este método no es un método de anulación o este es un método de anulación? ¿O puedo crear mi anotación de anulación personalizada? y debería funcionar exactamente el mismo comportamiento que la anotación de anulación de Java
Chirag Dasani
3
Como dije en mi respuesta, el comportamiento no es parte de la anotación. La interpretación radica en las cosas que consumen la anotación. Por eso, a menos que cambie el consumidor, prácticamente no puede crear su propia versión personalizada de @Override(u otras anotaciones estándar).
Matt Ball
3

Básicamente, las anotaciones son solo marcadores que lee el compilador o la aplicación. Dependiendo de su política de retención, están disponibles solo en tiempo de compilación o se pueden leer en tiempo de ejecución mediante reflexión.

Muchos marcos utilizan la retención en tiempo de ejecución, es decir, comprueban de forma reflexiva si algunas anotaciones están presentes en una clase, método, campo, etc. y hacen algo si la anotación está presente (o no). Además, los miembros de las anotaciones se pueden utilizar para transmitir más información.

Thomas
fuente
3

Siga este enlace . Esto proporcionará una respuesta cercana a su problema. Si nos centramos en las anotaciones en Java, las anotaciones se introdujeron en Java 5 y no son específicas de Spring. En general, las anotaciones le permiten agregar metadatos a una clase, método o variable. Una anotación puede ser interpretada por el compilador (por ejemplo, la anotación @Override) o por un marco como spring (por ejemplo, la anotación @Component).

Además estoy agregando más referencias.

  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
Ruchira Gayan Ranaweera
fuente
@LuiggiMendoza java 1.7 annotation doc added
Ruchira Gayan Ranaweera
@Ruchira me abrieron todos los enlaces, pero aún no tengo claro cómo funciona. ¿Puedes explicarme en detalles como considerar como anotaciones de primavera? Puedo hacer todas las cosas usando anotaciones sin escribir ninguna configuración en el archivo spring.xml. ¿Se vincula internamente la anotación a la configuración xml?
Chirag Dasani
@ChiragDasani echa un vistazo a esto. esto puede ayudarlo a static.springsource.org/spring/docs/3.0.0.M3/reference/html/… y también vea esta publicación SO stackoverflow.com/questions/2798181/…
Ruchira Gayan Ranaweera