一直没有养成写博客的习惯,后来觉得还是有必要的。一来可以总结和记录,二来我觉得学习知识是一个转换的过程,最终变成自己的理解,能深入浅出的表述出来。这也是我需要不断锻炼的地方。
代理是一种设计模式有很多种:
1、静态代理 (类似于装饰者模式)。
2、jdk代理 (利用java的发射技术)。
3、cglib代理。
前两种是面向接口的,必须实现一个或多个接口。cglib可以对单个类实现代理。这里我主要讲的是jdk动态代理。jdk的动态代理主要用到了两个东西Proxy,InvocationHandler。我把图贴出来先混个脸熟。
Proxy:里面的newProxyInstance()的静态方法就是用来程序运行时生成代理类的。
InvocationHandler:它是一个接口{从代理类对象方法到被代理类(目标类)方法的调用就是通过这个接口}里面只有一个invoke方法,需要我们自己实现。
下面来走一下动态代理的过程,体会一下。
这是目录结构:
一、首先我们需要一个接口
//动态代理是面向接口的
public interface MyProxy {
public void sayHello();
}
二、对接口进行实现
//这个就是被代理类或者叫目标类
public class MyProxyImpl implements MyProxy {
public void sayHello() {
System.out.println("hello");
}
}
三、对InvocationHandler调用处理器进行实现
//调用处理器
public class MyInvokeHandler implements InvocationHandler {
//目标类的对象
MyProxy mpi;
//构造中对目标类的对象进行初始化
public MyInvokeHandler(MyProxy mpi){
this.mpi = mpi;
}
/**
*
* proxy:生成的代理类对象--这个参数貌似没用到,目前我还不知道有什么用处
* method:目标类方法的对象
* args:目标类方法参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用目标类对象方法之前....");
//这里就是利用了java的反射技术来进行
Object obj = method.invoke(mpi, args);
System.out.println("调用目标类对象方法之后....");
return obj;
}
}
四:用户端:生成代理对象并且进行方法调用
//用户端:生成代理对象并且进行方法调用
public class ProxyTest {
public static void main(String[] args) {
//创建需要被代理的目标类对象
MyProxy myProxy = new MyProxyImpl();
//得到目标类对象的类信息
Class<?> clazz = myProxy.getClass();
//创建调用处理器,把目标类对象传入
InvocationHandler ih = new MyInvokeHandler(myProxy);
/**
* 生成代理类
* 参数1:类加载器
* 参数2:目标类对象实现的接口数组
* 参数3:调用处理器
*/
MyProxy proxy = (MyProxy) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), ih);
//调用代理类对象方法。
proxy.sayHello();
}
}
五:输出结果
从输出结果可以看到,由于对目标类MyProxyImpl进行了代理,在打印hello前后各打印了一句话 结果预期。
但是在刚开始的时候有两个问题不解:
1、就是MyInvokeHandler对象是怎么调用?
2、这里我一直感觉怪怪的,从代理类对象的sayHello()到目标类对象的sayHello()是怎么做到的?
--带着这两个问题只要看看我们生成的代理类就茅塞顿开了,下面是生成的代理类。
/**
* 生成的代理类
* 1、继承了Proxy。
* 2、跟目标类一样,也实现了MyProxy接口。
*
*/
public final class $Proxy0 extends Proxy implements MyProxy {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
//通过反射得到目标类sayHello方法对象
m3 = Class.forName("***.MyProxyImpl").getMethod("sayHello",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
} //static
//构造中调用了父类构造并对父类对象中InvocationHandler属性赋值
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
//生成的代理类方法
public final void sayHello() {
try {
//这里调用父类的InvocationHandler对象的invoke方法
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
这个生成的代理类完全解释我上面的疑惑。
通过调用newProxyInstance()系统会生成$Proxy0类,并且继承Proxy和实现MyProxy。再通过$Proxy0类的构造方法调用父类构造对InvocationHandler属性赋值,这样就可以在$Proxy0类的sayHello方法中调用InvocationHandler对象的invoke()。
生成的$Proxy0类中也对equals、hashCode、toString进行了重写。
大概过程就是这样,可能有些地方讲的不清楚,自己还多回头多看看。