在说动态代理之前,还是简单地回顾一下静态代理吧。
1. 静态代理
应用场景:
学生交作业给老师。现在想通过静态代理的方式,让班长(代理类)代替学生(被代理类)交作业给老师。
为了让代理类与被代理类有相同的行为,那么,通过会让被代理类去实现一个接口,而且,被代理类也会去实现相同的接口。如下:
静态代理案例
Person
:公共接口
public interface Person {
// 交作业的方法
void handOut();
}
Student
:被代理类
public class Student implements Person {
@Override
public void handOut() {
System.out.println("学生交作业。");
}
}
不使用静态代理
public class Test {
public static void main(String[] args) {
Person student = new Student();
// 学生交作业。
student.handOut();
}
}
如果不使用静态代理,那么运行测试代码后,控制台将直接打印出:学生交作业。
使用静态代理
StudentProxy
:代理类
public class StudentProxy implements Person {
// 目标对象
private Person person;
public StudentProxy(Person person) {
this.person = person;
}
@Override
public void handOut() {
System.out.println("【代理类】增强原方法");
person.handOut();
}
}
说明:
- 代理类中持有目标对象,然后通过构造器将目标对象传入;
- 重写
handOut()
方法,可增强被代理类的方法的功能。
修改测试类:
public class Test {
public static void main(String[] args) {
Person student = new Student();
Person monitor = new StudentProxy(student);
// 【代理类】增强原方法
// 学生交作业。
monitor.handOut();
}
}
使用静态代理后,运行测试代码后,控制台将直接打印出:【代理类】增强原方法;学生交作业。
可见,StudentProxy#handOut()
方法确实增强了 Student#handOut()
方法
。
静态代理优、缺点
优点: 静态代理使用起来简单、方便
缺点: 一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行
2. JDK 动态代理
常用的动态代理有两类:JDK 动态代理、CGLIB 动态代理。这里主要讲下 JDK 动态代理。
2.1 JDK 动态代理介绍
JDK 动态代理是 JRE 提供给我们的类库,可以直接使用,不依赖第三方。
Java 动态代理类位于 java.lang.reflect
包下,一般主要涉及到以下两个类:
1. InvocationHandler
:该接口中仅定义了一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
- proxy:动态代理对象
- method:被代理的方法
- args:方法的参数数组
这个抽象方法在代理类中动态实现
2. Proxy
:该类即为动态代理类
Proxy#newProxyInstance()
:返回代理类的一个实例
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
动态代理类:它是在运行时生成的一种类,在生成它时,必须提供一组 interfaces 给它,然后该类就会实现这些 interface。动态代理类就是 Proxy
,它不会替你干任何事,在生成它的时候,也必须提供一个 handler,由它接管实际的工作。
2.2 JDK 动态代理使用
JDK 动态代理使用步骤
动态代理步骤:
- 创建一个实现了接口
InvocationHandler
的类,它必须实现invoke()
方法 - 创建被代理的类和公共接口
- 调用
Proxy.newProxyInstance()
方法创建一个代理类 - 通过代理对象调用方法
我们使用 JDK 动态代理来实现下上面的应用场景吧。
Person
:公共接口
public interface Person {
// 交作业的方法
void handOut();
}
Student
:被代理类
public class Student implements Person {
@Override
public void handOut() {
System.out.println("学生交作业。");
}
}
InvocationHandler 实现类
添加一个 InvocationHandler
,它不是代理类,因为它并没有实现公共接口 Person
,它仅仅是一个包装器 wrapper。包装了 target
对象。
public class PersonHandlerImpl<T> implements InvocationHandler {
// 被代理对象
private T target;
public PersonHandlerImpl(T target) {
this.target = target;
}
/**
* @param proxy: 动态代理对象
* @param method: 正在执行的方法
* @param args: 调用目标方法时传入的实参
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【增强方法】代理对象正在执行的方法:" + method.getName());
Object result = method.invoke(target, args);
return result;
}
}
添加一个代理工厂类 ProxyFactory
:生产代理对象
public class ProxyFactory {
public static Object creatProxyObj(Object target) {
PersonHandlerImpl handler = new PersonHandlerImpl(target);
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
}
}
测试类:
public class Test {
public static void main(String[] args) {
Person student = new Student();
Person studentProxy = (Person) ProxyFactory.creatProxyObj(student);
studentProxy.handOut();
}
}
Proxy
根据 target
的接口生成了一个实现类,它就是动态代理类 $Proxy0
。
生成过程:由于拿到的是接口,那么,我们便可以获知接口的所有的信息(方法的定义),也就能声明一个类去实现这个接口,并重写这个接口中所有的方法。而在这些重写的方法中会调用其它对象的方法。当然,这个被调用的对象不能是 target
。因为,如果是 target
对象,那么,我们如何增强其中的方法 handOut()
。
所以,它调用的是 target
的包装类,这个类需要我们来实现。但是,JDK 给出了约束,这个包装类要实现 InvocationHandler
接口。上述例子中,就是 PersonHandlerImpl
。这个类里面有个方法,它是 target
的所有方法的调用入口 invoke()
。在调用它之前,我们可以加自己的代码增强。
所以,可以这么认为:动态代理类 代理了 InvocationHandler
,而 InvocationHandler
又代理了我们的 target
,两级代理
2.3 JDK 动态代理的原理
通过 Debug 模式,我们可以知道:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的 handler 对象的 invoke() 方法来进行调用。
即:当调用 studentProxy.handOut()
方法时,
Person studentProxy = (Person) ProxyFactory.creatProxyObj(student);
studentProxy.handOut();
会自动调用 PersonHandlerImpl#invoke()
方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【增强方法】代理对象正在执行的方法:" + method.getName());
Object result = method.invoke(target, args);
return result;
}
这是为什么呢???
过程分析:
我们通过 Debug 模式看代码慢慢分析:
首先看看 Proxy#newProxyInstance()
方法:返回一个代理类对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 1.校验 InvocationHandler 是否为空
Objects.requireNonNull(h);
// 2.根据类加载器和接口来获取动态代理类对应的 Class 类型
Class<?> cl = getProxyClass0(loader, intfs);
// 3.通过反射来获取构造器对象并生成动态代理对象
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//...
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
}
// ...
}
重点看第2步:Proxy#getProxyClass0
方法:
// 代理类缓存
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
// ...
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 1.公共接口数量不能超过 2^16 - 1 个
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 2.先从缓存中取代理类实例。如果缓存中有,则直接去;否则,需要ProxyClassFactory生成代理类
return proxyClassCache.get(loader, interfaces);
}
查看 WeakCache#get()
方法:
public V get(K key, P parameter) {
//...
while (true) {
if (supplier != null) {
V value = supplier.get();
if (value != null) {
return value;
}
}
}
//...
}
虽然,这个 get()
方法有很多逻辑,但是,它最后一定会调用 V value = supplier.get();
并且,会返回 value
。因为,这是一个 while(true)
循环,且只有一个 return
语句。
查看 Factory#get()
方法:
Factory
为 WeakCache
的一个内部类
public synchronized V get() { // serialize access
//...
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
//...
return value;
}
}
查看 valueFactory.apply(key, parameter)
语句。其中,valueFactory
是一个代理工厂 Proxy$ProxyClassFactory
。
Proxy$ProxyClassFactory#apply()
方法:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//...
if (proxyPkg == null) {
proxyPkg = com.sun.proxy + ".";
}
// 代理对象的名称
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + "$Proxy" + num;
// 1.生成代理对象字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
//...
}
}
通过这个方法,我们来生成一个字节码文件看看:
public class Test2 {
public static void main(String[] args) {
Person student = new Student();
Person studentProxy = (Person) ProxyFactory.creatProxyObj(student);
studentProxy.handOut();
// 生成字节码文件
createProxyClassFile();
}
private static void createProxyClassFile(){
String name = "ProxyObject";
byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{Person.class});
FileOutputStream out =null;
try {
out = new FileOutputStream(name + ".class");
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != out) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
反编译这个 class 文件:
public final class ProxyObject extends Proxy implements Person {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public ProxyObject(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void handOut() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (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"));
m3 = Class.forName("com.zzc.proxy.dynamicproxy.Person").getMethod("handOut");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
这就是我们生成的真正的动态代理类:$Proxy0
。
它继承了 Proxy
类,并且实现了公共的接口 Person
。
语句:Person studentProxy = (Person) ProxyFactory.creatProxyObj(student);
中的
studentProxy
就是它的一个实例。
语句:studentProxy.handOut();
就是调用动态代理类中的方法:
public final void handOut() throws {
try {
super.h.invoke(this, m3, (Object[])null);
}
//...
}
所以,为什么会自动调用 PersonHandlerImpl#invoke()
方法?
因为 JDK 生成了一个动态代理类,它继承 Proxy
,并实现了 Person
接口,自然会重写 handOut()
方法。在重新的方法中,又通过反射去调用了 PersonHandlerImpl#invoke()
方法。