java注解整理

作者: 的一幕 | 来源:发表于2019-08-12 19:19 被阅读12次

说到java注解,相信大家在平时写代码的时候见过注解,但是真的很少有人用注解写过什么功能,包括我也是,所以今天就带着大家一起学习java中注解:
下面通过一个简单的注解介绍它有那些组成部分:

image.png
Override注解发现,其中有@Target注解和@Retention注解修饰,其中@Target表示当前的注解是修饰在哪的,也就是作用域,@Retention表示当前的注解发生在什么时期,ElementType.METHOD表示修饰在方法上面,RetentionPolicy.SOURCE表示该注解发生在代码编写阶段。下面看看ElementTypeRetentionPolicy还有其他的那些修饰:
public enum RetentionPolicy {
    
     //该修饰表示注解只是在代码编写阶段起作用
    SOURCE,

    //在编译期起作用
    CLASS,

    //在运行期间起作用
    RUNTIME
}

SOURCE:表示该注解在代码编写阶段起作用,如果在代码编写期间有代码规范问题或错误就会直接提示
CLASS:表示该注解在编译期起作用,编译期会帮我们生成代码,有名的ButterKnife框架就是通过在编译期生成对应的类名_ViewBinding,大家可以看看apt生成的一些类。
RUNTIME:表示该注解在运行期起作用,也就是JVM的运行期。这个和上一节讲到的反射都是在运行期使用的。

public enum ElementType {
    //一般作用在枚举、类、接口上
    TYPE,

    //作用在属性上,BindView注解就是形式这种
    FIELD,

    //作用在方法上
    METHOD,

    //作用在方法参数上
    PARAMETER,

    //作用在构造器上
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    //作用在注解上面,比如上面的@Retention、@Target都是修饰在注解上的
    ANNOTATION_TYPE,

    //作用在包上
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

常用的也就只有下面这几种:
TYPE 作用对象类/接口/枚举
FIELD 成员变量
METHOD 成员方法
PARAMETER 方法参数
ANNOTATION_TYPE 注解的注解

修饰变量注解@Target(ElementType.FIELD)

下面通过一个例子,将注解和反射一起结合使用,该注解实现的一个功能就是通过该注解能实现view点击后跳转到下一个activity的动作:
定义注解

//定义该注解作用在什么时期,此处定义为jvm运行时期
@Retention(RetentionPolicy.RUNTIME)
//定义该注解作用在变量上
@Target(ElementType.FIELD)
public @interface IntentAnnotation {
    //定义注解要传入的值,后面因为要用到该值,因此提前申明出来
    Class<?> value();
}

为了完成activity之间的跳转,申明了一个A_Activity

public class A_Activity extends AppCompatActivity {
    //将注解作用在button上面,传入我们想要跳转的activity
    @IntentAnnotation(B_Activity.class)
    private Button button;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        button = findViewById(R.id.button);
        AnnotationUtils.inject(this);
    }
}

此处还定义了一个AnnotationUtils的工具类,其实主要作用在它身上,咱们看看如何去写:

public class AnnotationUtils {
    private static final String TAG = "AnnotationUtils";

    public static void inject(final Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        //获取class文件所有的属性集合
        Field[] fields = aClass.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            //判断当前属性是不是有想要的注解
            if (field.isAnnotationPresent(IntentAnnotation.class)) {
                final IntentAnnotation annotation = field.getAnnotation(IntentAnnotation.class);
                Log.d(TAG, "field.getGenericType().toString():" + field.getGenericType().toString());
                //判断下属性的类型
                if (field.getGenericType().toString().startsWith(
                        "class android.widget.")) {
                    try {
                        //此处就是要干的事情
                        View view = (View) field.get(activity);
                        view.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                Intent intent = new Intent(activity, annotation.value());
                                activity.startActivity(intent);
                            }
                        });
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                } else {
                    throw new RuntimeException("the field must be a view");
                }
            }

        }
    }
}

所以说会使用注解,咱们自己也就可以封装很多不用重复写的代码,通过一行注解就能搞定。

修饰方法注解@Target(ElementType. METHOD)

下面通过一个简单的类似Eventbus的代码:

提供给方法的注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface onEvent {

}

package com.single.layoutinflaterdemo.bus;

import android.os.Looper;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

public class EventBus {
    static volatile EventBus sInstance;
    Finder mFinder;
    //CopyOnWriteArrayList一般用在读和写同时存在的情况下使用
    //键存的是要提供注解的类的class对象,值存的要被调用的方法,类信息
    public Map<Class<?>, CopyOnWriteArrayList<Subscriber>> mSubscriberMap;
    //通过handler发送消息
    PostHandler mPostHandler;

    private EventBus() {
        mFinder = new NameBasedFinder();
        mSubscriberMap = new HashMap<>();
        mPostHandler = new PostHandler(Looper.getMainLooper(), this);
    }

    /**
     * 得到一个单例的上下文对象
     *
     * @return
     */
    public static EventBus getDefault() {
        if (sInstance == null) {
            synchronized (EventBus.class) {
                if (sInstance == null) {
                    sInstance = new EventBus();
                }
            }
        }
        return sInstance;
    }

    public void register(Object subscriber) {
        /**
         * 该处是关键,找到该类下面有onEvent注解的方法
         */
        List<Method> methods = mFinder.findSubscriber(subscriber.getClass());
        if (methods == null || methods.size() < 1) {
            return;
        }
        CopyOnWriteArrayList<Subscriber> subscribers = mSubscriberMap.get(subscriber.getClass());
        if (subscribers == null) {
            subscribers = new CopyOnWriteArrayList<>();
            /**
             * 该map用来存储onevent开头的方法的第一个参数为key和
             * subscribers的集合
             */
            mSubscriberMap.put(subscriber.getClass(), subscribers);
        }
        for (Method method : methods) {
            /**
             * 封装了该类和该类带有onevent的方法
             */
            Subscriber newSubscriber = new Subscriber(subscriber, method);
            subscribers.add(newSubscriber);
        }
    }

    public void unregister(Object subscriber) {
        CopyOnWriteArrayList<Subscriber> subscribers = mSubscriberMap.remove(subscriber.getClass());
        if (subscribers != null) {
            for (Subscriber s : subscribers) {
                s.mMethod = null;
                s.mSubscriber = null;
            }
        }
    }

    public void post(Object event) {
        mPostHandler.enqueue(event);
    }
}

可以看到上面有List<Method> methods = mFinder.findSubscriber(subscriber.getClass());,此处很关键,找onEvent注解的方法:

public class NameBasedFinder implements Finder {
    /**
     * @param subscriber
     * @return
     */
    @Override
    public List<Method> findSubscriber(Class<?> subscriber) {
        List<Method> methods = new ArrayList<>();
        //先找到类下面的所有方法
        for (Method method : subscriber.getDeclaredMethods()) {
            method.setAccessible(true);
            //这个就是找方法的注解关键
            if (method.isAnnotationPresent(onEvent.class)) {
                methods.add(method);
            }
        }
        return methods;
    }
}

在post时候,将要被加注解的类放到handler中:

public void post(Object event) {
    mPostHandler.enqueue(event);
}

接着到PostHandler如何去处理加注解的方法:

public class PostHandler extends Handler {
    final EventBus mEventBus;

    public PostHandler(Looper looper, EventBus EventBus) {
        super(looper);
        mEventBus = EventBus;
    }

    @Override
    public void handleMessage(Message msg) {
        CopyOnWriteArrayList<Subscriber> subscribers = mEventBus.mSubscriberMap.get(msg.obj.getClass());
        for (Subscriber subscriber : subscribers) {
            subscriber.mMethod.setAccessible(true);
            try {
                /**
                 * 第二个参数是方法的参数,第-个参数是该类的对象                 
                 */
                subscriber.mMethod.invoke(subscriber.mSubscriber);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void enqueue(Object event) {
        Message message = obtainMessage();
        message.obj = event;
        sendMessage(message);
    }

}

其实说到底还是通过反射去调用该方法,由于我这里没写方法的参数,所以invoke的时候是没带参数值的,大家可以根据eventBus源码自己动手写写。

关于更多的注解使用,还是希望大家多练,多看相关的源码才能熟练。更多注解学习请看这里

相关文章

网友评论

    本文标题:java注解整理

    本文链接:https://www.haomeiwen.com/subject/fithjctx.html