Java注解笔记

1、使用注解-示例:注解事件处理器

在用户页面编程中,一件更令人讨厌的事情就是组装事件源上的监听器。很多监听器是下面这种形式的:

myButton.addActionListener(() -> doSomething());

在这里我们设计了一个注解来免除这种苦差事。其使用方式如下:

@ActionListenerFor(source="myButton") void doSomething() { ... }

程序员不再需要去调用addActionListener了。相反地,每个方法直接用一个注解标记起来。
ActionListenerInstaller.java

package com.company.v2Annotation08.runtimeAnntations;

import java.awt.event.ActionListener;
import java.lang.reflect.*;

/**
 * Created by kzm on 2020/11/18 19:25
 */
public class ActionListenerInstaller {

    public static void processAnnotations(Object obj){
        try {
            Class<?> cl = obj.getClass();
            for (Method m : cl.getDeclaredMethods()){// 循环class的所有方法
                ActionListenerFor a = m.getAnnotation(ActionListenerFor.class);// 判断每个方法上是否存在指定注解
                if (a != null) {// 如果存在注解
                    Field f = cl.getDeclaredField(a.source());// 获取注解中source属性标注的字段
                    f.setAccessible(true);
                    addListener(f.get(obj), obj, m);// 给该字段添加监听
                }
            }
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    public static void addListener(Object source, final Object param, final Method m) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return m.invoke(param);
            }
        };
        Object listener = Proxy.newProxyInstance(null, new Class[]{ActionListener.class}, handler);
        Method adder = source.getClass().getMethod("addActionListener", ActionListener.class);
        adder.invoke(source, listener);

    }
}

ButtonFrame.java

package com.company.v2Annotation08.buttons3;

import com.company.v2Annotation08.runtimeAnntations.ActionListenerFor;
import com.company.v2Annotation08.runtimeAnntations.ActionListenerInstaller;

import javax.swing.*;
import java.awt.*;

/**
 * Created by kzm on 2020/11/18 19:45
 */
public class ButtonFrame extends Frame {
    private static final int DEFAULT_WIDTH = 300;
    private static final int DEFAULT_HEIGHT = 200;

    private JPanel panel;
    private JButton yellowButton;
    private JButton blueButton;
    private JButton redButton;

    public ButtonFrame()
    {
        setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

        panel = new JPanel();
        add(panel);

        yellowButton = new JButton("Yellow");
        blueButton = new JButton("Blue");
        redButton = new JButton("Red");

        panel.add(yellowButton);
        panel.add(blueButton);
        panel.add(redButton);

        ActionListenerInstaller.processAnnotations(this);
    }

    @ActionListenerFor(source = "yellowButton")
    public void yellowBackground()
    {
        panel.setBackground(Color.YELLOW);
    }

    @ActionListenerFor(source = "blueButton")
    public void blueBackground()
    {
        panel.setBackground(Color.BLUE);
    }

    @ActionListenerFor(source = "redButton")
    public void redBackground()
    {
        panel.setBackground(Color.RED);
    }
}

ActionListenerFor.java

package com.company.v2Annotation08.runtimeAnntations;

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

/**
 * Created by kzm on 2020/11/18 19:27
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionListenerFor {
    String source();
}

当然,这些注解本身不会做任何事情,它们只是存在于源文件中。编译器将它们置于类文件中,并且虚拟机会将它们载入。我们现在需要的是一个分析注解以及安装行为监听器的机制。这也是类ActionListenerInstaller的职责所在。ButtonFrame构造器将调用下面的方法:

ActionListenerInstaller.processAnnotation(this);

静态的processAnnotations方法可以枚举出某个对象接收到的所有方法。对于每一个方法,它先获取ActionListenerFor注解对象,然后再对它进行处理。

Class<?> cl = obj.getClass();
for (Method m : cl.getDeclaredMethods()){// 循环class的所有方法
    ActionListenerFor a = m.getAnnotation(ActionListenerFor.class);// 判断每个方法上是否存在指定注解
    if (a != null) ...
}

这里,我们使用了定义在AnnotatedElement接口中的getAnnotation方法。Method、Constructor、Field、Class和Package这些类都实现了这个接口。
源成员域的名字是存储在注解对象中的。我们可以通过调用source方法对它进行检索,然后查找匹配的成员域。

String fieldName = a.source();
Field f = cl.getDeclaredField(fieldName);
2、注解语法-注解接口

注解是由注解接口来定义的:

modifiers @interface AnnotationName
{
      elementDeclaration1
      elementDeclaration2
}

每个元素声明都具有下面这种形式:

type elementName();

或者

type elementName() default value;

例如,下面这个注解具有两个元素:assignedTo和severity。

public @interface BugReport
{
      String assignedTo() default "[none]";
      int severity();
}

所有的注解接口都隐式地扩展自java.lang.annotation.Annotation接口。

3、注解语法-注解

每个注解都具有下面这种格式:

@annotationName(elementName1=value1, elementName2=value2, ...)

例如,

@BugReport(assignedTo="Harry", severity=10)

如果某个元素的值并未指定,那么就使用声明的默认值。

4、标准注解
注解接口 应用场景 目的
Deprecated 全部 将项标记为过时的
SuppressWarnings 除了包和注解之外的所有情况 阻止某个给定类型的警告信息
Override 方法 检查该方法是否覆盖了某一个超类方法
PostConstruct 方法 被标记的方法应该在构造之后立即被调用
PreDestroy 方法 被标记的方法应该在移除之前立即被调用
Resource 类、接口、方法、域 在类或者接口上:标记为在其他地方要用到的资源;在方法或者域上 :为 “注入” 而标记
Resources 类、接口 一个资源组
Grenerated 全部
Target 注解 指明可以应用这个注解的那些项
Retention 注解 指明这个注解可以保留多久
Documented 注解 指明这个注解应该包含在注解项的文档中
Inherited 注解 指明当这个注解应用于一个类的时候,能够自动被他的子类继承
Repeatable 注解 指明这个注解可以在同一个项上应用多次
5、标准注解-元注解

@Target元注解可以应用于一个注解,以限制该注解可以应用到哪些项上。例如,

@Target({ElementType.TYPE, ElementType.METHOD})
public @interface BugReport

@Target注解的元素类型

元素类型 注解使用场合
ANNOTATTON_TYPE 注解类型声明
PACKAGE
TYPE 类(包括enum) 及接口(包括注解类型)
METHOD 方法
CONSTRUCTOR 构造器
FIELD 成员域
PARAMETER 方法或构造器参数
LOCAL_VARIABLE 局部变量
LOCAL_PARAMETER 类型参数
TYPE_USER 类型用法
@Retention元注解用于指定一个注解应该保留多长时间。
用于@Retention注解的保留策略
保留规则 描述
---- ----
SOURCE 不包括在类文件中的注解
CLASS 包括在类文件中的注解,但是虚拟机不需要将它们载入
RUNTIME 包括在类文件中的注解,并由虚拟机载入。通过反射API可获得它们
posted @ 2020-11-21 22:34  luotuoccc  阅读(72)  评论(0)    收藏  举报