Java反射--反射与Annotation
内容学习于: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可以实现更加灵活的配置,同时利用其可以简化工厂设计模式的开发结构。