Android进阶之路Android开发Android开发经验谈

Android coder 需要理解的注解、反射和动态代理

2020-08-08  本文已影响0人  木木玩Android

注解我们经常使用它,很多框架也提供了很多注解给我们使用,如 ARouter@Route(path = "/test/activity")butterknife@BindView(R.id.user) EditText username; 等,但是,你有没有自定义过注解,写过自己的注解处理器呢?反射听起来很高大上,但是实际上你真的了解他之后,只是一些API的调用而已;动态代理其实只是在静态代理(代理模式)基础上使用了反射技术;本篇文章将带领大家对注解、反射及动态代理有更清晰的认知。


本篇文章的示例代码放在 Github 上,所有知识点,如图:

注解

注解(Annotations),元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

注解有多种用途,例如:

注解的格式

注解的格式如下:

@Persileeclass MyClass { ... }复制代码

注解以 @ 开头后面跟上内容,注解可以包含元素,例如:

@Persilee(id=666, value = "lsy")class MyClass { ... }复制代码

如果,只有一个 value 元素,则可以省略该名称,如果,没有元素,则可以省略括号,例如

@Persilee("lsy") // 只有一个 value 元素class MyClass { ... }@Persilee // 没有元素class MyClass { ... }复制代码

如果,注解有相同的类型,则是重复注解,如

@Persilee("lsy")@Persilee("zimu")class MyClass { ... }复制代码

注解声明

注解的定义类似于接口的定义,在关键字 interface 前加上 @,如:

@interface Persilee {    int id();    String value();}复制代码

注解类型

int id()String value() 是注解类型(annotation type),它们也可以定义可选的默认值,如:

@interface Persilee {    int id();    String value() default "lsy";}复制代码

在使用注解时,如果定义的注解的注解类型没有默认值,则必须进行赋值,如:

@Persilee(id = 666) // id 必须要赋值,如,@Persilee 会提示 id 必须赋值class MyClass { ... }复制代码

元注解

在注解上面的注解称为元注解(meta-annotations),如

@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.SOURCE)@interface Persilee {    int id();    String value() default "lsy";}复制代码

java.lang.annotation 中定义了几种元注解类型(常使用的是 @Retention、@Target),如

@Retention 指定注解的存储方式,我们由 RetentionPolicy.java (是一个枚举)可知,如:

public enum RetentionPolicy {    SOURCE, // 标记的注解仅保留在源级别中,并被编译器忽略。    CLASS, // 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。    RUNTIME // 标记的注解由 JVM 保留,因此运行时环境可以使用它。}复制代码

@Target 指定注解可以使用的范围,我们由 ElementType.java (是一个枚举)可知使用范围,如下:

public enum ElementType {    TYPE, // 类    FIELD, // 字段或属性    METHOD, // 方法    PARAMETER, // 参数    CONSTRUCTOR, // 构造方法    LOCAL_VARIABLE, // 局部变量    ANNOTATION_TYPE, // 也可以使用在注解上    PACKAGE, // 包    TYPE_PARAMETER, // 类型参数    TYPE_USE // 任何类型}复制代码

对于 TYPE_PARAMETER (类型参数) 、 TYPE_USE (任何类型名称) 可能不是很好理解,如果把 Target 设置成 @Target({ElementType.TYPE_PARAMETER}),表示可以使用在泛型(上篇文章有介绍过泛型)的类型参数上,如:

public class TypeParameterClass<@Persilee T> {    public <@Persilee T> T foo(T t) {        return null;    }}复制代码

如果把 Target 设置成 @Target({ElementType.TYPE_USE}),表示可以使用在任何类型上,如:

TypeParameterClass<@Persilee String> typeParameterClass = new TypeParameterClass<>();@Persilee String text = (@Persilee String)new Object();复制代码

@Documented 注解表示使用了指定的注解,将使用 Javadoc 工具记录这些元素。

@Inherited 注解表示注解类型可以从超类继承。

@Repeatable 注解表明标记的注解可以多次应用于同一声明或类型使用。

注解应用场景

根据 @Retention 元注解定义的存储方式,注解一般可以使用在以下3种场景中,如:

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

小案例(使用注解实现语法检查)

我们定义一个 weekDay 字段,类型是 WeekDay 枚举类型,方便我们设置枚举中指定的值,如:

class WeekDayDemo {    private static WeekDay weekDay;    enum WeekDay {        SATURDAY,SUNDAY    }    public static WeekDay getWeekDay() {        return weekDay;    }    public static void setWeekDay(WeekDay weekDay) {        WeekDayDemo.weekDay = weekDay;    }    public static void main(String[] args) {        setWeekDay(WeekDay.SATURDAY);        System.out.println(getWeekDay());    }}复制代码

众所周知,在 Java 中枚举的实质是特殊的静态成员变量,在运行时候,所有的枚举会作为单例加载到内存中,非常消耗内存,那么,有没有什么优化的方案呢,在此,我们使用注解来取代枚举。

我们使用常量和 @intDef (语法检查)元注解去代替枚举,如:

class IntdefDemo {    private static final int SATURDAY = 0;    private static final int SUNDAY = 1;    private static int weekDay;    @IntDef({SATURDAY, SUNDAY})    @Target({ElementType.FIELD, ElementType.PARAMETER})    @Retention(RetentionPolicy.SOURCE)    @interface WeekDay { //自定义一个 WeekDay 注解    }    public static void setWeekDay(@WeekDay int weekDay) { // 使用 WeekDay 注解限制参数类型        IntdefDemo.weekDay = weekDay;    }    public static void main(String[] args) {        setWeekDay(SATURDAY); // 只能 传入 SATURDAY, SUNDAY    }}复制代码

APT注解处理器

APT(Annotation Processor Tools) 注解处理器,用于处理注解,编写好的 Java 文件,需要经过 Javac 的编译,编译为虚拟机能够加载的字节码(Class)文件,注解处理器是 Javac 自带的一个工具,用来在编译时期处理注解信息。

上文中我们已自定义好了 @Persilee 注解,下面我们来编写一个简单的注解处理器来处理 @Persilee 注解,我们可以新建一个 Java 的 Module,创建一个 PersileeProcessor 的类,如:

@SupportedAnnotationTypes("net.lishaoy.anreprdemo.Persilee")  //指定要处理的注解public class PersileeProcessor extends AbstractProcessor {    @Override    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {        Messager messager = processingEnv.getMessager(); //        messager.printMessage(Diagnostic.Kind.NOTE, "APT working ...");        for (TypeElement typeElement: set) {            messager.printMessage(Diagnostic.Kind.NOTE,"===>" + typeElement.getQualifiedName());            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(typeElement);            for (Element element: elements) {                messager.printMessage(Diagnostic.Kind.NOTE,"===>" + element.getSimpleName());            }        }        return false;    }}复制代码

然后,在 main 目录下新建 resources 目录,如图:

这个目录结构是规定死的,必须这样写,然后在 javax.annotation.processing.Processor 文件里注册需要处理的注解处理器,如

net.lishaoy.aptlib.PersileeProcessor复制代码

最后,在 appbuild.gradle 文件引入模块,如

dependencies {  ...  annotationProcessor project(':aptlib')}复制代码

在你 Build 工程时候,会在 Task :app:compileDebugJavaWithJavac 任务打印我们在注解处理程序的日志信息,如:

注: APT working ...注: ===>net.lishaoy.anreprdemo.Persilee注: ===>MainActivity复制代码

因为,我们只在 MainActivity 中使用了 @Persilee 注解,如下:

@Persilee(id = 666, value = "lsy")public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }}复制代码

反射

一般情况下,我们使用某个类时必定知道它是什么类,用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

Cook cook = new Cook(); // 实例化一个对象,标准用法cook.cookService("🍅");复制代码

反射是一开始并不知道初始化的类对象是什么,也不能使用 new 关键字来创建对象,反射是在运行的时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,调用对应的方法。

Java 反射机制主要提供了以下功能:

Class类

Class是一个类,封装了当前对象所对应的类的信息,我们写的每一个类都可以看成一个对象,是 java.lang.Class 类的对象,Class是用来描述类的类。

获得Class对象

Class对象的获取有3种方式,如下:

Cook cook = new Cook();Class cookClass = Cook.class;Class cookClass1 = cook.getClass();Class cookClass2 = Class.forName("net.lishaoy.reflectdemo.Cook");复制代码

创建实例

我们可以通过反射来生成对象的实例,如:

Class cookClass = Cook.class;Cook cook1 = (Cook) cookClass.newInstance();复制代码

获取构造器

获取构造器的方法有,如下:

我们来新建一个 Person ,以便我们的演示,如:

public class Person {    public String name;    private int age;    public Person(String name, int age) {        this.name = name;        this.age = age;    }    public Person() {        super();    }    public String getName() {        System.out.println("get name: " + name);        return name;    }    public void setName(String name) {        this.name = name;        System.out.println("set name: " + this.name);    }    public int getAge() {        System.out.println("get age: " + age);        return age;    }    public void setAge(int age) {        this.age = age;        System.out.println("set age: " + this.age);    }    private void privateMethod(){        System.out.println("the private method!");    }}复制代码

很常规的一个类,里面有私有的属性和方法。

下面,我们新建一个 GetConstructor 的类来演示,获取构造器方法如何使用,如:

class GetConstructor {    public static void main(String[] args) throws            ClassNotFoundException,            NoSuchMethodException,            IllegalAccessException,            InvocationTargetException,            InstantiationException {        String className = "net.lishaoy.reflectdemo.entity.Person";        Class<Person> personClass = (Class<Person>) Class.forName(className);        //获取全部的constructor对象        Constructor<?>[] constructors = personClass.getConstructors();        for (Constructor<?> constructor: constructors) {            System.out.println("获取全部的constructor对象: " + constructor);        }        //获取某一个constructor对象        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);        System.out.println("获取某一个constructor对象: " + constructor);        //调用构造器的 newInstance() 方法创建对象        Person person = constructor.newInstance("lsy", 66);        System.out.println(person.getName() + ", " + person.getAge() );    }}复制代码

输出结果,如下:

获取全部的constructor对象: public net.lishaoy.reflectdemo.entity.Person(java.lang.String,int)获取全部的constructor对象: public net.lishaoy.reflectdemo.entity.Person()获取某一个constructor对象: public net.lishaoy.reflectdemo.entity.Person(java.lang.String,int)lsy, 66复制代码

获取方法

获取方法的方法有,如下:

我们新创建一个 GetMethod 来演示如何来获取和调用方法,如:

class GetMethod {    public static void main(String[] args) throws            ClassNotFoundException,            NoSuchMethodException,            IllegalAccessException,            InstantiationException,            InvocationTargetException {        Class<?> aClass = Class.forName("net.lishaoy.reflectdemo.entity.Person");        //获取所有的public方法(包含从父类继承的方法)        Method[] methods = aClass.getMethods();        for (Method method: methods) {            System.out.println("获取所有public方法: " + method.getName() + "()");        }        System.out.println("===========================");        //获取所有方法(不包含父类方法)        methods = aClass.getDeclaredMethods();        for (Method method: methods) {            System.out.println("获取所有方法: " + method.getName() + "()");        }        System.out.println("===========================");        //获取指定的方法        Method method = aClass.getDeclaredMethod("setAge", int.class);        System.out.println("获取指定的方法:" + method);        //调用方法        Object instance = aClass.newInstance();        method.invoke(instance, 66);        //调用私有方法        method = aClass.getDeclaredMethod("privateMethod");        method.setAccessible(true); // 需要调用此方法且设置为 true        method.invoke(instance);    }}复制代码

运行结果,如下:

获取所有public方法: getName()获取所有public方法: setName()获取所有public方法: setAge()获取所有public方法: getAge()获取所有public方法: wait()获取所有public方法: wait()获取所有public方法: wait()获取所有public方法: equals()获取所有public方法: toString()获取所有public方法: hashCode()获取所有public方法: getClass()获取所有public方法: notify()获取所有public方法: notifyAll()===========================获取所有方法: getName()获取所有方法: setName()获取所有方法: setAge()获取所有方法: privateMethod()获取所有方法: getAge()===========================获取指定的方法:public void net.lishaoy.reflectdemo.entity.Person.setAge(int)set age: 66the private method!BUILD SUCCESSFUL in 395ms复制代码

获取成员变量

获取成员变量的方法有,如下:

我们再来新建一个 GetField 的类来演示如何获取成员变量,如下:

class GetField {    public static void main(String[] args) throws            ClassNotFoundException,            NoSuchFieldException,            IllegalAccessException,            InstantiationException {        Class<?> aClass = Class.forName("net.lishaoy.reflectdemo.entity.Person");        // 获取所有字段(不包含父类字段)        Field[] fields = aClass.getDeclaredFields();        for (Field field: fields) {            System.out.println("获取所有字段: " + field.getName());        }        System.out.println("================");        // 获取指定字段        Field name = aClass.getDeclaredField("name");        System.out.println("获取指定字段: " + name.getName());        // 设置指定字段的值        Object instance = aClass.newInstance();        name.set(instance, "per");        // 获取指定字段的值        Object o = name.get(instance);        System.out.println("获取指定字段的值: " + o);        // 设置和获取私有字段的值        Field age = aClass.getDeclaredField("age");        age.setAccessible(true); // 需要调用此方法且设置为 true        age.set(instance, 66);        System.out.println("获取私有字段的值: " + age.get(instance));    }}复制代码

运行结果,如下:

获取所有字段: name获取所有字段: age================获取指定字段: name获取指定字段的值: per获取私有字段的值: 66BUILD SUCCESSFUL in 395ms复制代码

使用注解和反射实现自动findViewById(案例)

我们已经对注解和反射有了更清晰的认知,下面我们通过一个小案例来巩固我们的学习:使用注解和反射完成类似 butterknife 的自动 findViewById 的功能。

新建一个空的 Android 工程,在工程目录下新建 inject 目录,在此目录下新建一个 InjectView 的类和 BindView 的自定义注解,如:

创建InjectView

InjectView 类通过反射完成 findViewById 功能:

public class InjectView {    public static void init(Activity activity) {        // 获取 activity 的 class 对象        Class<? extends Activity> aClass = activity.getClass();        // 获取 activity 的所以成员变量        Field[] declaredFields = aClass.getDeclaredFields();        // 变量所以成员变量        for (Field field: declaredFields) {            // 判断属性是否加上了 @BindView 注解            if(field.isAnnotationPresent(BindView.class)){                // 获取注解 BindView 对象                BindView bindView = field.getAnnotation(BindView.class);                // 获取注解类型元素 id                int id = bindView.value();                // 通过资源 id 找到对应的 view                View view = activity.findViewById(id);                // 设置可以访问私有字段                field.setAccessible(true);                try {                    // 给字段赋值                    field.set(activity,view);                } catch (IllegalAccessException e) {                    e.printStackTrace();                }            }        }    }}复制代码

创建@BindView注解

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface BindView {    @IdRes int value(); // @IdRes 只能传 id 资源}复制代码

使用@BindView注解

MainActivity 里使用 @BindView 注解,如:

public class MainActivity extends AppCompatActivity {    // 使用注解    @BindView(R.id.text_view)    private TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // 初始化 InjectView,完成自动 findViewById 功能        InjectView.init(this);        // 测试 R.id.text_view 是否自动赋值给 textView        textView.setText("通过 @BindView 注解自动完成 findViewById");    }}复制代码

运行结果,如图:

是不是很简单,一个类就完成了自动 findViewById 的功能。

动态代理

在了解动态代理之前,我们先来回顾下静态代理。

静态代理

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,如,我们生活中常见的中介。

代理模式一般会有3个角色,如图:

为什么要使用代理模式

静态代理案例

场景如下:

小明可以在某网站上购买国内的东西,但是,不能买海外的东西,于是,他找了海外代购帮他买东西。

如何用代码描述呢?根据代理模式的3个角色,我们分别定义1个接口2个类,如:OrderService 接口(抽象角色)、ImplJapanOrderService 类(真实角色)、ProxyJapanOrder 类(代理角色)

OrderService 接口(抽象角色),代码如下:

public interface OrderService {    int saveOrder();}复制代码

ImplJapanOrderService 类(真实角色),代码如下:

// 实现抽象角色接口public class ImplJapanOrderService implements OrderService {    @Override    public int saveOrder() {        System.out.println("下单成功,订单号为:888888");        return 888888;    }}复制代码

ProxyJapanOrder 类(代理角色),代码如下:

// 实现抽象角色接口public class ProxyJapanOrder implements OrderService {    private OrderService orderService; // 持有真实角色    public OrderService getOrderService() {        return orderService;    }    public void setOrderService(OrderService orderService) {        this.orderService = orderService;    }    @Override    public int saveOrder() {        System.out.print("日本代购订单,");        return orderService.saveOrder(); // 调用真实角色的行为方法    }}复制代码

在创建一个 Client 类来测试我们的代码,如下:

public class Client {    public static void main(String[] args) {        // 日本代购订单        OrderService orderJapan = new ImplJapanOrderService();        ProxyJapanOrder proxyJapanOrder = new ProxyJapanOrder();        proxyJapanOrder.setOrderService(orderJapan);        proxyJapanOrder.saveOrder();    }}复制代码

运行结果,如下:

日本代购订单,下单成功,订单号为:888888BUILD SUCCESSFUL in 1s复制代码

如果,需要购买韩国的东西,需要新增一个 ImplKoreaOrderService 类(韩国服务商) 和 ProxyKoreaOrder 类(韩国代理),如还需要购买其他国家的东西,需要新增不同的类,则会出现静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,如是,我们需要使用动态代理。

动态代理

动态代理是在运行时才创建代理类和其实例,因此,我们可以传不同的真实角色,实现一个代理类完成多个真实角色的行为方法,当然,其效率比静态代理低。那么如何实现动态代理呢,JDK已为我们提供了 Proxy 类 和 InvocationHandler 接口来完成这件事情。

我们来创建一个 ProxyDynamicOrder 类(动态代理类),代码如下:

public class ProxyDynamicOrder implements InvocationHandler {    private Object orderService; // 持有真实角色    public Object getOrderService() {        return orderService;    }    public void setOrderService(Object orderService) {        this.orderService = orderService;    }    // 通过 Proxy 动态创建真实角色    public Object getProxyInstance(){        return Proxy.newProxyInstance(                orderService.getClass().getClassLoader(),                orderService.getClass().getInterfaces(),                this                );    }    @Override    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {        return method.invoke(orderService, objects); // 通过反射执行真实角色的行为方法    }}复制代码

在来看看,Client 类里如何调用,代码如下:

public class Client {    public static void main(String[] args) {        // 静态代理模式        // 国内订单        OrderService order = new ImplOrderService();        order.saveOrder();        // 日本代购订单        OrderService orderJapan = new ImplJapanOrderService();        ProxyJapanOrder proxyJapanOrder = new ProxyJapanOrder();        proxyJapanOrder.setOrderService(orderJapan);        proxyJapanOrder.saveOrder();        // 韩国代购订单        OrderService orderKorea = new ImplKoreaOrderService();        ProxyKoreaOrder proxyKoreaOrder = new ProxyKoreaOrder();        proxyKoreaOrder.setOrderService(orderKorea);        proxyKoreaOrder.saveOrder();        // 动态代理模式        // 国内订单        ProxyDynamicOrder proxyDynamicOrder = new ProxyDynamicOrder();        OrderService orderService = new ImplOrderService();        proxyDynamicOrder.setOrderService(orderService);        OrderService orderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();        orderService1.saveOrder();        // 日本代购订单        OrderService japanOrderService = new ImplJapanOrderService();        proxyDynamicOrder.setOrderService(japanOrderService);        OrderService japanOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();        japanOrderService1.saveOrder();        // 韩国代购订单        OrderService koreaOrderService = new ImplKoreaOrderService();        proxyDynamicOrder.setOrderService(koreaOrderService);        OrderService koreaOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();        koreaOrderService1.saveOrder();        // 生成动态代理生成的class文件        //ProxyUtil.generateClassFile(koreaOrderService.getClass(), koreaOrderService1.getClass().getSimpleName());    }}复制代码

运行结果,如下:

下单成功,订单号为:666666日本代购订单,下单成功,订单号为:888888韩国代购订单,下单成功,订单号为:666888下单成功,订单号为:666666下单成功,订单号为:888888下单成功,订单号为:666888BUILD SUCCESSFUL in 1s复制代码

只需要一个 ProxyDynamicOrder 代理类即可完成 ImplOrderServiceImplJapanOrderServiceImplKoreaOrderService 真实角色提供的服务。

动态代理原理

我们在 proxyDynamicOrder.getProxyInstance() 代码上打个断点,通过调试模式发现,如图:

代理类的名字是 $Proxy0@507,为什么是这个名字,我们在编译后的目录里也找不到 $Proxy0@507 类文件,如图:

我们通过查看 Proxy.newProxyInstance 方法源码,可知,如:

@CallerSensitivepublic static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException {    Objects.requireNonNull(var2);    Class[] var3 = (Class[])var1.clone();    SecurityManager var4 = System.getSecurityManager();    if (var4 != null) {        checkProxyAccess(Reflection.getCallerClass(), var0, var3);    }    // 获取代理类的 class 对象    Class var5 = getProxyClass0(var0, var3);    try {        if (var4 != null) {            checkNewProxyPermission(Reflection.getCallerClass(), var5);        }        // 获取代理类的构造器        final Constructor var6 = var5.getConstructor(constructorParams);        if (!Modifier.isPublic(var5.getModifiers())) {            AccessController.doPrivileged(new PrivilegedAction<Void>() {                public Void run() {                    var6.setAccessible(true);                    return null;                }            });        }        // 创建代理类的示例        return var6.newInstance(var2);    } catch (InstantiationException | IllegalAccessException var8) {        throw new InternalError(var8.toString(), var8);    } catch (InvocationTargetException var9) {        Throwable var7 = var9.getCause();        if (var7 instanceof RuntimeException) {            throw (RuntimeException)var7;        } else {            throw new InternalError(var7.toString(), var7);        }    } catch (NoSuchMethodException var10) {        throw new InternalError(var10.toString(), var10);    }}复制代码

然后,跟进 getProxyClass0(var0, var3) 看看是如何获取代理类的 class 对象的,点击进入,如下:

private static Class<?> getProxyClass0(ClassLoader var0, Class<?>... var1) {    if (var1.length > 65535) {        throw new IllegalArgumentException("interface limit exceeded");    } else {        // 缓存了代理类的 class 对象        return (Class)proxyClassCache.get(var0, var1);    }}复制代码

然后,我们来看看这个 var1 是个什么东西,我们往上找了找,果然发现,如下:

// var1 就是我们实现的 InvocationHandler 接口protected Proxy(InvocationHandler var1) {    Objects.requireNonNull(var1);    this.h = var1;}复制代码

然后,我们点进 proxyClassCache.get(var0, var1) 方法,如图:

使用关键代码 this.subKeyFactory.apply(var1, var2) 去获取我们的代理类的 class 对象,我们进入 apply 实现类 ProxyClassFactory,如:

public Class<?> apply(ClassLoader var1, Class<?>[] var2) {    IdentityHashMap var3 = new IdentityHashMap(var2.length);    Class[] var4 = var2;    int var5 = var2.length;    ...    if (var16 == null) {        var16 = "com.sun.proxy.";    }    long var19 = nextUniqueNumber.getAndIncrement();    // 生成代理类的类名    String var23 = var16 + "$Proxy" + var19;    // 生成代理类的字节码    byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);    try {        // 生成代理类的 class 对象        return Proxy.defineClass0(var1, var23, var22, 0, var22.length);    } catch (ClassFormatError var14) {        throw new IllegalArgumentException(var14.toString());    }}复制代码

然后,我们点进 Proxy.defineClass0 方法,如下:

private static native Class<?> defineClass0(ClassLoader var0, String var1, byte[] var2, int var3, int var4);复制代码

是一个 native 方法,所以涉及到 C 或 C++ ,我们就不往后追踪。

那么,代理的 Class 文件到底存在哪儿呢,由一个类的生命周期,如图:

代理的 Class 文件通过反射存在内存中,所以我们可以通过 byte[] 写入文件,我们新建一个工具类来把内存中的 class 字节码写入文件,如:

public class ProxyUtil {    public static void generateClassFile(Class aClass, String proxyName) {        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                proxyName,                new Class[]{aClass}        );        String path = aClass.getResource(".").getPath();        System.out.println(path);        FileOutputStream outputStream = null;        try {            outputStream = new FileOutputStream(path + proxyName + ".class");            outputStream.write(proxyClassFile);            outputStream.flush();        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                outputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}复制代码

通过输出的 path 路径,找到文件,如:

/Users/lishaoying/Documents/APP/Android/practice/annotation_reflect/anRePrDemo/proxyDemo/build/classes/java/main/net/lishaoy/proxydemo/service/impl/复制代码

文件代码,如下:

// 继承了 Proxy 实现了 ImplKoreaOrderService 接口public final class $Proxy0 extends Proxy implements ImplKoreaOrderService {    // 生成了各种方法    private static Method m1;    private static Method m8;    private static Method m3;    private static Method m2;    private static Method m5;    private static Method m4;    private static Method m7;    private static Method m9;    private static Method m0;    private static Method m6;    public $Proxy0(InvocationHandler var1) throws  {        super(var1);    }    ...    // 生成了 真实角色的 saveOrder 方法    public final int saveOrder() throws  {        try {            // h 是什?,点进去发现就是我们 传入的 InvocationHandler 接口            // m3 是什么? 下面 static 代码块,就是我们的 saveOrder 方法            return (Integer)super.h.invoke(this, m3, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    ...    public final Class getClass() throws  {        try {            return (Class)super.h.invoke(this, m7, (Object[])null);        } catch (RuntimeException | Error var2) {            throw var2;        } catch (Throwable var3) {            throw new UndeclaredThrowableException(var3);        }    }    ...    static {        try {            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));            m8 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("notify");            m3 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("saveOrder");            m2 = Class.forName("java.lang.Object").getMethod("toString");            m5 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait", Long.TYPE);            m4 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait", Long.TYPE, Integer.TYPE);            m7 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("getClass");            m9 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("notifyAll");            m0 = Class.forName("java.lang.Object").getMethod("hashCode");            m6 = Class.forName("net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService").getMethod("wait");        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }}复制代码

写在最后


我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料免费分享出来。

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。包含知识脉络 + 诸多细节,由于篇幅有限,下面只是以图片的形式给大家展示一部分。

Android学习PDF+学习视频+面试文档+知识点笔记

【Android高级架构视频学习资源】

Android部分精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

【Android进阶学习视频】、【全套Android面试秘籍】可以简信我【学习】查看免费领取方式!

使用注解、反射、动态代理完成简单的Retrofit

由于文章篇幅已经很长,且使用注解、反射、动态代理完成简单的 Retrofit 的案例代码过多,所以就不再这里展示,感兴趣的小伙伴可以去 GitHub 查看源码。

最后附上博客和GitHub地址,如下:

博客地址:h.lishaoy.net
GitHub地址:github.com/persilee

上一篇下一篇

猜你喜欢

热点阅读