注解与反射以及动态代理
注解
注解的定义
Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。
基本规则:Annotation不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行。
什么是元数据
- 元数据以标签的形式存在于Java代码中。
- 元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
- 元数据需要编译器之外的工具额外的处理用来生成其它的程序部件。
- 元数据可以只存在于Java源代码级别,也可以存在于编译之后的Class文件内部。
注解的原理
当Java源代码被编译时,编译器的一个插件annotation处理器则会处理这些annotation。处理器可以产生报告信息,或者创建附加的Java源文件或资源。如果annotation本身被加上了RententionPolicy的运行时类,则Java编译器则会将annotation的元数据存储到class文件中。然后,Java虚拟机或其他的程序可以查找这些元数据并做相应的处理。当然除了annotation处理器可以处理annotation外,我们也可以使用反射自己来处理annotation
注解的分类
系统内置标准注解
- @Override(标记注解类型)
- @Deprecated(标志已过时),
- @SuppressWarnings(抑制编译器警告)
元注解
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta�
annotation(元注解)。一般的,我们在定义自定义注解时,需要指定的元注解有两个 :
另外还有@Documented 与 @Inherited 元注解,前者用于被javadoc工具提取成文档,后者表示允许子类继承父类中定义的注解。
@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。下文会介绍他们不同的应用场景。
Android support annotations
- Nullness注解
- Resource Type 注解(@StringRes,@ColorInt等)
- Threading 注解(@UiThread UI线程,
,@MainThread 主线程,@WorkerThread 子线程,
@BinderThread 绑定线程
) - Overriding Methods 注解: @CallSuper
注解的应用场景
按照@Retention 元注解定义的注解存储方式,注解可以被在三种场景下使用:
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倍的内存占用。
APT技术
APT全称为:"Anotation Processor Tools",意为注解处理器。顾名思义,其用于处理注解。编写好的Java源文件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码Class文件。注解处理器是 javac 自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac调起,并将注解信息传递给注解处理器进行处理。
注解处理器是对注解应用最为广泛的场景。在Glide、EventBus3、Butterknifer、Tinker、ARouter等等常用框架中都有注解处理器的身影。但是你可能会发现,这些框架中对注解的定义并不是 SOURCE 级别,更多的是 CLASS 级别,别忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。
注解处理器的创建步骤
Android Studio创建一个java library
-
自定义一个注解(Annotation),用于存储元数据
@Retention(RetentionPolicy.CLASS) @Target({ElementType.FIELD}) public @interface Print { }
-
创建一个自定义Annotation Processor继承于AbstractProcessor
@AutoService(Print.class) @SupportedAnnotationTypes("com.lbz.apt_annotation.Print") public class MyProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { return false; } @Override public synchronized void init(ProcessingEnvironment processingEnv) { System.out.println("Hello APT"); processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Hello APT"); super.init(processingEnv); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> strings = new HashSet<>(); strings.add(Print.class.getCanonicalName()); return super.getSupportedAnnotationTypes(); } @Override public SourceVersion getSupportedSourceVersion() { return processingEnv.getSourceVersion(); }
@AutoService(MyProcessor.class) :向javac注册我们这个自定义的注解处理器,这样,在javac编译时,才会调用到我们这个自定义的注解处理器方法。
AutoService这里主要是用来生成
META-INF/services/javax.annotation.processing.Processor文件的。如果不加上这个注解,那么,你需要自己进行手动配置进行注册,具体手动注册方法如下:
创建一个META-INF/services/javax.annotation.processing.Processor文件, 其内容是一系列的自定义注解处理器完整有效类名集合:com.lbz.apt_processor.MyProcessor
- 编写process方法
CLASS
字节码增强,在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。
RUNTIME
在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。
反射
什么是反射
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。
Java反射机制主要提供了以下功能:
在运行时构造任意一个类的对象
在运行时获取或者修改任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的方法(属性)
获得 Class 对象
//获取Class的方式一
Person person1 = new Person();
Class personClass1 = person1.getClass();
//获取Class的方式二
Class<?> personClass2 = null;
try {
personClass2 = Class.forName("com.lbz.annotationdemo.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取Class的方式三
Class<Person> personClass3 = Person.class;
//获取Class的方式四
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass("com.lbz.annotationdemo.Person");
判断是否为某个类的实例
一般地,我们用 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.reflflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:
public static Object newInstance(Class<?> componentType, int length);
反射获取泛型真实类型
当我们对一个泛型类进行反射时,需要的到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通过 Type 体系来完成。 Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是:
- TypeVariable
泛型类型变量。可以泛型上下限等信息;
- ParameterizedType
具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
- GenericArrayType
当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
- WildcardType
通配符泛型,获得上下限信息;
TypeVariable
class Main<K extends Comparable & Serializable, V> {
K key;
V value;
public static void main(String[] args) throws Exception {
// 获取字段的类型
Field fk = Main.class.getDeclaredField("key");
Field fv = Main.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);
}
}
}
K
V
class Main
class Main
K 的上界:
interface java.lang.Comparable
interface java.io.Serializable
V 的上界:
class java.lang.Object
ParameterizedType
class Main {
HashMap<String, String> map;
public static void main(String[] args) throws Exception {
Field f = Main.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
}
}
}
java.util.HashMap<java.lang.String, java.lang.String>
class java.util.HashMap
class java.lang.String
class java.lang.String
GenericArrayType
class Main {
List<String>[] lists;
public static void main(String[] args) throws Exception {
Field f = Main.class.getDeclaredField("lists");
GenericArrayType genericType = (GenericArrayType) f.getGenericType();
System.out.println(genericType.getGenericComponentType());
}
}
java.util.List<java.lang.String>
WildcardType
class Main {
private List<? extends Number> a; // 上限
private List<? super String> b; //下限
public static void main(String[] args) throws Exception {
Field fieldA = Main.class.getDeclaredField("a");
Field fieldB = Main.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);
}
}
class java.lang.Number
class java.lang.String
? extends java.lang.Number
Gson反序列化
在进行GSON反序列化时,存在泛型时,可以借助 TypeToken 获取Type以完成泛型的反序列化。但是为什么TypeToken 要被定义为抽象类呢?
因为只有定义为抽象类或者接口,这样在使用时,需要创建对应的实现类,此时确定泛型类型,编译才能够将泛型signature信息记录到Class元数据中。
动态代理
参考https://juejin.cn/post/6974018412158664734
静态代理
定义
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
目的
- 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
- 通过代理对象对访问进行控制;
图解
代理模式一般会有三个角色:
抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!
静态代理缺点
- 1、重复性: 需要代理的业务或方法越多,重复的模板代码越多;
- 2、脆弱性: 一旦改动基础接口,代理类也需要同步修改(因为代理类也实现了基础接口)。
动态代理
在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提供了 Proxy 来完成这件事情。基本使用如下:
- 创建接口,定义目标列要完成的功能。
- 创建目标类实现接口。
- 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能。
- 调用目标方法
- 增强功能
- 使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//目标接口
interface Api {
float test(String a);
}
//目标类
class ApiImpl implements Api {
@Override
public float test(String a) {
System.out.println("目标类中,执行目标方法:" + a);
return 1F;
}
}
//必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法 2,功能增强)
class MyInvocationHandler implements InvocationHandler {
private Object target;
//动态代理:目标对象是活动的,不是固定的,需要传入进来
//传入是谁,就给谁创建代理
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res;
res = method.invoke(target, args);//执行目标方法
if (res != null) {
Float price = (Float) res;
price = price + 100;
res = price;
}
//在目标类的方法调用后,你做的其他功能,都是增强的意思
System.out.println("增强--------------------");
return res;
}
}
class Main {
public static void main(String[] args) {
//1.创建目标对象
Api factory = new ApiImpl();
//2.创建InvocationHandler对象
InvocationHandler invocationHandler = new MyInvocationHandler(factory);
//3.创建代理对象
Api proxy = (Api) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), invocationHandler);
//4.通过代理执行方法
float hello = proxy.test("hello");
System.out.println("通过动态代理对象,调用方法:" + hello);
}
}
目标类中,执行目标方法:hello
增强--------------------
通过动态代理对象,调用方法:101.0
实际上, Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译而来,即没有真正的文件,只是在内存中按照Class格式生成了一个Class。
动态代理源码分析
本文以jdk提供的Proxy作为分析,Android的实现和jdk的实现大同小异,差别最后再说。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
//安全管理器.判断有没有创建代理的权限
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 这是动态代理中最重要的方法,代理类就是从中得到的
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* 使用我们的InvocationHandler实现类来调用构造方法
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//判断代理类是否是被public修饰的,如果不是,设计代理类是可以通过反射访问到的
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//创建代理类对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
为什么动态代理需要传入classLoader?
1⃣️需要校验传入的接口是否可被当前的类加载器加载,假如无法加载,证明这个接口与类加载器不是同一个,按照双亲委派模型,那么类加载层次就被破坏了
2⃣️需要类加载器去根据生成的类的字节码去通过defineClass方法生成类的class文件,也就是说没有类加载的话是无法生成代理类的
我们主要看一下怎么获取到代理类的
Class<?> cl = getProxyClass0(loader, intfs);
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 通过缓存得到代理类,如果没有就通过ProxyClassFactory创建
return proxyClassCache.get(loader, interfaces);
}
接下来,我们看看ProxyClassFactory如何创建代理类
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 代理类类名的前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 代理类名字计数器 AtomicLong具有原子性
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* 判断相同名字的接口是否是同一个Class对象
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* 判断相应接口是否是一个真正的接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* 验证接口是否重复
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // 生成的代理类的包名
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 验证所有非公共接口的包是否在同一个包
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// 如果是公共接口,则用com.sun.proxy包
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 为代理类生成名字,规则 包名+前缀+数字 例:com.sun.proxy.$Proxy0
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成代理类的二进制文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//动态生成代理类,这就是最后一步了,由于是native方法,所以就无法向下追究了
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
接下来我们看一下具体是怎么获取代理类的二进制文件
ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
//发现可以通过 saveGeneratedFiles参数来决定是否把代理类代存到本地
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
//文件写入
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
发现可以通过 saveGeneratedFiles参数来决定是否把代理类代存到本地,点击它来查看在哪进行设置
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
我们可以把sun.misc.ProxyGenerator.saveGeneratedFiles这个属性设为true来把生成的类保存到本地,再运行,就会看到系统帮我们生成的代理类。以下为部分截取
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("agent.Massage").getMethod("message", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
在初始化时,获得 method 备用。而这个代理类中所有方法的实现变为:
public final void message(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
这里的 h 其实就是 InvocationHandler 接口,所以我们在使用动态代理时,传递的 InvocationHandler 就是一个监听,在代理对象上执行方法,都会由这个监听回调出来。
最后用一张图作为总结
和Android提供的Proxy对比
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
可以看到Android并没有通过ProxyGenerator生成代理类对象,而是直接交给native方法去处理。
静态代理和动态代理对比
- 共同点:两种代理模式实现都在不改动基础对象的前提下,对基础对象进行访问控制和扩展,符合开闭原则。
- 不同点:静态代理存在重复性和脆弱性的缺点;而动态代理(搭配泛型参数)可以实现了一个代理同时处理 N 种基础接口,一定程度上规避了静态代理的缺点。从原理上讲,静态代理的代理类 Class 文件在编译期生成,而动态代理的代理类 Class 文件在运行时生成,代理类在 coding 阶段并不存在,代理关系直到运行时才确定。