Java注解、反射及动态代理

本文探讨了Java注解的定义、元注解的使用,如@Target和@Retention,以及注解在源码检查(IDEA)、APT注解处理器和字节码操作(如AspectJ)的应用。深入解析了反射机制,包括Class类和反射操作,以及动态代理的原理和Retrofit的实践应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java注解、反射及动态代理

注解

注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他如反射、插桩等技术才有意义。
Java 注解(Annotation)又称 Java 标注,是 JDK1.5 引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

注解声明

声明一个注解类型

Java中所有的注解,默认实现 Annotation 接口:

package java.lang.annotation; 
public interface Annotation { 
    boolean equals(Object obj); 
    int hashCode(); 
    String toString(); 
    Class<? extends Annotation> annotationType(); 
}

与声明一个"Class"不同的是,注解的声明使用 @interface 关键字。一个注解的声明如下:

public @interface Test{ }

元注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)。一般的,我们在定义自定义注解时,需要指定的元注解有两个 :

@Target

注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:

名称作用域
ElementType.ANNOTATION_TYPE可以应用于注解类型
ElementType.CONSTRUCTOR可以应用于构造函数
ElementType.FIELD可以应用于字段或属性
ElementType.LOCAL_VARIABLE可以应用于局部变量
ElementType.METHOD可以应用于方法级注解
ElementType.PACKAGE可以应用于包声明
ElementType.PARAMETER可以应用于方法的参数
ElementType.TYPE可以应用于类的任何元素
@Retention

注解指定标记注解的存储方式:

名称保留级别
RetentionPolicy.SOURCE标记的注解仅保留在源级别中,并被编译器忽略
RetentionPolicy.CLASS标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略
RetentionPolicy.RUNTIME标记的注解由 JVM 保留,因此运行时环境可以使用它

@Retention 三个值中 SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。下文会介绍他们不同的应用场景。

下面来看例子:

//@Target(ElementType.TYPE) 只能在类上标记该注解 
@Target({ElementType.TYPE,ElementType.FIELD}) // 允许在类与类属性上标记该注解 
@Retention(RetentionPolicy.SOURCE) //注解保留在源码中 
public @interface Test { 

}

注解类型元素

在上文元注解中,允许在使用注解时传递参数。我们也能让自定义注解的主体包含 annotation type element (注解类型元素) 声明,它们看起来很像方法,可以定义可选的默认值。

@Target({ElementType.TYPE,ElementType.FIELD}) 
@Retention(RetentionPolicy.SOURCE) 
public @interface Test { 
    String value(); //无默认值 
    int age() default 1; //有默认值 
}

注意:在使用注解时,如果定义的注解中的类型元素无默认值,则必须进行传值。

@Test("帅") 
//如果只存在value元素需要传值的情况,则可以省略:元素名= 
@Test(value="帅",age = 2) 
int i;

注解应用场景

按照**@Retention**元注解定义的注解存储方式,注解可以被在三种场景下使用:

RetentionPolicy.SOURCE

RetentionPolicy.SOURCE,作用于源码级别的注解,可提供给IDE语法检查、APT等场景使用。

IDE语法检查

在Android开发中, support-annotations 与 androidx.annotation) 中均有提供 @IntDef 注解,此注解的定义如下:

@Retention(SOURCE) //源码级别注解 
@Target({ANNOTATION_TYPE}) 
public @interface IntDef { 
    int[] value() default {}; 
    boolean flag() default false; 
    boolean open() default false; 
}

Java中Enum(枚举)的实质是特殊单例的静态成员变量,在运行期所有枚举类作为单例,全部加载到内存中。

比常量多5到10倍的内存占用。此注解的意义在于能够取代枚举,实现如方法入参限制。

如:我们定义方法 test ,此方法接收参数 teacher 需要在:LanceAlvin中选择一个。如果使用枚举能够实现为:

public enum Teacher{ 
    LANCE,
    ALVIN 
}
public void test(Teacher teacher) {
}

而现在为了进行内存优化,我们现在不再使用枚举,则方法定义为:

public static final int LANCE = 1; 
public static final int ALVIN = 2; 
public void test(int teacher) { 
}

然而此时,调用 test 方法由于采用基本数据类型int,将无法进行类型限定。此时使用@IntDef增加自定义注解:

public static final int LANCE = 1; 
public static final int ALVIN = 2; 
@IntDef(value = {LANCE, ALVIN}) //限定为LANCE,ALVIN 
@Target(ElementType.PARAMETER) //作用于参数的注解 
@Retention(RetentionPolicy.SOURCE) //源码级别注解 
public @interface Teacher {
}
public void test(@Teacher int teacher) {
}

此时,我们再去调用 test 方法,如果传递的参数不是 LANCE 或者 ALVIN 则会显示 Inspection 警告(编译不会报错)。

以上注解均为 SOURCE 级别,本身IDEA/AS 就是由Java开发的,工具实现了对Java语法的检查,借助注解能对被注解的特定语法进行额外检查。

APT注解处理器

APT全称为:“Anotation Processor Tools”,意为注解处理器。顾名思义,其用于处理注解。编写好的Java源文件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码Class文件。注解处理器是 javac 自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac调起,并将注解信息传递给注解处理器进行处理。

注解处理器是对注解应用最为广泛的场景。在Glide、EventBus3、Butterknifer、Tinker、ARouter等等常用框架中都有注解处理器的身影。但是你可能会发现,这些框架中对注解的定义并不是 SOURCE 级别,更多的是 CLASS 级别,别忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。
APT相关移步至:
https://blog.youkuaiyun.com/qq_20521573/article/details/82321755

RetentionPolicy.CLASS

定义为 CLASS 的注解,会保留在class文件中,但是会被虚拟机忽略(即无法在运行期反射获取注解)。此时完全符合此种注解的应用场景为字节码操作。如:AspectJ、热修复Roubust中应用此场景。
所谓字节码操作即为,直接修改字节码Class文件以达到修改代码执行逻辑的目的。

在程序中有多处需要进行是否登录的判断,如果我们使用普通的编程方式,需要在代码中进行 if-else 的判断,也许存在十个判断点,则需要在每个判断点加入此项判断。此时,我们可以借助 AOP(面向切面) 编程思想,将程序中所有功能点划分为: 需要登录 与 无需登录两种类型,即两个切面。对于切面的区分即可采用注解。

//Java源码 
@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.CLASS) 
public @interface Login {
}
@Login 
public void jumpA(){ 
    startActivity(new Intent(this,AActivity.class)); 
}
public void jumpB(){ 
    startActivity(new Intent(this,BActivity.class)); 
}

在上诉代码中, jumpA 方法需要具备登录身份。而 Login 注解的定义被设置为 CLASS 。因此我们能够在该类所编译的字节码中获得到方法注解 Login 。在操作字节码时,就能够根据方法是否具备该注解来修改class中该方法的内容加入 if-else 的代码段:

//Class字节码 
@Login public void jumpA() { 
    if (this.isLogin) { 
        this.startActivity(new Intent(this, LoginActivity.class)); 
    } else { 
        this.startActivity(new Intent(this, AActivity.class)); 
    } 
}
RetentionPolicy.RUNTIME

注解保留至运行期,意味着我们能够在运行期间结合反射技术获取注解中的所有信息。

反射(Reflect)

反射之中包含了一个「反」字,所以了解反射我们先从「正」开始。

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

反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。

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

  1. 在运行时构造任意一个类的对象
  2. 在运行时获取任意一个类所具有的成员变量和方法
  3. 在运行时调用任意一个对象的方法(属性)

Java 是一门面向对象的语言。在面向对象的世界里,万事万物皆对象,既然万事万物皆对象,那么我们的类是不是对象呢?我们写的每一个类都可以看成一个对象,是 java.lang.Class 类的对象。每一个类对应的Class放在哪里呢?当我们写完一个类的Java文件,编译成class文件的时候,编译器都会将这个类的对应的class对象放在class文件的末尾。里面都保存了些什么?大家可以理解保存了类的元数据信息,一个类的元数据信息包括什么?有哪些属性,方法,构造器,实现了哪些接口等等,那么这些信息在Java里都有对应的类来表示。

Class类

Class是一个类,封装了当前对象所对应的类的信息

一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类。

Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等

对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

对象只能由系统建立对象,一个类(而不是一个对象)在 JVM 中只会有一个Class实例

获取Class对象的三种方式

  1. 通过类名获取 类名.class
  2. 通过对象获取 对象名.getClass()
  3. 通过全类名获取 Class.forName(全类名)
Class类的常用方法
方法名功能说明
static Class<?> forName(@NonNull String className)返回指定类名name的Class对象
native T newInstance()调用缺省构造函数,返回该Class对象的一个实例
String getName()返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
Class<? super T> getSuperclass()返回当前Class对象的父类的Class对象
Class<?>[] getInterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
判断是否为某个类的实例

一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:

public native boolean isInstance(Object obj);

判断是否为某个类的类型

public boolean isAssignableFrom(Class<?> cls)
创建实例

通过反射来生成对象主要有两种方式:

  • 使用Class对象的newInstance()方法来创建Class对象对应类的实例。

    Class<?> c = String.class; 
    Object str = c.newInstance();
    
  • 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

    //获取String所对应的Class对象 
    Class<?> c = String.class; 
    //获取String类带一个String参数的构造器 
    Constructor constructor = c.getConstructor(String.class); //根据构造器创建实例 
    Object obj = constructor.newInstance("23333"); 
    System.out.println(obj);
    
获取构造器信息

得到构造器的方法

Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的public构造函数(包括父类) Constructor[] getConstructors() -- 获得类的所有公共构造函数 
Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(包括私有) Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

public T newInstance(Object ... initargs)
获取类的成员变量(字段)信息

获得字段信息的方法

Field getField(String name) -- 获得命名的公共字段 
Field[] getFields() -- 获得类的所有公共字段 
Field getDeclaredField(String name) -- 获得类声明的命名的字段 
Field[] getDeclaredFields() -- 获得类声明的所有字段
调用方法

获得方法信息的方法

Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法 
Method[] getMethods() -- 获得类的所有公共方法 
Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法 
Method[] getDeclaredMethods() -- 获得类声明的所有方法

当我们从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。 invoke 方法的原型为:

public Object invoke(Object obj, Object... args)
利用反射创建数组

数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference 其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:

public static Object newInstance(Class<?> componentType, int length);

反射获取泛型真实类型

当我们对一个泛型类进行反射时,需要的到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通过 Type 体系来完成。 Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是:

  • TypeVariable 泛型类型变量。可以泛型上下限等信息;
  • ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
  • GenericArrayType 当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
  • WildcardType 通配符泛型,获得上下限信息;

TypeVariable

public class TestType <K extends Comparable & Serializable, V> { 
    K key; 
    V value; 
    public static void main(String[] args) throws Exception { // 获取字段的类型 
        Field fk = TestType.class.getDeclaredField("key"); 
        Field fv = TestType.class.getDeclaredField("value"); 
        TypeVariable keyType = (TypeVariable)fk.getGenericType(); 
        TypeVariable valueType = (TypeVariable)fv.getGenericType(); 
        // getName 方法 
        System.out.println(keyType.getName()); // K 
        System.out.println(valueType.getName()); // V 
        // getGenericDeclaration 方法 
        System.out.println(keyType.getGenericDeclaration()); // class com.test.TestType
        System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
        // getBounds 方法 
        System.out.println("K 的上界:"); // 有两个 
        for (Type type : keyType.getBounds()) { // interface java.lang.Comparable 
            System.out.println(type); // interface java.io.Serializable 
        }
        System.out.println("V 的上界:"); // 没明确声明上界的, 默认上界是 Object 
        for (Type type : valueType.getBounds()) { // class java.lang.Object 
            System.out.println(type); 
        } 
    } 
}

ParameterizedType

public class TestType { 
    Map<String, String> map; 
    public static void main(String[] args) throws Exception { 
        Field f = TestType.class.getDeclaredField("map"); 
        System.out.println(f.getGenericType()); // java.util.Map<java.lang.String, java.lang.String> 
        ParameterizedType pType = (ParameterizedType) f.getGenericType(); 
        System.out.println(pType.getRawType()); // interface java.util.Map 
        for (Type type : pType.getActualTypeArguments()) { 
            System.out.println(type); // 打印两遍: class java.lang.String 
        } 
    } 
}

GenericArrayType

public class TestType<T> { 
    List<String>[] lists; 
    public static void main(String[] args) throws Exception { 
        Field f = TestType.class.getDeclaredField("lists"); 
        GenericArrayType genericType = (GenericArrayType) f.getGenericType(); 
        System.out.println(genericType.getGenericComponentType()); 
    } 
}

WildcardType

public class TestType { 
    private List<? extends Number> a; // 上限 
    private List<? super String> b; //下限 
    public static void main(String[] args) throws Exception { 
        Field fieldA = TestType.class.getDeclaredField("a"); 
        Field fieldB = TestType.class.getDeclaredField("b"); 
        // 先拿到范型类型 
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType(); 
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType(); 
        // 再从范型里拿到通配符类型 
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0]; 
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0]; 
        // 方法测试 
        System.out.println(wTypeA.getUpperBounds()[0]); // class java.lang.Number 
        System.out.println(wTypeB.getLowerBounds()[0]); // class java.lang.String 
        // 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
		System.out.println(wTypeA); 
    } 
}
Gson反序列化
static class Response<T> { 
    T data; 
    int code; 
    String message; 
    @Override public String toString() { 
        return "Response{" + 
            "data=" + data + 
            ", code=" + code + 
            ", message='" + message + '\'' + 
            '}'; 
    }
    public Response(T data, int code, String message) { 
        this.data = data; 
        this.code = code; 
        this.message = message; 
    } 
}
static class Data { 
    String result; 
    public Data(String result) { 
        this.result = result; 
    }
    @Override public String toString() { 
        return "Data{" + "result=" + result + '}'; 
    } 
}
public static void main(String[] args) { 
    Response<Data> dataResponse = new Response(new Data("数据"), 1, "成功"); 
    Gson gson = new Gson(); 
    String json = gson.toJson(dataResponse); 
    System.out.println(json);
    //为什么TypeToken要定义为抽象类? 
    Response<Data> resp = gson.fromJson(json, new TypeToken<Response<Data>>() { 
    }.getType());
    System.out.println(resp.data.result); 
}

在进行GSON反序列化时,存在泛型时,可以借助 TypeToken 获取Type以完成泛型的反序列化。但是为什么TypeToken 要被定义为抽象类呢?
因为只有定义为抽象类或者接口,这样在使用时,需要创建对应的实现类,此时确定泛型类型,编译才能够将泛型signature信息记录到Class元数据中。

动态代理

代理模式和静态代理

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

举个例子来说明:张三想买某种用品,虽然他可以自己去找,但是这确实太浪费时间和精力了,或者不好意思去买。于是张三就通过中介Mark来买,Mark来帮张三,张三只是负责选择自己喜欢的的size,然后付钱就可以了。

目的:(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性; (2)通过代理对象对原有的业务增强;

代理模式一般会有三个角色:
在这里插入图片描述

  • 抽象对象:指代理角色和真实角色对外提供的公共方法,一般为一个接口
  • 真实对象:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
  • 代理对象:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!而访问者不再访问真实角色,而是去访问代理角色。

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。

静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。

动态代理

是指在使用时再创建代理类和实例

优点

  • 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码
  • 更强的灵活性

缺点

  1. 效率低,相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法
  2. 应用场景局限,因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类创建代理类。

在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。

//抽象角色 
interface Api { 
    void test(String a); 
}
//真实角色 
class ApiImpl implements Api{ 
    @Override public void test(String a) { 
        System.out.println("真实实现:" + a); 
    } 
}
//创建真实角色实例 
Api api = new ApiImpl(); 
//JDK动态代理: 
Proxy.newProxyInstance(getClass().getClassLoader(), 
                       new Class[]{Api.class}, //JDK实现只能代理接口 
                       new InvocationHandler() { 
                           @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                               //执行真实对象方法 
                               return method.invoke(api, args); 
                           } 
                       });

实际上, Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译而来,即没有真正的文件,只是在内存中按照Class格式生成了一个Class。

动态代理实现原理
public interface AToolsFactory {
    void saleATools(String size);
}

public interface BToolsFactory {
    void saleBTools(float length);
}
public class AaFactory implements AToolsFactory {
    @Override
    public void saleATools(String size) {
        System.out.println("按需求定制了一个size为"+size+"的model");
    }
}
public class BbFactory implements BToolsFactory {
    @Override
    public void saleBTools(float length) {
        System.out.println("按需求定制了一个高度为"+length+"的model");
    }
}

public class MarkCompany implements InvocationHandler {

    /*持有的真实对象*/
    private Object factory;

    public Object getFactory() {
        return factory;
    }

    public void setFactory(Object factory) {
        this.factory = factory;
    }

    /*通过Proxy获得动态代理对象*/
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),this);
    }

    /*通过动态代理对象方法进行增强*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        doSthAfter();
        Object result = method.invoke(factory, args);
        doSthBefore();
        return result;
    }

    /*前置处理器*/
    private void doSthAfter() {
        System.out.println("精美包装,快递一条龙服务");
    }
    /*后置处理器*/

    private void doSthBefore() {
        System.out.println("根据需求,进行市场调研和产品分析");
    }
}

AToolsFactory aafactory = new AaFactory();
MarkCompany markCompany = new MarkCompany();
markCompany.setFactory(aafactory);
AToolsFactory employee1= (AToolsFactory)markCompany.getProxyInstance();
employee1.saleManTools("E");

BToolsFactory bbToolsFactory = new BbFactory();
markCompany.setFactory(bbToolsFactory);
BToolsFactory employee2= (BToolsFactory)markCompany.getProxyInstance();
employee2.saleWomanTools(1.9f);

通过调试模式我们发现,动态代理里,代理类的类名是这样的:

AToolsFactory employee1= (AToolsFactory)markCompany.getProxyInstance();//{$Proxy0@551}

这个代理类为何是这个名字?它是如何执行被代理对象的相关方法呢?我们在java文件编译后的目录里其实找不到这个名为$Proxy0的class文件的。

观察Proxy.newProxyInstance方法,与创建对象有关的代码主要有:

获得代理类的class对象:

Class<?> cl = getProxyClass0(loader,nitfs); 

获得代理类的构造器:

final Constructor<?> cons = cl.getConstructor(constructorParams); 

创建代理类的实例

return cons.newInstance(new Object[]{h});

看来其中的关键点就是如何获得代理类的class对象,我们进入getProxyClass0方法,进而进入proxyClassCache.get方法,通过这个这个方法所在的类名,我们可以推测,JDK内部使用了某种机制缓存了我们的代理类的class对象,同时get方法接受的参数是被代理类的类加载器和类实现的的接口。

在这个get方法中,除去和缓存相关的操作,同时用到了被代理类的类加载器和类实现的的接口这两个参数的是

Object var9 = Objects.requireNonNull(this.subKeyFactory.apply(var1, var2)); 

我们再进入java.lang.reflect.Proxy查看这个方法的实现,

  public Class<?> apply(ClassLoader var1, Class<?>[] var2) {
      		........

            if (var16 == null) {
                var16 = "com.sun.proxy.";
            }

            long var19 = nextUniqueNumber.getAndIncrement();
            String var23 = var16 + "$Proxy" + var19;//代理类的类名
            byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);//代理类的字节码

            try {
                return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
            } catch (ClassFormatError var14) {
                throw new IllegalArgumentException(var14.toString());
            }
        }
    }  

而最终生成代理类的class对象是defineClass0方法,但是这个方法是个native方法,所以我们不去也无法深究它,但是通过这个方法的参数我们可以明显看到它接收了上面所生成的byte数组。

public class ProxyUtils {

    public static void generateClassFile(Class clazz,String proxyName){
        /*ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);*/
        byte[] proxyClassFile =ProxyGenerator.generateProxyClass(
                proxyName, new Class[]{clazz});
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;

        try {
            out = new FileOutputStream(paths+proxyName+".class");
            out.write(proxyClassFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}



ProxyUtils.generateClassFile(aafactory.getClass(),
                employee1.getClass().getSimpleName());

而我们通过ProxyUtils,这个自己写的工具类,将这个byte数组写入文件,我们并反编译,我们将会看到

public final class $Proxy0 
    extends Proxy //派生自Proxy类
    implements AaFactory{//实现了业务接口
    private static Method m1;//各种方法
    private static Method m2;
    private static Method m3;
    private static Method m4;
    ...
} 

同时我们还会看到其中实现了业务接口的方法

public final void saleATools(String paramString) throws Exception {
      try{
          this.h.invoke(this,m3,new Object[]{paramString});
      }catch(Exception e){
          
      }
}  

而h则来自派生类Proxy中

public class Proxy implements Serializable {
    private static final long serialVersionUID = -2222568056686623797L;
    private static final Class<?>[] constructorParams = new Class[]{InvocationHandler.class};
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache(new Proxy.KeyFactory(), new Proxy.ProxyClassFactory());
    protected InvocationHandler h;
    ...
} 

这个h的实例来自哪里?不就是我们在创建代理类的实例时传入的吗?

public class MarkCompany implements InvocationHandler {
	/*持有的真实对象*/
	private Object factory;

	public Object getFactory() {
    	return factory;
	}

	public void setFactory(Object factory) {
    	this.factory = factory;
	}

	/*通过Proxy获得动态代理对象*/
	public Object getProxyInstance(){
    return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
            factory.getClass().getInterfaces(),this);
	}
    ...
} 
使用了动态代理的Retrofit

Retrofit简单的说就是一个网络请求的适配器,它将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。此外它还具有强大的可扩展性,支持各种格式转换以及RxJava。我们基于Retrofit2解析。

先定义一个名为X的java接口,当然里面有各种注解。

@FormUrlEncoded注解表示from表单,另外还有@Multipart等注解。@POST表示post请求,此外还可以使用@GET请求

public interface X{
	@FormUrlEncoded
    @POST("user/personal_list_info")
    Call<Response<PersonalInfo> getPersonalListInfo(@Field("cur_page") int page);
} 

然后如何使用的呢?

首先将域名传入构造一个Retrofit,然后通过retrofit中的create方法传入一个Java接口并得到一个x(当然x这个对象是经过处理了的)调用getPersonalListInfo(12)然后返回一个Call,最后这个Call调用了enqueue方法去异步请求http,这就是一个基本的Retrofit的网络请求。Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client。

void requestRetrofit(){
	Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxxx.com/").build();
	X x = retrofit.create(X.class);
	Call<Response<PersonalInfo>> call = x.getPerSonalListInfo(12);
    call.enqueue(new Callback<Response<PersonalInfo>>(){
        @Override
        public void onResponse(Call<Response<PersonalInfo>>call,
        Response<Response<PersonalInfo>> response){
        	//数据请求成功
    	}
        public void onFailure(Call<Response<PersonalInfo>> call, Throwable t){
            //	数据请求失败
        }
    });
} 

我们只定义了一个接口X,并没有实现这个接口,那么它是如何工作的呢?我们看看create方法的实现。

create()方法是个泛型方法,调用它时会返回一个范型T的对象,我们这里类型是X接口,在内部实现上,很明显了使用了动态代理返回了一个X的代理类。当调用X内部方法的时候,会调用invoke方法。invoke方法内则通过内部一系列的封装最后返回一个Call对象。

 @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值