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可获得它们 |

浙公网安备 33010602011771号