Java反射反射与注解

Posted 水木竹水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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。 


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

Java 注解与反射 基础

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

Java注解与反射

java反射与注解结合使用(根据传入对象输出查询sql)

Java反射反射与注解

Java---注解与反射