博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(一) 使用注解
阅读量:4049 次
发布时间:2019-05-25

本文共 7648 字,大约阅读时间需要 25 分钟。

是那些插入到源代码中用于某种工具处理的表情。这些表情可以再源码层次上进行操作,或者可以处理编译器将它们纳入到注解类文件中。

注解不会改变对便携的程序的编译方式。Java编译器对包含注解和不包含注解的代码会生成相同的虚拟机指令。
为了能够受益于注解,需要选择一个 处理工具 ,然后向你的处理工具可以理解的代码中插入注解,之后运行该 处理工具。
注解可能的用法:
    附属文件的自动生成,例如部署描述符或者bean信息类。
    测试、日志、事务语义等代码的自动生成。
   
注解的基本概念
例:一个简单的注解的示例:

public class MyClass{        ...        @Test public void checkRandomInsertions(){}    }

 

其中注解 @Test 用于注解checkRandomInsertions方法。

(1)在Java中,注解是当作一个 修饰符 来使用的,它被置于被注解项之前,中间没有分号(修饰符即public和static之类的关键字)。每一个注解的名称签名都加上了@符号,这有点类似于Javadoc的注解。然后,Javadoc注解出现在/**...*/定界符的内部,而注解是代码的一部分。
@Test注解本身并不会做任何事情,它需要工具支持才会有用。
例如,当测试一个类的时候,JUnit4测试工具可能会调用所有标示位@Test的方法。另一个工具可能会删除一个类文件中所有测试方法,以便在对这个类测试完毕后,不会将这些测试方法与程序装载在一起。
(2)注解可以定义成包含元素的形式
例: @Test(timeout="10000")
这些元素可以被阅读这些注解的工具去处理。其他形式的元素也是有可能的。
(3)除了方法外,还可以注解 类、属性及变量,这些注解可以存在于任何可以放置一个像public或者static这样的修饰符的地方。
(4)每个注解都必须通过一个注解接口进行定义。这些接口中的方法与注解中的元素相对应。
例: JUnit的注解TestCase可以如下定义

@Target(ElementType.METHOD)    @Retention(RetentionPolicy.RUNTIME)    public @interface Test{        long timeout() default 0L;    }

 

@interface声明创建了一个真正的Java接口。处理注解的工具将接收那些实现了这个注解接口的对象。一个工具可以调用timeout方法来检索某个特定Test注解的timeout元素。

注解Target和Retention是元注解。它们注解了Test注解,即将Test注解标识成一个只能运用到方法上的注解,并且当类文件载入到虚拟机的时候,仍可以保留下来。
DEMO : 通过注解标记来完成包装事件源上的监听器
很多监听器为如下形式:

myButton.addActionListener(new ActionListener(){        public void actionPerformed(ActionEvent event){            doSomething();        }    });

 

在DEMO中通过例如下面这样的注解来处理这样的问题

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

DEMO:

import java.awt.EventQueue;import javax.swing.JFrame;public class ButtonTest {    public static void main(String[] args) {        EventQueue.invokeLater(new Runnable(){            public void run(){                JFrame frame = new ButtonFrame();                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                frame.setVisible(true);            }        });    }}

 

import java.awt.Color;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JPanel;public class ButtonFrame extends JFrame{       public static final int DEFAULT_WIDTH = 300;    public static final int DEFAULT_HEIGHT = 300;       private JPanel panel;    private JButton yellowButton;    private JButton blueButton;    private JButton redButton;       public ButtonFrame(){        setTitle("ButtonTest");        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);    }}

 

 

/** * 定义一个注解接口 */import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface ActionListenerFor {    String source();}
 

如果只定义了注解接口,注解本身不会不会处理任何问题,它们只是存在于源文件中。编译器将它们置于类文件中,并且虚拟机会将它们载入。
通过一个分析注解以及安装行为的监听器的机制,类ActionListenerInstaller的职责。
(1)ButtonFrame构造器将调用下面的方法:

ActionListenerInstaller.processAnnotations(this);

 

   

(2)ActionListenerInstaller的静态方法processAnnotations用于枚举出某个对象接收到的所有方法。
对于每一个方法,它先获取ActionListenerFor,然后再对它进行处理。

Class
cl = obj.getClass(); for(Method m : cl.getDeclaredMethods()){ ActionListenerFor a = m.getAnnotation(ActionListenerFor.class); if(a != null){ ... } }
 

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

String fieldName = a.source();    Field f = cl.getDeclaredField(fieldName);
 

这表明我们的注解有点局限。源元素必须是一个属性的名字,而不能是本地变量。
(4)对于每一个被注解的方法,我们构造了一个实现了ActionListener接口的代理对象,其actionPerformed方法将调用这个被注解过的方法。
该实例中的注解的功能是通过实例中类ButtonFrame调用实例中类ActionListenerInstaller的processAnnotations方法建立起来的。
在该例中注解的处理流程
   
    @Action ListenerFor
        源文件
          ↓
    Java编译器
          ↓
    @ActionListenerFor
        类文件
          ↓
    Java虚拟机     →   程序运用API反射机制处理注解
   
在该例中,注解是是在运行时进行处理的。
也有可能在源码级别上对它们进行处理。源代码生成器可能已经产生了用于添加监听器的代码,
注解也可能已经在字节码级别上进行过处理,字节码编译器可能已经将addActionListener调用置于框体构造器了。
可以利用一些类库相对直截了当地实现这项任务。

 

/** * 分析注解以及安装行为监听器 */import java.awt.event.ActionListener;import java.lang.annotation.Annotation;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ActionListenerInstaller {    public static void processAnnotations(Object obj){        try{            Class
clazz = obj.getClass(); for(Method m : clazz.getDeclaredMethods()){ ActionListenerFor a = m.getAnnotation(ActionListenerFor.class); if(a != null){ String fieldName = a.source(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); addListener(field.get(obj), obj, m); } } }catch(Exception e){ e.printStackTrace(); } } private static void addListener(Object source, final Object param, final Method method) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{ InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { return method.invoke(param); } }; Object listener = Proxy.newProxyInstance(null, new Class[]{java.awt.event.ActionListener.class}, handler); Method adder = source.getClass().getMethod("addActionListener", ActionListener.class); adder.invoke(source, listener); }}
 

DEMO 分析实例中注解,分析注解类型,并打印指定注解元素

public class Main {    public static void main(String[] args) {        Main m = new Main();        MyListenerInstaller.processAnnotations(m);    }    @MyListener(element1="value1", element2=1)    public void test(){}}
 
import java.lang.annotation.Annotation;import java.lang.reflect.Method;public class MyListenerInstaller {    public static void processAnnotations(Object obj){            Class
clazz = obj.getClass(); for(Method m : clazz.getDeclaredMethods()){ Annotation[] annotations = m.getDeclaredAnnotations(); if(annotations.length>0){ for(Annotation ann : annotations){ Class typeClass = ann.annotationType(); if(typeClass == MyListener.class){ MyListener ml = (MyListener) ann; System.out.println(ml.element1()); System.out.println(ml.element2()); } } } } }}
 

接口 java.lang.reflect.AnnotatedElement

boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) : 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。
<T extends Annotation> T getAnnotation(Class<T> annotationClass) : 如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getAnnotations() : 返回此元素上存在的所有注解。包括继承来的注解。如果没有出现任何注解,那么将返回一个长度为0的数组。
Annotation[] getDeclaredAnnotations() : 返回直接存在于此元素上的所有注解。获得声明该项的所有注解,不包含继承而来的注解。如果没有出现任何注解,那么将返回一个长度为0的数组。

转载地址:http://layci.baihongyu.com/

你可能感兴趣的文章
【leetcode】Linked List Cycle (python)
查看>>
【leetcode】Candy(python)
查看>>
【leetcode】Clone Graph(python)
查看>>
【leetcode】Sum Root to leaf Numbers
查看>>
【leetcode】Pascal's Triangle II (python)
查看>>
java自定义容器排序的两种方法
查看>>
如何成为编程高手
查看>>
本科生的编程水平到底有多高
查看>>
AngularJS2中最基本的文件说明
查看>>
从头开始学习jsp(2)——jsp的基本语法
查看>>
使用与或运算完成两个整数的相加
查看>>
备忘:java中的递归
查看>>
DIV/CSS:一个贴在左上角的标签
查看>>
Solr及Spring-Data-Solr入门学习
查看>>
Vue组件
查看>>
python_time模块
查看>>
python_configparser(解析ini)
查看>>
selenium学习资料
查看>>
<转>文档视图指针互获
查看>>
从mysql中 导出/导入表及数据
查看>>