进阶Java编程(13)反射与Annotation
1,反射取得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 com.mufasa.demo1; 2 3 import java.lang.annotation.Annotation; 4 import java.lang.reflect.Method; 5 6 public class JavaAPIDemo { 7 public static void main(String[] args) throws NoSuchMethodException { 8 {//获取接口上的Annotation信息 9 Annotation[] annotations=IMessage.class.getAnnotations(); 10 for(Annotation temp:annotations){ 11 System.out.println(temp); 12 } 13 } 14 System.out.println("-------"); 15 {//①获取MessageImpl子类上的Annotation,失败;②获取MessageImpl.toString方法 16 Method method=MessageImpl.class.getDeclaredMethod("send", String.class); 17 Annotation[] annotations=method.getAnnotations(); 18 for(Annotation temp:annotations){ 19 System.out.println(temp); 20 } 21 } 22 } 23 }
1 package com.mufasa.demo1; 2 /* 3 什么是函数式接口(Functional Interface) 4 其实之前在讲Lambda表达式的时候提到过,所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。 5 这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。 6 函数式接口用途 7 它们主要用在Lambda表达式和方法引用(实际上也可认为是Lambda表达式)上。 8 */ 9 @FunctionalInterface 10 @Deprecated(since = "1.5") 11 interface IMessage {//现在这里有两个Annotation 12 public void send(String msg); 13 }
1 package com.mufasa.demo1; 2 3 import java.io.Serializable; 4 5 @SuppressWarnings("serial")//无法在程序执行的时候获取 6 public class MessageImpl implements IMessage, Serializable { 7 @Override 8 public void send(String msg) { 9 System.out.println("【消息发送】"+msg); 10 } 11 }
不同的Annotation有它存在的范围,下面对比两个Annotation:
现在发现【@FunctionalInterface】是在运行时生效的Annotation,所以当程序执行的时候可以获取此Annotation,而【@SuppressWarnings】是在源代码编写的时候有效。而在【RetentionPolicy】枚举类中还有一个class的定义,指的是类定义的时候生效。
2,自定义Annotation
现在已经清楚了Annotation的获取,以及Annotation的运行策略,但是最为关键性的一个因素是如何可以实现自定义的Annotation呢?为此在Java里面提供有新的语法,使用【@interface】来定义Annotation。
·范例:自定义Annotation
1 package com.mufasa.demo2; 2 3 import java.lang.annotation.Retention; 4 import java.lang.annotation.RetentionPolicy; 5 6 @Retention(RetentionPolicy.RUNTIME)//定义Annotation的运行策略 7 @interface DefaultAnnotation {//自定义Annotation 8 public String title();//获取数据 9 public String url() default "www.cnblogs.com";//获取数据,默认值 10 }
1 package com.mufasa.demo2; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 6 public class JavaAPIDemo { 7 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 8 Method method=Message.class.getMethod("send", String.class);//获取指定方法 9 DefaultAnnotation annotation=method.getAnnotation(DefaultAnnotation.class);//获取指定Annotation 10 // System.out.println(annotation.title()); 11 // System.out.println(annotation.url()); 12 String msg=annotation.title()+"("+annotation.url()+")";//消息内容 13 method.invoke(Message.class.getDeclaredConstructor().newInstance(),msg); 14 } 15 }
使用Annotation之后最大的特点就是可以结合反射机制实现程序的处理。
3,工厂设计模式与Annotation整合
现在已经清楚Annotation的整体作用,但是Annotation在开发之中能做哪些事情呢?为了帮助大家进一步理解Annotation的处理目的下面将结合工厂设计模式来应用Annotation操作。
1 package com.mufasa.demo3; 2 3 import java.lang.annotation.Retention; 4 import java.lang.annotation.RetentionPolicy; 5 6 @Retention(RetentionPolicy.RUNTIME) 7 public @interface UseMessage { 8 public Class<?> clazz(); 9 }
1 package com.mufasa.demo3; 2 3 @UseMessage(clazz = MessageImpl.class)//!!!通过注解来控制整个程序的完成!!! 4 public class MessageService { 5 private IMessage message; 6 public MessageService(){ 7 UseMessage useMessage=MessageService.class.getAnnotation(UseMessage.class); 8 this.message=(IMessage) Factory.getInstance(useMessage.clazz()); 9 } 10 public void send(String msg){ 11 this.message.send(msg); 12 } 13 }
1 package com.mufasa.demo3; 2 3 public class JavaAPIDemo { 4 public static void main(String[] args){ 5 //①普通 6 // IMessage msg=Factory.getInstance(MessageImpl.class); 7 // msg.send("www.cnblogs.com"); 8 //②特殊 9 MessageService messageService=new MessageService(); 10 messageService.send("www.cnblogs.com"); 11 } 12 } 13 /* 14 【代理连接】进行消息发送通道的连接 15 【消息发送】www.cnblogs.com 16 【代理断开】关闭连接通道 17 */
1 package com.mufasa.demo3; 2 3 import java.lang.reflect.InvocationTargetException; 4 5 class Factory { 6 private Factory(){} 7 public static <T> T getInstance(Class<T> clazz){//直接返回实例化操作对象 8 try{ 9 return (T) new MessageProxy().bind(clazz.getDeclaredConstructor().newInstance()); 10 }catch (Exception e){ 11 e.printStackTrace(); 12 return null; 13 } 14 } 15 }
由于Annotation的存在,所以对于面向接口的编程的配置处理将可以直接利用Annotation的属性完成控制,从而使得整体代码变得简洁。