Java进阶之注解和反射及动态代理

前言

Android注解在之前都没用过,只知道最简单的 @override,最新心血来潮想学习下,找了一些资料,将学到的知识做下记录。

一、注解的含义

Java 注解(Annotation)又称 Java 标注,是 JDK1.5 引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。
著名的butterknife框架,在早期的时候,使用的应该是运行时注解,所以,在效率性能方面会有影响。但是后期,JakeWharton修改为编译期注解,这样对程序的运行就不存在性能问题了。

二、元注解:修饰注解的注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)

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

以上是@Override的注解源码,它本身是一个注解,但是它的定义上还有 @Target 和 @Retention 在修饰它, 像这两个用来 专门修饰注解的注解 就叫做元注解。
其中,@Target用于注解对象的作用范围,一对多的关系,可指明多个适用对象(value 是特殊的key值,调用时可直接设置对应的值,其他参数或有多个参数时需写上参数“名称 = 值")

@Target(ElementType.TYPE, ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface XTest {
- //   String name() default "";//默认值
    long value() default 1000;
- //   int id() ;//调用时候,需要写上参数“名称=值”
}

// 调用
- //@XTest(id = 1, name = "111", value = 100)
@XTest(100)
public class ReflectDemoActivity extends AppCompatActivity {
	......
}
参数作用范围
ElementType.TYPE类、接口、枚举、注解类型
ElementType.FIELD类成员属性(构造方法、方法、成员变量)
ElementType.METHOD方法
ElementType.PARAMETER参数
ElementType.CONSTRUCTOR方法构造参数
ElementType.LOCAL_VARIABLE局部变量
ElementType.ANNOTATION_TYPE注解
ElementType.PACKAGE
ElementType.TYPE_PARAMETER类型参数
ElementType.TYPE_USE类型使用说明

@Retention用于注解保留的生命周期,其中SOURCE < CLASS < RUNTIME(可以通过 ASM 插件查看对应状态)

参数作用周期
RetentionPolicy.SOURCE只在源码中有效,编译时抛弃,如@Override
RetentionPolicy.CLASS编译期注解, 编译时由编译器保留,但 Java 虚拟机(JVM)会忽略
RetentionPolicy.RUNTIME运行期注解,由 JVM 保留,因此运行时环境可以使用它

@IntDef 用于IDE语法检查(源码注解)
若出现需要对入参进行类型限定,如限定为资源ID、布局ID等类型参数,将参数类型直接给定int即可。
然而,我们可以利用Android为我们提供的语法检查注解,来辅助进行更为直接的参数类型检查与提示,如图片资源ID用@DrawableRes,亦可自定义语法检查注解。

package com.example.practicedemo.javademo.inject;

import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;

import com.example.practicedemo.R;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class ReflectDemo {
    // 耗内存
    enum WeekDay{
        SUNDAY, MONDAY
    }

    private static final int SUNDAY = 0;
    private static final int MONDAY = 1;

    @WekDay private static int mCurrentIntDay;
    private static WeekDay mCurrentDay;

    @IntDef({SUNDAY, MONDAY})
    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.SOURCE)
    @interface WekDay{}

    public static void main(String[] args) {
        //setDrawable(1);
        setDrawable(R.drawable.ic_launcher_background);
        setCurrentDay(WeekDay.SUNDAY);
       // setCurrentIntDay(1);
        setCurrentIntDay(SUNDAY);
    }

    public static void setCurrentDay(WeekDay currentDay) {
        mCurrentDay = currentDay;
    }

    public static void setCurrentIntDay(@WekDay int currentIntDay) {
        mCurrentIntDay = currentIntDay;
    }

    public static void setDrawable(@DrawableRes int id) {}
}

三、注解的应用场景

根据注解的保留级别不同,对注解的使用自然存在不同场景,以下是场景案例:

级别–》技术说明
源码—APT(+ IDE语法检查)在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。
字节码—字节码增强在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。
运行时—反射在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。

源码------注解处理器

  • main目录下创建一个Java库模块,命名为compiler
  • 此模块中创建一个类继承AbstractProcessor——MyProcessor
  • 注解处理器的注册流程: main目录下新建目录resources–》META-INF.services
    –》再创建文件javax.annotation.processing.Processor,文件中写入注解程序的全类名即可(com.example.compiler.MyProcessor)
package com.example.compiler;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

// 指定处理注解(可不指定)
@SupportedAnnotationTypes("com.example.practicedemo.javademo.inject.XTest")
public class MyProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE, "==============================");
        return false;
    }
}

四、自定义注解

4.1 自定义编写规则

  • 注解类型的声明,使用**@interface** 和元注解进行描述。
  • 注解的元素是以 接口方法 的形式体现的(但它真的是元素),方法声明不能包含任何参数或throws,只能用public或默认(default)访问权修饰。
  • 方法的返回类型必须是基本数据类型、String、Class、枚举、注解或这些类型的数组,方法可以有默认值(如果没有默认值,必须在属性中设置;Class默认值不能为null)
  • 如果自定义注解只有一个参数,参数的名字如果是自定义那么,在调用时候,需要写上参数“名称=值”
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface XTest {
    String name() default "";//默认值
    long value() default 1000;
    int id() ;//调用时候,需要写上参数“名称=值”
}
  • 如果参数有且只有一个,且参数名字是value,则,调用时候,可不写参数名称
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBindView {
   @IdRes int value() ;
}

//调用时:
@MyBindView(R.id.tv_test)
public TextView tv_test;

4.2 自定义运行时注解
自定义注解(4.1规则中最后一项) + 注解处理器(会用到java反射机制)------类似于傻瓜式ButterKnife。
注释处理器:

public class MyButterKnife {
    public static void bindView(Activity activity){
        Class<? extends Activity> clazz = activity.getClass();
        // getDeclaredFields------只能获得自己的成员
        // getFields------获得自己+父类的成员(不包括private)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println("名称: " + field.getName());
            // 判断此类是否被MyBindView注解申明
            if (field.isAnnotationPresent(MyBindView.class)) {
                MyBindView annotation = field.getAnnotation(MyBindView.class);
                if (annotation != null) {
                    try {
                    	field.setAccessible(true);//设置访问权限,允许操作private属性
                        field.set(activity, activity.findViewById(annotation.value()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }else {
            }
        }
    }
}

自定义注释调用:

@MyBindView(R.id.tv_test)
    public TextView tv_test;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo_reflect);
        MyButterKnife.bindView(this);
        Test();
    }

    private void Test() {
        tv_test.setText("自定义注释成功~~~~");
    }

4.3 通过自定义注解与反射实现页面跳转的参数注入
在这之前需要先梳理一下关于反射的相关知识,参考了反射知识博客

package com.example.practicedemo.javademo.inject.jumptask;

import android.content.Intent;
import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.example.practicedemo.R;
import com.example.practicedemo.javademo.inject.InjectUtils;

import java.util.ArrayList;

public class FirstActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);

        ArrayList<UserParcelable> userParcelableList = new ArrayList<>();

        userParcelableList.add(new UserParcelable("parceleable1"));
        Intent intent = new Intent(this, SecondActivity.class)
                .putExtra("name", "namevalue")
                .putExtra("attr","attr")
                .putExtra("array", new int[]{1, 2, 3, 4, 5, 6})
                .putExtra("userParcelable", new UserParcelable("userParcelable"))
                .putExtra("userParcelables", new UserParcelable[]{new UserParcelable("userParcelables")})
                .putExtra("users",new UserSerializable[]{new UserSerializable("sericlizable")})
                .putExtra("strs",new String[]{"1","2"})
                .putParcelableArrayListExtra("userParcelableList", userParcelableList);
        startActivity(intent);
    }
}

package com.example.practicedemo.javademo.inject.jumptask;

import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.example.practicedemo.javademo.inject.InjectUtils;

import java.util.Arrays;
import java.util.List;

public class SecondActivity extends AppCompatActivity {
    @Autowired
    String name;
    @Autowired("attr")
    String attr;
    @Autowired
    int[] array;
    @Autowired
    UserParcelable userParcelable;
    @Autowired
    UserParcelable[] userParcelables;
    @Autowired
    List<UserParcelable> userParcelableList;

    @Autowired("users")
    UserSerializable[] userSerializables;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.injectAutowired(this);
        System.out.println(toString());
    }

    @Override
    public String toString() {
        return "SecondActivity{" +
                "name='" + name + '\'' +
                ", attr='" + attr + '\'' +
                ", array=" + Arrays.toString(array) +
                ", userParcelable=" + userParcelable +
                ", userParcelables=" + Arrays.toString(userParcelables) +
                ", userParcelableList=" + userParcelableList +
                ", userSerializables=" + Arrays.toString(userSerializables) +
                '}';
    }
}
package com.example.practicedemo.javademo.inject;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.view.View;

import com.example.practicedemo.javademo.inject.jumptask.Autowired;
import com.example.practicedemo.javademo.inject.jumptask.InjectView;

import java.lang.reflect.Field;
import java.util.Arrays;

public class InjectUtils {
    public static void injectAutowired(Activity activity) {
        Class<? extends Activity> cls = activity.getClass();
        //获得数据
        Intent intent = activity.getIntent();
        Bundle extras = intent.getExtras();
        if (extras == null) {
            return;
        }

        //获得此类所有的成员
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field field : declaredFields) {
             // 判断属性是否被Autowired注解声明
            if (field.isAnnotationPresent(Autowired.class)) {
                Autowired autowired = field.getAnnotation(Autowired.class);
                //获得key
                String key = TextUtils.isEmpty(autowired.value()) ? field.getName() : autowired.value();

                if (extras.containsKey(key)) {
                    Object obj = extras.get(key);
                    // Parcelable数组类型不能直接设置,其他的都可以.
                    //获得数组单个元素类型
                    Class<?> componentType = field.getType().getComponentType();
                    //当前属性是数组并且是 Parcelable(子类)数组
                    if (field.getType().isArray() && Parcelable.class.isAssignableFrom(componentType)) {
                        Object[] objs = (Object[]) obj;
                        //创建对应类型的数组并由objs拷贝
                        Object[] objects = Arrays.copyOf(objs, objs.length, (Class<? extends Object[]>) field.getType());
                        obj = objects;
                    }

                    field.setAccessible(true);
                    try {
                        // 反射时第一个参数是在哪个对象上设置属性或调用方法
                        field.set(activity, obj);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
package com.example.practicedemo.javademo.inject.jumptask;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 此处注解作用是标识  作用在成员属性  作用在运行时--RUNTIME
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}

五、代理模式

三个对象:
抽象接口
代理对象------Agent
真实对象------Impl

5.1 静态代理

/**
 * 代理抽象角色: 定义了服务的接口
 */
public interface Teach {
    void teach();
}
public class Person1 implements Teach {
    @Override
    public void teach() {
        System.out.println("Person1 teach");
    }
}
public class Agent implements Teach {
    private final Teach teach;

    public Agent(Teach teach) {
        this.teach = teach;
    }

    //....前置处理
    public void before() {
        System.out.println("Agent before");
    }

    //....后置处理
    public void after() {
        System.out.println("Agent after");
    }
    @Override
    public void teach() {
        before();
        teach.teach();
        after();
    }
}
public class Impl {
    public static void main(String[] args) {
        //静态代理
        Person1 person1 = new Person1();
        Agent agent = new Agent(person1);
        agent.teach();
    }
}

每个代理类只能为一个接口服务,若Person新增功能则需再添加Agent,会使得程序中有很多代理类,因此就有了动态代理,一个代理类实现全部代理功能

5.2 动态代理

public interface Study {
    void study();
}

public class Person2 implements Teach, Study {
    @Override
    public void teach() {
        System.out.println("Person2 teach");
    }

    @Override
    public void study() {
        System.out.println("Person2 study");
    }
}
Person2 person2 = new Person2();
        // 动态代理
        // InvocationHandler的作用------回调
        Object o = Proxy.newProxyInstance(Impl.class.getClassLoader(),
                new Class[]{Teach.class, Study.class}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("invoke");
                        return method.invoke(person2, args);
                    }
                });

        Teach teach = (Teach)o;
        teach.teach();

六、Retrofit简易版实现——注解+反射+动态代理的结合

在这里插入图片描述

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
    String value();
}

@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
    String value() default "";
}

@Target(METHOD)
@Retention(RUNTIME)
public @interface POST {
    String value() default "";
}

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {
    String value();
}
public interface CustomWeatherApi {
    @POST("/v3/weather/weatherInfo")
    Call postWeather(@Field("city") String city, @Field("key") String key);


    @GET("/v3/weather/weatherInfo")
    Call getWeather(@Query("city") String city, @Query("key") String key);
}
package com.example.practicedemo.javademo.inject.customretrofit;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;

public class CustomRetrofit {
    final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
    final Call.Factory callFactory;
    final HttpUrl baseUrl;

    CustomRetrofit(Call.Factory callFactory, HttpUrl baseUrl) {
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
    }

    public <T> T create(final Class<T> service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //解析这个method 上所有的注解信息
                        ServiceMethod serviceMethod = loadServiceMethod(method);
                        //args:
                        return serviceMethod.invoke(args);
                    }
                });
    }

    // 请求多次从缓存中处理
    private ServiceMethod loadServiceMethod(Method method) {
        //先不上锁,避免synchronized的性能损失
        ServiceMethod result = serviceMethodCache.get(method);
        if (result != null) return result;
        //多线程下,避免重复解析,
        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                result = new ServiceMethod.Builder(this, method).build();
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }

    /**
     * 构建者模式,将一个复杂对象的构建和它的表示分离,可以使使用者不必知道内部组成的细节。
     */
    public static final class Builder {
        private HttpUrl baseUrl;
        //Okhttp->OkhttClient
        private okhttp3.Call.Factory callFactory;  //null


        public Builder callFactory(okhttp3.Call.Factory factory) {
            this.callFactory = factory;
            return this;
        }

        public Builder baseUrl(String baseUrl) {
            this.baseUrl = HttpUrl.get(baseUrl);
            return this;
        }

        public CustomRetrofit build() {
            if (baseUrl == null) {
                throw new IllegalStateException("Base URL required.");
            }
            okhttp3.Call.Factory callFactory = this.callFactory;
            if (callFactory == null) {
                callFactory = new OkHttpClient();
            }

            return new CustomRetrofit(callFactory, baseUrl);
        }
    }
}
// 解析方法中的所有注解信息:记录请求类型/参数/请求地址
public class ServiceMethod {
    private final Call.Factory callFactory;
    private final String relativeUrl;
    private final boolean hasBody;
    private final ParameterHandler[] parameterHandler;
    private FormBody.Builder formBuild;
    HttpUrl baseUrl;
    String httpMethod;
    HttpUrl.Builder urlBuilder;

    public ServiceMethod(Builder builder) {
        baseUrl = builder.customRetrofit.baseUrl;
        callFactory = builder.customRetrofit.callFactory;

        httpMethod = builder.httpMethod;
        relativeUrl = builder.relativeUrl;
        hasBody = builder.hasBody;
        parameterHandler = builder.parameterHandler;

        //如果是有请求体,创建一个okhttp的请求体对象
        if (hasBody) {
            formBuild = new FormBody.Builder();
        }
    }

    public Object invoke(Object[] args) {
        /**
         * 1  处理请求的地址与参数
         */
        for (int i = 0; i < parameterHandler.length; i++) {
            ParameterHandler handlers = parameterHandler[i];
            //handler内本来就记录了key,现在给到对应的value
            handlers.apply(this, args[i].toString());
        }

        //获取最终请求地址
        HttpUrl url;
        if (urlBuilder == null) {
            urlBuilder = baseUrl.newBuilder(relativeUrl);
        }
        url = urlBuilder.build();

        //请求体
        FormBody formBody = null;
        if (formBuild != null) {
            formBody = formBuild.build();
        }

        Request request = new Request.Builder().url(url).method(httpMethod, formBody).build();
        return callFactory.newCall(request);
    }

    // get请求,  把 k-v 拼到url里面
    public void addQueryParameter(String key, String value) {
        if (urlBuilder == null) {
            urlBuilder = baseUrl.newBuilder(relativeUrl);
        }
        urlBuilder.addQueryParameter(key, value);
    }

    //Post   把k-v 放到 请求体中
    public void addFiledParameter(String key, String value) {
        formBuild.add(key, value);
    }


    public static class Builder {
        private final CustomRetrofit customRetrofit;
        private final Annotation[] methodAnnotations;
        private final Annotation[][] parameterAnnotations;
        ParameterHandler[] parameterHandler;
        private String httpMethod;
        private String relativeUrl;
        private boolean hasBody;

        public Builder(CustomRetrofit customRetrofit, Method method) {
            this.customRetrofit = customRetrofit;
            //获取方法上的所有的注解
            methodAnnotations = method.getAnnotations();
            //获得方法参数的所有的注解 (一个参数可以有多个注解,一个方法又会有多个参数)
            parameterAnnotations = method.getParameterAnnotations();
        }

        public ServiceMethod build() {
            /**
             * 1 解析方法上的注解, 只处理POST与GET
             */
            for (Annotation methodAnnotation : methodAnnotations) {
                if (methodAnnotation instanceof POST) {
                    //记录当前请求方式
                    this.httpMethod = "POST";
                    //记录请求url的path
                    this.relativeUrl = ((POST) methodAnnotation).value();
                    // 是否有请求体
                    this.hasBody = true;
                } else if (methodAnnotation instanceof GET) {
                    this.httpMethod = "GET";
                    this.relativeUrl = ((GET) methodAnnotation).value();
                    this.hasBody = false;
                }
            }

            /**
             * 2 解析方法参数的注解
             */
            int length = parameterAnnotations.length;
            parameterHandler = new ParameterHandler[length];
            for (int i = 0; i < length; i++) {
                // 一个参数上的所有的注解
                Annotation[] annotations = parameterAnnotations[i];
                // 处理参数上的每一个注解
                for (Annotation annotation : annotations) {
                    //可以加一个判断:如果httpMethod是get请求,现在又解析到Filed注解,可以提示使用者使用Query注解
                    if (annotation instanceof Field) {
                        //得到注解上的value: 请求参数的key
                        String value = ((Field) annotation).value();
                        parameterHandler[i] = new ParameterHandler.FiledParameterHandler(value);
                    } else if (annotation instanceof Query) {
                        String value = ((Query) annotation).value();
                        parameterHandler[i] = new ParameterHandler.QueryParameterHandler(value);
                    }
                }
            }
            return new ServiceMethod(this);
        }
    }
}
// 记录请求参数的key
public abstract class ParameterHandler {

    abstract void apply(ServiceMethod serviceMethod, String value);

    static class QueryParameterHandler extends ParameterHandler {
        String key;

        public QueryParameterHandler(String key) {
            this.key = key;
        }

        //serviceMethod: 回调
        @Override
        void apply(ServiceMethod serviceMethod, String value) {
            serviceMethod.addQueryParameter(key,value);
        }
    }

    static class FiledParameterHandler extends ParameterHandler {
        String key;

        public FiledParameterHandler(String key) {
            this.key = key;
        }

        @Override
        void apply(ServiceMethod serviceMethod, String value) {
            serviceMethod.addFiledParameter(key,value);
        }
    }
}

七、实例:结合三者实现onClick的注入

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值