×

如何实现自定义Java运行时注解功能

96
皮球二二
2016.06.01 13:07* 字数 622

注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

基础知识

相信大家在Android开发过程中,对注解有一定程度的接触,比如我们在使用EventBus的时候,我们声明

@Subscribe(threadMode = ThreadMode.MainThread)

来接受通过EventBus发出来的数据,
还有ButterKnife,我们在使用OnClick添加点击事件或Bind绑定组件的时候,同样也有类似的写法

@Bind(R.id.index_image)
@OnClick({R.id.index_layout)

以至于什么@Override、@Deprecated、@SuppressWarnings这就不需要说了。
我们在写的时候,最大的感触就是:太方便了!!!直接加一个声明就Ok了。你是不是心动了,自己也写几个注解,是不是程序能够更加简洁一些了?先别急,看看注解的结构

@Retention(CLASS) 
@Target(FIELD)
public @interface Bind {  
/** View ID to which the field will be bound. */  
    int[] value();
}

其实一点概念都不知道的话,确实看到这个会懵逼。我们一个个来解释一下

  1. Retention 表示在什么级别保留此信息
    SOURCE:源码注解,注解仅存在代码中,注解会被编译器丢弃
    CLASS:编译时注解,注解会在class文件中保留,但会被VM丢弃
    RUNTIME:运行时注解,VM运行期间也会保留该注解,因此可以通过反射来获得该注解
  2. Target 表示作用域,可能的ElementType参数包括:
    CONSTRUCTOR:构造方法声明
    FIELD:字段声明
    LOCAL_VARIABLE:局部变量声明
    METHOD:方法声明
    PACKAGE:包声明
    PARAMETER:参数声明
    TYPE:类,接口或enum声明
  3. Inherited 容许子类继承
  4. Documented 生成javadoc时会包含注解

实战1---获取注解值

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAnnotation {    
    String value() default "defaultValue";    
    int age();
}

我们声明了一个运行时的注解,可以作用在类还有方法上,包含2个成员变量,其中value有一个默认值是defaultValue
来看看如何获取

@MyAnnotation(age = 10)
public class AnnotationDemo {    
    @MyAnnotation(value = "Hello", age = 100)    
    public static void main(String[] args) {        
        try {            
            Class class_=Class.forName("com.renyu.demo.AnnotationDemo");            
            Annotation myAnnotation=class_.getAnnotation(MyAnnotation.class);            
            if (myAnnotation !=null && myAnnotation instanceof MyAnnotation) {          
                System.out.println(((MyAnnotation) myAnnotation).value()+((MyAnnotation) myAnnotation).age());            
            }            
            for (Method method : class_.getDeclaredMethods()) {                
                MyAnnotation methodAnnotation=method.getAnnotation(MyAnnotation.class);                
                if (methodAnnotation!=null) {           
                    System.out.println(methodAnnotation.value()+methodAnnotation.age());                
                }            
            }        
        } catch (ClassNotFoundException e) {            
            e.printStackTrace();        
        }    
    }
}

果断打印出期望值

defaultValue10
Hello100

实战2---教你写一个简单的类似ButterKnife工具

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface onClick {    
    int[] value();
}

这边如出一辙

下面是简单的调度类,负责实际意义上的点击事件处理

public class InjectorProcessor {    
    public void process(final Object object) {        
        Class class_=object.getClass();        
        Method[] methods=class_.getDeclaredMethods();        
        for (final Method method : methods) {            
            onClick clickMethod=method.getAnnotation(onClick.class);            
            if (clickMethod!=null) {                
                if (object instanceof Activity) {                   
                    for (int id : clickMethod.value()) {                        
                        View view=((Activity) object).findViewById(id);                        
                        view.setOnClickListener(new View.OnClickListener() {                            
                            @Override                            
                            public void onClick(View v) {                                
                                try {                                    
                                    method.invoke(object);                                
                                } catch (IllegalAccessException e) {                                    
                                    e.printStackTrace();                                
                                } catch (InvocationTargetException e) {                                    
                                    e.printStackTrace();                                
                                }                            
                            }                        
                        });                    
                    }                
                }            
             }        
         }    
    }
}

最后使用就很简单了

public class MainActivity extends AppCompatActivity {    
    @Override    
    protected void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);        
        setContentView(R.layout.activity_main);        
        InjectorProcessor processor=new InjectorProcessor();        
        processor.process(MainActivity.this);    
    }    

    @onClick({R.id.textview})    
    public void click() {        
        Toast.makeText(this, "HHH", Toast.LENGTH_SHORT).show();    
    }
}
Java学习
Web note ad 1