Como anotações como @Override funcionam internamente em Java?

90

Alguém pode me explicar como as anotações funcionam internamente em java?

Eu sei como podemos criar anotações personalizadas usando a biblioteca java.lang.annotation em java. Mas ainda não entendi como funciona internamente, por exemplo, a anotação @Override.

Ficarei muito grato se alguém puder explicar isso em detalhes.

Chirag Dasani
fonte
4
O que você quer dizer com "trabalhar internamente"? O compilador? O tempo de execução?
chrylis -cautiouslyoptimistic-
3
@chrylis Trabalho internamente significa como é automaticamente identificado que este método é um método de substituição ou este não é um método de substituição. É trabalho em ambos os tempos. como a anotação de substituição funciona em tempo de compilação e a anotação do controlador de primavera funciona em tempo de execução
Chirag Dasani

Respostas:

137

A primeira distinção principal entre os tipos de anotação é se eles são usados ​​em tempo de compilação e depois descartados (como @Override) ou colocados no arquivo de classe compilado e disponíveis em tempo de execução (como Spring @Component). Isso é determinado pela política @Retention da anotação. Se estiver escrevendo sua própria anotação, você precisará decidir se a anotação é útil em tempo de execução (para autoconfiguração, talvez) ou apenas em tempo de compilação (para verificação ou geração de código).

Ao compilar o código com anotações, o compilador vê a anotação da mesma forma que vê outros modificadores nos elementos de origem, como modificadores de acesso ( public/ private) ou final. Ao encontrar uma anotação, ele executa um processador de anotação, que é como uma classe de plug-in que diz que está interessado em uma anotação específica. O processador de anotação geralmente usa o Reflection API para inspecionar os elementos que estão sendo compilados e pode simplesmente executar verificações neles, modificá-los ou gerar um novo código a ser compilado. @Overrideé um exemplo do primeiro; ele usa a API do Reflection para garantir que pode encontrar uma correspondência para a assinatura do método em uma das superclasses e usa o Messagerpara causar um erro de compilação se não puder.

Existem vários tutoriais disponíveis sobre como escrever processadores de anotação; aqui está um útil . Olhe através dos métodos sobre a Processorinterface de por quanto o compilador invoca um processador de anotação; a operação principal ocorre no processmétodo, que é chamado sempre que o compilador vê um elemento que tem uma anotação correspondente.

chrylis -cautiosamente otimista-
fonte
3
seria bom nos mostrar exatamente como o processador de anotação do Spring analisa o @Component e injeta a classe em seu contêiner
Junchen Liu
@shanyangqu Isso está fora do assunto para a questão, que não é específico do Spring. Você mesmo pode ler as classes de pós-processador.
chrylis -cautiouslyoptimistic-
41

Além do que outros sugeriram, recomendo que você escreva uma anotação personalizada e seu processador do zero para ver como a anotação funciona.

No meu, por exemplo, escrevi uma anotação para verificar se os métodos estão sobrecarregados em tempo de compilação.

Em primeiro lugar, crie uma anotação chamada Overload. Esta anotação é aplicada ao método, então eu a anoto com@Target(value=ElementType.METHOD)

package gearon.customAnnotation;

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

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

}

Em seguida, crie o processador correspondente para lidar com elementos anotados por anotação definida. Para o método anotado por @Overload, sua assinatura deve aparecer mais de uma vez. Ou o erro é impresso.

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

Após empacotar a anotação e seu processo em um arquivo jar, crie uma classe @Overloade use javac.exe para compilá-la.

import gearon.customAnnotation.Overload;

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

    @Overload
    public static void foo(String s){

    }

    @Overload
    public static void nonOverloadedMethod(){

    }
} 

Como nonOverloadedMethod()não foi realmente sobrecarregado, obteremos a saída como a seguir:

insira a descrição da imagem aqui

Eugene
fonte
As informações acima são muito boas em relação ao JDK6 (+1 para isso), mas como fazer a mesma coisa usando o JDK5 também? Usando JDK6 SupportedAnnotationTypes, classe AbstractProcessor tornou-se mais simples, mas como a mesma coisa aconteceu no passado (como Spring FrameWork 2.5 em jdk5)? Como JVM detecta processador de anotação como classe em jdk5? você pode orientar editando a resposta na seção 2 (uma para JDK5, a versão atual é para Jdk6 +)?
prajitgandhi
Na sua classe, OverloadProcessor::processpor que é entry.getValue() == 1? Não é necessário adicionar uma anotação na classe / interface pai, então roundEnv.getElementsAnnotatedWith(Overload.class)não obteremos a classe / interface pai, certo?
Chovendo em
Estou confuso nesta parte, mas acho que sua resposta é muito útil.
Chovendo em
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼ Se um método for tratado como Sobrecarga, deve haver pelo menos outro método com o mesmo nome definido na Classe.
Eugene
1
@ Chovendo para um método dizer Sobrecarregado, ele deve aparecer mais de uma vez na mesma classe, mas se for `== 1` então é um erro.
KrishPrabakar
5

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

Não há nada de especial nele que o diferencie de uma anotação que você mesmo possa escrever. Os bits interessantes estão nos consumidores das anotações. Para uma anotação como @Override, isso seria no próprio compilador Java, ou uma ferramenta de análise de código estático, ou seu IDE.

Matt Ball
fonte
1
Eu conheço o código-fonte da anotação Override. Mas como funciona internamente. como como pode ser identificado este método não é o método de substituição ou este é o método de substituição? ou posso criar minha anotação de substituição personalizada? e deve funcionar exatamente da mesma forma que a anotação de substituição de java
Chirag Dasani
3
Como disse na minha resposta, o comportamento não faz parte da anotação. A interpretação está nas coisas que consomem a anotação. Por causa disso, a menos que você mude o consumidor, você não pode praticamente criar sua própria versão personalizada do @Override(ou outras anotações padrão).
Matt Ball
3

Basicamente, as anotações são apenas marcadores que são lidos pelo compilador ou pelo aplicativo. Dependendo de sua política de retenção, eles estão disponíveis apenas em tempo de compilação ou podem ser lidos em tempo de execução usando reflexão.

Muitos frameworks usam retenção em tempo de execução, ou seja, eles verificam reflexivamente se algumas anotações estão presentes em uma classe, método, campo etc. e fazem algo se a anotação está presente (ou não). Além disso, os membros das anotações podem ser usados ​​para passar mais informações.

Thomas
fonte
3

Siga este link . Isso fornecerá uma resposta próxima para o seu problema. Se nos concentramos nas anotações em Java, Annotations foram introduzidas no Java 5 e não são específicas do Spring. Em geral, as anotações permitem adicionar metadados a uma classe, método ou variável. Uma anotação pode ser interpretada pelo compilador (por exemplo, a anotação @Override) ou por uma estrutura como spring (por exemplo, a anotação @Component).

Além disso, estou adicionando mais referências.

  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
fonte
@LuiggiMendoza java 1.7. Documento de anotação adicionado
Ruchira Gayan Ranaweera
@Ruchira abriram todos os links, mas ainda não tenho certeza de como funciona. Você pode me explicar em detalhes, como considerar como anotações de primavera. Posso fazer todas as coisas usando anotações sem escrever nenhuma configuração no arquivo spring.xml. é vincular anotação internamente à configuração xml?
Chirag Dasani
@ChiragDasani, dê uma olhada nisso. isso pode ajudá-lo static.springsource.org/spring/docs/3.0.0.M3/reference/html/… e também veja este post sobre stackoverflow.com/questions/2798181/…
Ruchira Gayan Ranaweera