反射与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

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@FunctionalInterface//程序执行时可以获取
@Deprecated(since = "1.0")//程序执行时可以获取
interface IMessage {//有2个Annotation
    void send(String message);
}
@SuppressWarnings("serial")//该Annotation无法在程序执行时获取
class MessageImpl implements IMessage, Serializable {
    @Override//无法在程序执行时获取
    public void send(String message) {
        System.out.println("【消息发送】" + message);
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        {//获取IMessage接口上的Annotation信息
            Annotation[] annotations = IMessage.class.getAnnotations();//获取接口上的全部Annotation
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
                //@java.lang.FunctionalInterface()
                //@java.lang.Deprecated(forRemoval=false, since="1.0")
            }
        }
        System.out.println("-----------------------");
        {//获取MessageImpl类上的Annotation信息
            Annotation[] annotations = MessageImpl.class.getAnnotations();//获取类上的全部Annotation
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        }
        System.out.println("-----------------------");
        {//获取MessageImpl.send()方法上的Annotation信息
            Method method = MessageImpl.class.getDeclaredMethod("send", String.class);
            Annotation[] annotations = method.getAnnotations();//获取方法上的全部Annotation
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        }
    }
}

  不同的Annotation有它的存在范围,下面对比两个Annotation:
@FunctionalInterface(运行时):

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

@SuppressWarnings(源代码):

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {}

现在发现“@FunctionalInterface”是在运行时生效的,所以程序执行时可以获取;
“@SuppressWarnings”是在源代码编写时有效;
在RetentionPolicy枚举类中还有一个class的定义,指的是在类定义时生效。

自定义Annotation

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

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
@Retention(RetentionPolicy.RUNTIME)//定义Annotation的运行策略
@interface DefaultAnnotation {//自定义的Annotation
    String title();//标题
    String url() default "www.baidu.com";//地址,默认值
}
class Message {
    @DefaultAnnotation(title = "MLDN")
    public void send(String message) {
        System.out.println("【消息发送】" + message);
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Method method = Message.class.getMethod("send",String.class);
        DefaultAnnotation  annotation = method.getAnnotation(DefaultAnnotation.class);
//        //直接调用Annotation中的方法
        String message = annotation.title()+"("+annotation.url()+")";
        Object obj= Message.class.getDeclaredConstructor().newInstance();
        method.invoke(obj, message);//【消息发送】MLDN(www.baidu.com)
    }
}

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

工厂设计模式与Annotation整合

  现在已经清楚了Annotation的整体作用,但是Annotation到底在开发中能做哪些事情呢?为了进一步理解Annotation的处理目的,下面将结合工厂设计模式来应用Annotation操作。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        MessageService service=new MessageService();
        service.send("www.baidu.com");
    }
}
@Retention(RetentionPolicy.RUNTIME)
@interface UserMessage{
    Class <?> clazz();
}
@UserMessage(clazz =NetMessageImpl.class )
class MessageService{
    private IMessage message;
    public MessageService(){
        UserMessage userMessage=MessageService.class.getAnnotation(UserMessage.class);
        Class<?> clazz=userMessage.clazz();
        this.message = (IMessage)Factory.getInstance(clazz);
    }
    public void send(String message){
        this.message.send(message);
    }
}
class Factory  {
    private Factory() {}
    public static <T> T getInstance(Class<T> clazz){
        //直接返回一个实例化对象
        try {
            return (T)new MessageProxy().bind(clazz.getDeclaredConstructor().newInstance());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
interface IMessage {
    void send(String message);
}
class MessageImpl implements IMessage {
    @Override
    public void send(String message) {
        System.out.println("【消息发送】"+message);
    }
}
class NetMessageImpl implements IMessage {
    @Override
    public void send(String message) {
        System.out.println("【网络消息发送】"+message);
    }
}
class MessageProxy implements InvocationHandler {
    private Object target;
    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    public  boolean connect(){
        System.out.println("【代理操作】进行消息发送通道的连接。");
        return true;
    }
    public void close() {
        System.out.println("【代理操作】关闭连接通道");
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if(connect()){
                return method.invoke(target, args);
            }
            throw new Exception("【ERROR】消息无法进行发送!");
        }finally {
            close();
        }
    }
}

  由于Annotation的存在,所以面向接口的编程处理将可以直接利用Annotation的属性完成控制,从而使得整体代码变得整洁。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容