反射与Annotation

Posted skykuqi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反射与Annotation相关的知识,希望对你有一定的参考价值。

一.反射与Annotation
  从JDK1.5之后,java开发提供了Annotation技术支持,这种技术为项目的编写带来的新的模型,经过了多年的发展,Annotation的技术得到了非常广泛的应用,并且已经在所有的项目开发之中都会存在.
--获取Annotation:在进行类或方法定义的时候都可以使用一系列的Annotation进行色声明,于是如果要想获取这些Annotation的信息,那么就可以直接通过反射来完成.在java.lang.reflect里面,有一个叫做AccessibleObject类,在这个类中提供有获取Annotation的方法:
  获取全部Annotation: public Annotation[] getAnnotations()
  获取指定的Annotation: public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
--范例:定义一个接口,并且在接口上使用Annotation

 1 package 反射.反射与Annotation;
 2 
 3 import java.lang.annotation.Annotation;
 4 
 5 /**
 6  * @author : S K Y
 7  * @version :0.0.1
 8  */
 9 @FunctionalInterface
10 @Deprecated
11 interface IMessage             //存在两个Annotation注解
12     public void send();
13 
14 
15 @SuppressWarnings("serial")
16 class MessageImpl implements IMessage 
17     @Override
18     public void send() 
19         System.out.println("消息发送...");
20     
21 
22 
23 public class AnnotationDemo 
24     public static void main(String[] args) 
25         //获取接口上的Annotation信息
26         Annotation[] annotations = IMessage.class.getAnnotations();     //获取接口上的所有的Annotation
27         for (Annotation annotation : annotations) 
28             System.out.println(annotation);
29         
30     
31 

--运行结果

@java.lang.FunctionalInterface()
@java.lang.Deprecated()

Process finished with exit code 0

--尝试获取实现类的注解信息

 1 public class AnnotationDemo 
 2     public static void main(String[] args) 
 3         //获取接口上的Annotation信息
 4         Annotation[] annotations = IMessage.class.getAnnotations();     //获取接口上的所有的Annotation
 5         for (Annotation annotation : annotations) 
 6             System.out.println(annotation);
 7         
 8         System.out.println("=========================");
 9         //获取子类上的Annotation
10         Annotation[] implAnnotations = MessageImpl.class.getAnnotations();
11         for (Annotation implAnnotation : implAnnotations) 
12             System.out.println(implAnnotation);
13         
14     
15 

--运行结果

@java.lang.FunctionalInterface()
@java.lang.Deprecated()
=========================

Process finished with exit code 0

--发现并没有获取到注解@SuppressWarnings("serial"),其原因是该注解无法在程序执行的时候获取.那么尝试获取MessageImpl.send()方法上的Annotation会怎么样呢:

 1 public class AnnotationDemo 
 2     public static void main(String[] args) throws NoSuchMethodException 
 3         //获取接口上的Annotation信息
 4         Annotation[] annotations = IMessage.class.getAnnotations();     //获取接口上的所有的Annotation
 5         for (Annotation annotation : annotations) 
 6             System.out.println(annotation);
 7         
 8         System.out.println("=========================");
 9         //获取子类上的Annotation
10         Annotation[] implAnnotations = MessageImpl.class.getAnnotations();
11         for (Annotation implAnnotation : implAnnotations) 
12             System.out.println(implAnnotation);
13         
14         //尝试获取MessageImpl.toString()方法上的Annotation
15         System.out.println("==============================");
16         Method send = MessageImpl.class.getDeclaredMethod("send");
17         Annotation[] sendAnnotations = send.getAnnotations();
18         for (Annotation sendAnnotation : sendAnnotations) 
19             System.out.println(sendAnnotation);
20         
21     
22 

--运行结果

@java.lang.FunctionalInterface()
@java.lang.Deprecated()
=========================
==============================

Process finished with exit code 0

--可以发现也无法在运行时获取到 @Override注解的相关信息.不同的Annotation有他的存在的范围:
  @FunctionalInterface:

    @Documented
    @Retention(RetentionPolicy.RUNTIME)      //描述的是运行是生效
    @Target(ElementType.TYPE)
    public @interface FunctionalInterface

  @SuppressWarnings("serial"):

    @Target(TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE)
    @Retention(RetentionPolicy.SOURCE)      //描述的是在源代码时生效
    public @interface SuppressWarnings
    String[] value();
    

--可以发现"@FunctionalInterface"是在程序运行时生效的Annotation,所以当程序执行的时候可以获取此Annotation,而"@SuppressWarnings"是在源代码编写的时候有效.管观察RetentionPolicy的定义:

public enum RetentionPolicy

 

SOURCE,

 

CLASS,

 

RUNTIME

--在RetentionPolicy枚举类中还存在一个class的定义指的是在类定义的时候生效.

二.自定义Annotation
  现在已经清楚了Annotation的获取,以及Annotation的运行策略,但是最为关键性的因素是如何可以实现自定义的Annotation.为此在java中提供有新的语法使用"@interface"来定义Annotation.
范例:自定义Annotation

 1 package 反射.反射与Annotation;
 2 
 3 import java.lang.annotation.Retention;
 4 import java.lang.annotation.RetentionPolicy;
 5 import java.lang.reflect.InvocationTargetException;
 6 import java.lang.reflect.Method;
 7 
 8 /**
 9  * @author : S K Y
10  * @version :0.0.1
11  */
12 @Retention(RetentionPolicy.RUNTIME)
13         //定义Annotation运行时的策略
14 @interface DefaultAnnotation        //自定义的Annotation,
15     public String title();      //获取数据
16 
17     public String url() default "获取数据的默认值";     //获取数据,默认值
18 
19 
20 class Message 
21     @DefaultAnnotation(title = "sendMessage")   //title不具备默认值,因此必须去显示的定义
22     public void send(String msg) 
23         System.out.println("[消息发送]" + msg);
24     
25 
26 
27 public class MyAnnotationDemo 
28     public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException 
29         Method method = Message.class.getDeclaredMethod("send", String.class);  //获取指定的方法
30         DefaultAnnotation annotation = method.getAnnotation(DefaultAnnotation.class);//获取指定的Annotation1
31         String title = annotation.title();      //直接调用Annotation中的方法
32         String url = annotation.url();
33         System.out.println(title + " " + url);
34         String msg = annotation.title() + " " + url;
35         method.invoke(Message.class.getDeclaredConstructor().newInstance(),msg);        //利用反射实现消息的发送
36     
37 

--运行结果

sendMessage 获取数据的默认值
[消息发送]sendMessage 获取数据的默认值

Process finished with exit code 0

--使用Annotation之后的最大特点是可以结合反射机制实现程序的处理.

三.工厂设计模式与Annotation整合
  在清楚了Annotation的整体作用之后,但是Annotation在开发之中,到底可以完成什么样的功能.为了更好的理解Annotation的处理操作的目的,下面将结合工厂设计模式来应用Annotation操作.

 1 package 反射.Annotation与工厂设计模式整合;
 2 
 3 
 4 import java.lang.reflect.InvocationHandler;
 5 import java.lang.reflect.Method;
 6 import java.lang.reflect.Proxy;
 7 
 8 /**
 9  * @author : S K Y
10  * @version :0.0.1
11  */
12 interface IMessage 
13     public void send(String msg);
14 
15 
16 class MessageImpl implements IMessage 
17     @Override
18     public void send(String msg) 
19         System.out.println("[消息发送]" + msg);
20     
21 
22 
23 class MessageProxy implements InvocationHandler 
24     private Object target;
25 
26 
27     public Object bind(Object target) 
28         this.target = target;
29         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
30     
31 
32     public boolean connect() 
33         System.out.println("[代理操作]进行消息发送通道的连接");
34         return true;
35     
36 
37     public void close() 
38         System.out.println("[代理操作]关闭连接通道.");
39     
40 
41     @Override
42     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
43         try 
44             if (this.connect()) 
45                 return method.invoke(this.target, args);
46              else 
47                 throw new Exception("[error]消息无法进行发送");
48             
49          finally 
50             this.close();
51         
52     
53 
54 
55 class Factory 
56     private Factory() 
57     
58     public static <T> T getInstance(Class<T> tClass)        //直接返回一个实例化的操作对象
59         try 
60             return (T) new MessageProxy().bind(tClass.getDeclaredConstructor().newInstance());
61          catch (Exception e) 
62             e.printStackTrace();
63             return null;
64         
65     
66 
67 class MessageService
68     private IMessage message;
69 
70     public MessageService() 
71         this.message = Factory.getInstance(MessageImpl.class);
72     
73     public void send(String msg)
74         this.message.send(msg);
75     
76 
77 public class AnnotationFactoryDemo 
78     public static void main(String[] args) throws IllegalAccessException, InstantiationException 
79        MessageService service = new MessageService();
80        service.send("hello");
81 
82     
83 

--运行结果

[代理操作]进行消息发送通道的连接
[消息发送]hello
[代理操作]关闭连接通道.

Process finished with exit code 0

--上述代码的实现每次都需要在MessageService类中给出MessageImpl.class的声明,使用注解形式来简化代码

 1 package 反射.Annotation与工厂设计模式整合;
 2 
 3 
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.reflect.InvocationHandler;
 7 import java.lang.reflect.Method;
 8 import java.lang.reflect.Proxy;
 9 
10 /**
11  * @author : S K Y
12  * @version :0.0.1
13  */
14 interface IMessage 
15     public void send(String msg);
16 
17 
18 class MessageImpl implements IMessage 
19     @Override
20     public void send(String msg) 
21         System.out.println("[消息发送]" + msg);
22     
23 
24 
25 class MessageProxy implements InvocationHandler 
26     private Object target;
27 
28 
29     public Object bind(Object target) 
30         this.target = target;
31         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
32     
33 
34     public boolean connect() 
35         System.out.println("[代理操作]进行消息发送通道的连接");
36         return true;
37     
38 
39     public void close() 
40         System.out.println("[代理操作]关闭连接通道.");
41     
42 
43     @Override
44     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
45         try 
46             if (this.connect()) 
47                 return method.invoke(this.target, args);
48              else 
49                 throw new Exception("[error]消息无法进行发送");
50             
51          finally 
52             this.close();
53         
54     
55 
56 
57 class Factory 
58     private Factory() 
59     
60     public static <T> T getInstance(Class<T> tClass)        //直接返回一个实例化的操作对象
61         try 
62             return (T) new MessageProxy().bind(tClass.getDeclaredConstructor().newInstance());
63          catch (Exception e) 
64             e.printStackTrace();
65             return null;
66         
67     
68 
69 @Retention(RetentionPolicy.RUNTIME)
70 @interface UseMessage
71     public Class<?> thisClass();
72 
73 @UseMessage(thisClass = MessageImpl.class)
74 class MessageService
75     private IMessage message;
76 
77     public MessageService() 
78         UseMessage annotation = MessageService.class.getAnnotation(UseMessage.class);
79         this.message = (IMessage) Factory.getInstance(annotation.thisClass());
80     
81     public void send(String msg)
82         this.message.send(msg);
83     
84 
85 public class AnnotationFactoryDemo 
86     public static void main(String[] args) throws IllegalAccessException, InstantiationException 
87        MessageService service = new MessageService();
88        service.send("hello");
89 
90     
91 

--这样我们就可以依据注解来修改整个程序的功能实现.由于Annotation的存在,对于面向接口的配置处理可以直接利用Annotation的属性完成控制,从而使得整体代码变得简洁.

以上是关于反射与Annotation的主要内容,如果未能解决你的问题,请参考以下文章

如何在freemarker模板中调用反射方法?

如何获取被指定Annotation注释的所有类

反射与类加载之反射基本概念与Class

JAVA基础:注解与反射的使用方法与场景

熬夜刚完的注解与反射

C#反射与特性:反射基础