共计 5975 个字符,预计需要花费 15 分钟才能阅读完成。
1 动态代理
动态代理设计模式的原理:使用一个代理对象将原对象(目标对象)包装起来,然后利用该代理对象取代原对象。任何对原对象的调用都要经过代理。代理对象决定是否以及何时将方法调用转到原对象上。
2 动态代理用那些?
1 基于接口的动态代理:如 JDk 提供的代理 | |
2 基于继承的动态代理:如第三方包 Cglib,javassist 动态代理 | |
这里我们进行演示 JDK 自身提供的代理:
jdk 动态代理需要实现两个成员:一个是 Proxy 代理类,一个是 InvocationHandler 接口
1 JDK 动态代理类 | |
Proxy:所有动态代理对象的父类,专门用于生成代理类对象或者代理对象 | |
public static Object newProxyInstance(ClassLoader loader, | |
Class<?>[] interfaces, | |
InvocationHandler h) | |
2 接口 | |
InvocationHandler 完成动态代理的整个过程(动态代理类的调用处理程序都必须实现 InvocationHandler 接口) | |
proxy : 动态代理对象 | |
method : 正在被调用的方法对象 | |
args : 正在被调用的方法参数 | |
public Object invoke(Object proxy, Method method, Object[] args) | |
throws Throwable; |
接口和实现方法
public interface Arithmetic {public int add(int a,int b); | |
public int mul(int a,int b); | |
} | |
public class ArithmeticImpl implements Arithmetic { | |
@Override | |
public int add(int a, int b) {return a + b;} | |
@Override | |
public int mul(int a, int b) {return a * b;} | |
} |
代理对象
/** | |
生成代理对象 | |
**/ | |
public class ProxyObject { | |
// 目标对象 | |
private Object target; | |
public ProxyObject(Object target) {this.target = target;} | |
// 获取代理对象 | |
public Object getProxy(){ | |
// 代理对象 | |
Object proxy = null; | |
/* | |
ClassLoader loader : 目标对象的类加载器 | |
Class<?>[] interfaces : 接口们,目标对象提供的所有接口对象 | |
InvocationHandler h : 代理类的调用处理对象需要实现的接口 | |
*/ | |
ClassLoader loader = target.getClass().getClassLoader(); | |
Class<?>[] interfaces = target.getClass().getInterfaces(); | |
InvocationHandler h = new MyInvocationHandler(target); | |
// 这里创建代理对象,使得代理对象和目标对象拥有相同的方法行为,h 是代理对象调用处理执行的方法。proxy = Proxy.newProxyInstance(loader,interfaces,h); | |
return proxy; | |
} | |
} | |
class MyInvocationHandler implements InvocationHandler{ | |
private Object target; | |
public MyInvocationHandler(Object target) {this.target = target;} | |
// 代理对象调用代理方法,会回来调用 invoke 方法 | |
// proxy 代理对象,一般不会使用 | |
@Override | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("动态代理对象:" + proxy.getClass()); | |
System.out.println("正在被调用的方法方法对象:" + method); | |
System.out.println("正在被调用的方法方法名:" + method.getName()); | |
System.out.println("正在被调用的方法参数:" + Arrays.asList(args)); | |
System.out.println("执行方法前"); | |
// 实际上是内部在这儿使用目标对象执行目标方法。Object result = method.invoke(target,args); // 目标对象,调用目标方法 相当于 ArithmeticImpl 调用自己的方法 | |
System.out.println("执行方法后 result =" + result); | |
return result; | |
} | |
} |
测试
public static void main(String[] args) { | |
// 目标对象 | |
Arithmetic target = new ArithmeticImpl(); | |
// 获取代理对象 | |
Object object = new ProxyObject(target).getProxy(); | |
Arithmetic proxyObject = (Arithmetic) object; | |
proxyObject.add(1,2); | |
} |
结果
动态代理对象:class com.sun.proxy.$Proxy0 | |
正在被调用的方法方法对象:public abstract int com.example.demo.service.Arithmetic.add(int,int) | |
正在被调用的方法方法名:add | |
正在被调用的方法参数:[1, 2] | |
执行方法前 | |
执行方法后 result = 3 |
$Proxy0 就是我们的动态代理对象
可以在 main 方法中添加一行代码将代理的对象字节码打印出来!
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[]{Arithmetic.class}); | |
try { | |
String pathDir = "E:\\"; | |
String path = "\\$Proxy0.class"; | |
File f = new File(pathDir); | |
if (!f.exists()) {f.mkdir(); | |
} | |
path = f.getAbsolutePath() + path; | |
f = new File(path); | |
if (f.exists()) {f.delete(); | |
} | |
f.createNewFile(); | |
try (FileOutputStream fos = new FileOutputStream(path)) {fos.write(bytes); | |
} catch (Exception e) {e.printStackTrace(); | |
} | |
} catch (IOException e) {e.printStackTrace(); | |
} |
反编译后 $Proxy0 代码
import com.example.demo.service.Arithmetic; | |
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.lang.reflect.UndeclaredThrowableException; | |
public final class $Proxy0 | |
extends Proxy | |
implements Arithmetic | |
{ | |
private static Method m1; | |
private static Method m2; | |
private static Method m4; | |
private static Method m3; | |
private static Method m0; | |
public $Proxy0(InvocationHandler paramInvocationHandler) | |
throws | |
{ | |
// 调用父类 Proxy 设置 InvocationHandler 代理对象处理器,设置调用代理方法 h | |
super(paramInvocationHandler); | |
} | |
public final boolean equals(Object paramObject) | |
throws | |
{ | |
try | |
{ | |
// 判断需要代理的方法 | |
return ((Boolean)this.h.invoke(this, m1, new Object[] {paramObject})).booleanValue();} | |
catch (Error|RuntimeException localError) | |
{throw localError;} | |
catch (Throwable localThrowable) | |
{throw new UndeclaredThrowableException(localThrowable); | |
} | |
} | |
public final String toString() | |
throws | |
{ | |
try | |
{return (String)this.h.invoke(this, m2, null); | |
} | |
catch (Error|RuntimeException localError) | |
{throw localError;} | |
catch (Throwable localThrowable) | |
{throw new UndeclaredThrowableException(localThrowable); | |
} | |
} | |
public final int mul(int paramInt1, int paramInt2) | |
throws | |
{ | |
try | |
{ | |
// 处理调用 invoke 方法,this 当前代理对象 | |
return ((Integer)this.h.invoke(this, m4, new Object[] {Integer.valueOf(paramInt1), Integer.valueOf(paramInt2) })).intValue();} | |
catch (Error|RuntimeException localError) | |
{throw localError;} | |
catch (Throwable localThrowable) | |
{throw new UndeclaredThrowableException(localThrowable); | |
} | |
} | |
public final int add(int paramInt1, int paramInt2) | |
throws | |
{ | |
try | |
{return ((Integer)this.h.invoke(this, m3, new Object[] {Integer.valueOf(paramInt1), Integer.valueOf(paramInt2) })).intValue();} | |
catch (Error|RuntimeException localError) | |
{throw localError;} | |
catch (Throwable localThrowable) | |
{throw new UndeclaredThrowableException(localThrowable); | |
} | |
} | |
public final int hashCode() | |
throws | |
{ | |
try | |
{return ((Integer)this.h.invoke(this, m0, null)).intValue();} | |
catch (Error|RuntimeException localError) | |
{throw localError;} | |
catch (Throwable localThrowable) | |
{throw new UndeclaredThrowableException(localThrowable); | |
} | |
} | |
static | |
{ | |
try | |
{ | |
// 判断 是否是对象的方法 | |
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {Class.forName("java.lang.Object") }); | |
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); | |
m4 = Class.forName("com.example.demo.service.Arithmetic").getMethod("mul", new Class[] {Integer.TYPE, Integer.TYPE}); | |
m3 = Class.forName("com.example.demo.service.Arithmetic").getMethod("add", new Class[] {Integer.TYPE, Integer.TYPE}); | |
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); | |
return; | |
} | |
catch (NoSuchMethodException localNoSuchMethodException) | |
{throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); | |
} | |
catch (ClassNotFoundException localClassNotFoundException) | |
{throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); | |
} | |
} | |
} |
通过代理对象源码可以看到
初始化 h
public $Proxy0(InvocationHandler paramInvocationHandler) | |
throws | |
{ | |
// 调用父类 Proxy 设置 InvocationHandler 代理对象处理器,设置调用代理方法 h | |
super(paramInvocationHandler); | |
} |
底层调用
return ((Integer)this.h.invoke(this, m3, new Object[] {Integer.valueOf(paramInt1),
这行代码实际上是通过 invoke 调用当前 原目标方法对象 以及参数,也就是我们写的 MyInvocationHandler 类中 invoke 方法。
关于 InvocationHandler 接口中 invocation 方法中第一个参数 proxy 说明。
proxy 参数表示的是代理对象,那这个参数有何意义呢,什么时候去使用呢,为什么这里没有使用这个参数呢。
看了很多博客感觉 描述不清,众说纷纭。
jdk 中直接这么描述:
the proxy instance that the method was invoked on
一般常用后面两个参数,第一个是代理对象,一般方法中没有用到!
正文完