`

(一) 使用注解

阅读更多

是那些插入到源代码中用于某种工具处理的表情。这些表情可以再源码层次上进行操作,或者可以处理编译器将它们纳入到注解类文件中。
注解不会改变对便携的程序的编译方式。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的数组。

分享到:
评论

相关推荐

    在springboot中使用注解将值注入参数的操作

    在本例中,我们定义了一个名为 InfoResolver 的注解处理类,该类使用自定义的注解 @InfoAnnotation 并将值注入参数。 ```java public class InfoResolver implements HandlerMethodArgumentResolver { @Override ...

    Java 使用注解拼接SQL语句

    "Java使用注解拼接SQL语句"是一个常见的实践,它使得动态构建SQL查询变得更为简洁和可维护。这种技术通常与ORM(对象关系映射)框架如MyBatis或Hibernate结合使用,但也可以通过自定义处理逻辑实现。 1. **自定义...

    Hibernate关于注解的一对多,多对多,一对一

    本篇文章将深入探讨Hibernate中注解的一对多、多对多和一对一关系映射。 ### 一对多关系 在现实生活中,例如一个班级有一个班主任,而班主任可以管理多个学生,这就是典型的“一对多”关系。在Hibernate中,我们...

    使用注解的一对多和多对多

    本文将深入探讨"使用注解的一对多和多对多"这一主题,结合`hib1_使用注解的一对多和多对多`的源码包,我们将了解如何利用注解来实现这两种关联关系。 一、注解简述 注解(Annotation)是Java提供的一种元数据机制,...

    JavaEE 使用注解配置Bean的一个示例

    在JavaEE开发中,注解(Annotation)已经成为了一种强大的工具,它允许开发者在代码中嵌入元数据,简化了传统的XML配置方式。本示例将深入探讨如何使用注解来配置Bean,使得应用程序的配置更加简洁、直观。 首先,...

    Java自定义注解使用反射获取字段注解

    接下来,我们可以创建一个类并使用自定义注解标记某个字段: ```java public class AnnotationDemo { @MyCustomAnnotation("测试注解") private String testField; } ``` 现在,我们可以使用反射机制来获取这个...

    Android使用注解查找控件

    在Android开发中,注解(Annotation)是一种元数据,它提供了在代码中附加信息的方式,这些信息可以被编译器或者运行时环境用来执行特定的操作。`@Inject`注解通常与依赖注入框架如Dagger或Butter Knife关联,用于...

    使用Spring的注解方式实现AOP的细节

    4. **@Component**: 为了使切面类成为Spring容器的一部分,需要使用@Component或其他类似注解(如@Service、@Repository)标记。 5. **@EnableAspectJAutoProxy**: 在Spring配置类上添加此注解,启用基于Java代理的...

    Android annotations 注解 使用

    在Android开发中,注解(Annotations)是一种强大的工具,它能帮助我们简化代码,提高代码的可读性和可维护性。Android Annotations库是专门为Android应用设计的一个注解处理框架,它提供了一系列预定义的注解,可以...

    java关于注解使用的例子

    4. **注解使用** 注解可以应用于类、接口、方法、变量等,如: ```java @MyAnnotation(number = 1, name = "Test") public class TestClass { // ... } ``` 5. **元注解** 元注解是在注解上使用的注解,...

    spring使用注解依赖jar

    在本主题中,我们将深入探讨"spring使用注解依赖jar"这一关键知识点,包括Spring框架如何通过注解来实现依赖注入,以及如何管理相关的jar包。 首先,Spring框架的核心特性之一就是依赖注入(Dependency Injection,...

    使用注解javaBean关联数据库字段

    在Java编程中,注解(Annotation)是一种元数据,它提供了在代码中嵌入信息的方式,这些信息可以被编译器、JVM或其他工具读取并处理。在JavaBean中使用注解,可以极大地简化对象与数据库之间的映射,使得数据访问...

    java注解webservice学习第一篇

    Java注解是Java编程语言中的一个重要特性,它允许在代码中添加元数据,为编译器、解释器或任何其他工具提供额外的信息。在Java Web服务(WebService)开发中,注解的应用极大地简化了服务的创建和配置。本文将深入...

    使用Spring 2.5 基于注解驱动的 Spring MVC详解

    例如,在 BbtForumController 中,我们使用 @Controller 注解来标记该类为一个 Controller。 使用 @RequestMapping 注解 @RequestMapping 注解用于标记一个 Controller 方法,以便将其映射到一个特定的 URL 上。在...

    Struts 2使用注解配置Action

    2. `@Results`: 如果一个类中有多个Action方法,可以使用`@Results`注解来定义一组共用的结果。这可以避免在每个方法中重复声明相同的Result。 3. `@Namespace`: 用于定义Action的命名空间,帮助组织和隔离不同的...

    注解的使用

    例如,检查一个类是否使用了`@Entity`注解,以便确定是否需要进行数据库操作。 ### 7. 总结 注解极大地简化了Java开发,尤其是当涉及到代码生成、编译时检查和运行时行为改变时。通过理解和有效利用注解,可以提高...

    Spring java注解,元注解和自定义注解

    - 可以配合@ComponentScan注解使用,自动扫描指定包下的所有组件。 2. **@Service** - 特别适用于业务逻辑层的服务组件。 - 实际上是@Component的一个特例,提供了更明确的意义。 3. **@Repository** - 用于...

    使用注解配置Action

    在Struts2框架中,注解配置是一种便捷的方式,它允许开发者通过在类或方法上添加特定的注解来替代传统的XML配置文件。这种方式提高了代码的可读性和减少了配置文件的复杂性。本文将深入探讨如何使用注解配置Action,...

    ssh全注解项目(一对一,一对多,多对多)

    对于一对一、一对多和多对多的关系映射,Hibernate提供了`@OneToOne`、`@OneToMany`、`@ManyToOne`和`@ManyToMany`注解。比如: - `@OneToOne`用于建立一对一关系,可以使用`@JoinColumn`来指定外键所在的列。 - `@...

Global site tag (gtag.js) - Google Analytics