Java反射(四)反射与注解
开发过程中使用注解将增加程序的灵活性和扩展性,注解可以修饰接口、类、方法、属性等。
1.反射获取注解
能够通过反射获取类上的注解,主要依赖于核心类AccessibleObject(如下图,Java10的DOC),其实现了AnnotatedElement类。另外其子类包含Field、Executable(Method和Constructor)。
还可以通过该类可以设置属性的可见性,如getFields()获取所有属性,其中只包含父类的public属性,通过setAccessible(true),即可获取私有属性。
(1)获取注解主要方法:
获取所有注解(包含继承的):public Annotation[] getAnnotations()
获取指定注解(包含继承的):public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
获取所有注解(不包含继承的):public Annotation[] getDeclaredAnnotations()
获取指定注解(不包含继承的):public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
(2)什么类型的注解可以通过反射获取到?
① 只有被Retention(RUNTIME)修饰的注解接口才可以通过反射在运行时获取,其中RUNTIME为 RetentionPolicy
② Retention注解标识对应注解维持到什么阶段,具体由RetentionPolicy决定
③ 枚举类RetentionPolicy为注解维持策略,取值SOURCE(编译期)、CLASS(维持到class文件,默认值)、RUNTIME(维持到运行时)
如注解SuppressWarnings维持到SOURCE,无法通过反射获取到该注解
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,MODULE}) @Retention(SOURCE) public @interface SuppressWarnings
如功能接口注解FunctionalInterface,维持到RUNTIME,即可通过反射获取到该注解。
@Documented @Retention(RUNTIME) @Target(TYPE) public @interface FunctionalInterface
2.自定义注解
自定义注解:@interface.
下边是自定义注解,声明title,url(带默认值),seq(带默认值),并将注解修饰到方法上,如下:
import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation{ public String title(); public String url() default "www.baidu.com"; public int seq() default 0; } class Message{ @MyAnnotation(title = "message", seq = 1) public void send(String msg, int seq){ System.out.println("消息发送:"+msg); System.out.println("消息序号:"+seq); } }
测试:获取Method对象和对应的所有注解,并通过判断是否存在自定义的注解,进行调用对应方法,如下:
public class AnnotationTest { public static void main(String[] args) throws Exception{ Method method = Message.class.getDeclaredMethod("send", String.class, int.class); Annotation[] anno = method.getDeclaredAnnotations(); for(Annotation an : anno){ if(an instanceof MyAnnotation){ MyAnnotation an1 = (MyAnnotation)an; String msg =an1.title() + " " + an1.url(); method.invoke(Message.class.getDeclaredConstructor().newInstance(), msg, an1.seq()); } } } }
输出:
消息发送:message www.baidu.com
消息序号:1
3.工厂设计模式与Annotation
注解可以加强代码的复用,可以控制代码具体实现,下边举例综合工厂设计模式、代理和Annotation。
/** * 指定运行的注解类 */ @Retention(RetentionPolicy.RUNTIME) @interface ToClass{ public Class<?> clazz(); } /** * 工厂类 */ class InstanceFactory{ private InstanceFactory(){} public static <T> T getInstance(Class<T> clazz){ try{ //代理+反射实例化 return (T)new MyProxy().bind(clazz.getDeclaredConstructor().newInstance(), clazz); }catch (Exception e){ return null; } } } /** * 动态代理类 */ class MyProxy implements InvocationHandler{ private Object target; public <T> T bind(Object target, Class<T> clazz){ this.target = target; return (T)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ try { if(this.connect()){ return method.invoke(target, args); } }finally { this.close(); } return null; } private boolean connect(){ System.out.println("创建连接"); return true; } private void close(){ System.out.println("关闭连接"); } }
注解使用,通过该注解控制具体属性值,该例中设置clazz=MsgImpl.class,通过工厂生成代理后的对象,并赋给MsgService实例。
@ToClass(clazz = MsgImpl.class) class MsgService{ private Msg msg; public MsgService(){ ToClass clazzAnno = MsgService.class.getDeclaredAnnotation(ToClass.class); msg = (Msg)InstanceFactory.getInstance(clazzAnno.clazz()); } public void send(String text){ msg.send(text); } public String recv(String text){ return msg.recv(text); } } interface Msg{ void send(String msg); String recv(String msg); } class MsgImpl implements Msg{ public void send(String msg) { System.out.println("消息发送:"+msg); } public String recv(String msg) { System.out.println("消息接收:"+msg); return msg; } }
测试:
public static void main(String[] args){ MsgService msgService = new MsgService(); msgService.send("hello"); msgService.recv("hello world"); }
输出:
创建连接
消息发送:hello
关闭连接
创建连接
消息接收:hello world
关闭连接
当把MsgService改为如下时,底层将执行NetMsgImpl对用的方法。
@ToClass(clazz = NetMsgImpl.class) class MsgService{ ..... }
总结
自定义注解可以控制代码具体实现,增加代码的复用。另外,将反射与注解结合,可以实现具备特定功能的框架,如Spring。