注解咱们常用它,不少框架也提供了不少注解给咱们使用,如 ARouter
的 @Route(path = "/test/activity")
、butterknife
的 @BindView(R.id.user) EditText username;
等,可是,你有没有自定义过注解,写过本身的注解处理器呢?反射听起来很高大上,可是实际上你真的了解他以后,只是一些API的调用而已;动态代理其实只是在静态代理(代理模式)基础上使用了反射技术;本篇文章将带领你们对注解、反射及动态代理有更清晰的认知。html
本篇文章的示例代码放在 Github 上,全部知识点,如图:java
注解(Annotations),元数据的一种形式,提供有关于程序但不属于程序自己的数据。注解对它们注解的代码的操做没有直接影响。android
注解有多种用途,例如:git
注解的格式以下:github
@Persilee class MyClass { ... }
注解已 @
开头后面跟上内容,注解能够包含元素,例如:缓存
@Persilee(id=666, value = "lsy") class MyClass { ... }
若是,只有一个 value
元素,则能够省略该名称,若是,没有元素,则能够省略括号,例如bash
@Persilee("lsy") // 只有一个 value 元素 class MyClass { ... } @Persilee // 没有元素 class MyClass { ... }
若是,注解有相同的类型,则是重复注解,如app
@Persilee("lsy") @Persilee("zimu") class MyClass { ... }
注解的定义相似于接口的定义,在关键字 interface
前加上 @
,如:框架
@interface Persilee { int id(); String value(); }
int id()
和 String value()
是注解类型(annotation type),它们也能够定义可选的默认值,如:ide
@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(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
最后,在 app
的 build.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是一个类,封装了当前对象所对应的类的信息,咱们写的每个类均可以当作一个对象,是 java.lang.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: 66 the 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 获取私有字段的值: 66 BUILD SUCCESSFUL in 395ms
咱们已经对注解和反射有了更清晰的认知,下面咱们经过一个小案例来巩固咱们的学习:使用注解和反射完成相似 butterknife
的自动 findViewById
的功能。
新建一个空的 Android 工程,在工程目录下新建 inject 目录,在此目录下新建一个 InjectView
的类和 BindView
的自定义注解,如:
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(); } } } } }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface BindView { @IdRes int value(); // @IdRes 只能传 id 资源 }
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(); } }
运行结果,以下:
日本代购订单,下单成功,订单号为:888888 BUILD 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 下单成功,订单号为:666888 BUILD SUCCESSFUL in 1s
只须要一个 ProxyDynamicOrder
代理类便可完成 ImplOrderService
、 ImplJapanOrderService
、ImplKoreaOrderService
真实角色提供的服务。
咱们在 proxyDynamicOrder.getProxyInstance()
代码上打个断点,经过调试模式发现,如图:
代理类的名字是 $Proxy0@507
,为何是这个名字,咱们在编译后的目录里也找不到 $Proxy0@507
类文件,如图:
咱们经过查看 Proxy.newProxyInstance
方法源码,可知,如:
@CallerSensitive public 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()); } } }
因为文章篇幅已经很长,且使用注解、反射、动态代理完成简单的 Retrofit 的案例代码过多,因此就再也不这里展现,感兴趣的小伙伴能够去 GitHub 查看源码。
最后附上博客和GitHub地址,以下:
博客地址:https://h.lishaoy.net
GitHub地址:https://github.com/persilee