内容学习于:edu.aliyun.com


1. 利用反射获取Annotation

  Annotation是在JDK 1.5 之后追加的Java最为重要的新特性,而且从JDK 1.5开始由于Annotation的存在,使得整体的项目开发的形式发生了重大的改变。

  在一个类或者是一个方法上有可能会存在有大量的Annotation定义,如果现在要想获取这些结构上的Annotation就可以关注一个反射的提供类“AccessibleObject”,有如下支持Annotation处理方法。

  • 获取全部定义的Annotation方法:public Annotation[] getAnnotations()
  • 获取指定的Annotation对象:public T getDeclaredAnnotation(Class annotationClass)

  那么可以发现Class、Constructor、 Method、 Field 都可以使用以上的方法获取定义的Annotation信息,也就是说任何地方上定义的Annouafion都可以通过反射来获取。

  如下图所示:

获取Annotation:

@FunctionalInterface
@Deprecated(since = "1.1")
interface IMessage {
    public void send(String msg);
}

@Deprecated(since = "2.0")
class MessageImpl implements IMessage {

    @Override
    public void send(String msg) {

    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        {//获取接口上的Annotatiom
            Class<?> clazz = IMessage.class;
            Annotation annotation[] = clazz.getAnnotations();
            System.out.println(Arrays.toString(annotation));
        }
        {//获取send方法上的Annotation
            Class<?> clazz = MessageImpl.class;
            Method method = clazz.getDeclaredMethod("send", String.class);
            Annotation annotation[] = method.getAnnotations();
            System.out.println(Arrays.toString(annotation));

        }
    }
}

结果:

[@java.lang.FunctionalInterface(), @java.lang.Deprecated(forRemoval=false, since=“1.1”)]
[]
通过本程序的执行可以发现有些Annotation的定义是无法获取的,这个与Annotation自身的作用范围有关,来观察两个Annotation的定义。

@ Deprecated:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {}

@Override:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

  在以上系统内置的两个Annotation之中可以发现最大的不同是设置的“Rentention”Annotation 的策略不同,策略使用的是“RetentionPolicy"这个枚举类来进行的定义,该类有三种取值:

  • RetentionPolicy.SOURCE:该注解只允许出现在源代码之中;
  • RetentionPolicy.CLASS: 该注解可以保存在CLASS文件里面;
  • RetentionPolicy.RUNTIME:该注解在运行的时候有效。

  在日后你们定义的Annotation基本上都是在运行时有效的,所以基本上“RUNTIME"比较常用。

2. 自定义Annotation

  在项目的开发之中,很多的开发者可以利用Annotation结构进行代码的优化,所以就需要进行自定义Annotation实现,如果此时的Annotation 要想通过反射获取一定要将其定义在RUNTIME范围之中。

自定义Annotation:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Action{//定义自己的Annocation
    public String value();
    public String url() default "www.mldn.cn";
}

@Action("享受假期生活")//等同于@Action(value = "享受假期生活")
class Message{
    @Action(value = "Hello MLDN" ,url = "www.mldnjava.cn")
    public void send(){};
}


public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        {//获取类上的Annotatiom
            Action action = Message.class.getAnnotation(Action.class);
            System.out.println("【Message类上的Annocation数据】value = "+ action.value() + "、url = "+action.url());
        }
        {//获取send方法上的Annotation
            Action action = Message.class.getDeclaredMethod("send").getAnnotation(Action.class);
            System.out.println("【Message类上的Annocation数据】value = "+ action.value() + "、url = "+action.url());

        }
    }
}

  在进行Annotation定义的时候,所以在Annotation中出现的变量的定义都必须在定义Annotation的时候明确的进行内容的设置,如果你的变量名称设置为了value,则在使用此Annotation的时候允许你省略变量名称,即:

@Target({ElementType.TYPE,ElementType.METHOD})
@interface Action{//定义自己的Annocation
    public String value();
}

@Action("享受假期生活")//等同于@Action(value = "享受假期生活")
class Message{}

  之所以现在可以获取Annotation主要原因是因为其是为“ RUNTIME"方式定义的Annotation。

3. Annotation与工厂设计模式

  Annotation最大的特点是基于注解进行的配置处理操作,所以下面可以将Annotation 与工厂设计模式进行一个有效的整合处理,例如,现在假设要进行消息的发送,那么消息的发送有可能是向数据库发送,也有可能是向云服务器进行发送。
  如下图所示:

  如果使用工厂类,则需要追加新的工厂类出现,这样会导致代码的开发复杂度攀升,所以现在希望可以进行开发的操作省略,于是现在基于Annotation简化代码。

利用Annotation简化工厂设计:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface ChannelAnnotation{//定义自己的Annotation
    public String value();
}

interface IConnect{
    public boolean build();
}

class DatabaseConnect implements IConnect{
    @Override
    public boolean build() {
        System.out.println("【Database】进行数据库连接....");
        return true;
    }
}

class CloudConnect implements IConnect{
    @Override
    public boolean build() {
        System.out.println("【CloudConnect】进行数据库连接....");
        return true;
    }
}

@ChannelAnnotation("com.xzzz.demo.")
class Message{
    private IConnect connect;
    public Message(){
        //1.获取本类定义上的Annocation对象信息
        ChannelAnnotation annotation = this.getClass().getAnnotation(ChannelAnnotation.class);
        //2.通过Annotation获取类的名称,利用反射加载此类实例
        try {
            this.connect = (IConnect) Class.forName(annotation.value() + "CloudConnect").getDeclaredConstructor().newInstance();
        } catch (Exception e) { }
    }

    public void send(String msg){
        if (this.connect.build()){
            System.out.println("【消息发送】" + msg);
        }
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
      Message message = new Message();
      message.send("www.mldn.cn");
    }
}

结果:

【CloudConnect】进行数据库连接…
【消息发送】www.mldn.cn

  Annotation可以实现更加灵活的配置,同时利用其可以简化工厂设计模式的开发结构。