Java动态代理
代理模式,目前应用广泛的一种设计模式之一,最熟悉的莫过于在Spring的AOP了,如此重要的设计模式,在Java中如何使用呢?又是怎么实现的呢?接下来将围绕着这两个问题来进行实验解析。
首先是如何使用?在使用它的动态代理之前,先实现一个简单的静态代理。创建两个类,分别是Person
和PersonProxy
,并让这两个类都实现Walkable
接口,然后我们通过PersonProxy
来代替Person
走路。
定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package top.devonte.stream.proxy;
public interface Walkable { void walk(); }
package top.devonte.stream.proxy;
public class People implements Walkable {
@Override public void walk() { System.out.println("people is walking..."); } }
package top.devonte.stream.proxy;
public class WalkableProxy implements Walkable { private Walkable walkable;
public WalkableProxy(Walkable walkable) { this.walkable = walkable; }
@Override public void walk() { System.out.println("proxy make it walk"); walkable.walk(); System.out.println("proxy make it walk"); } }
package top.devonte.stream.proxy;
public class ProxyTest { public static void main(String[] args) { Walkable walkable = new WalkableProxy(new People()); walkable.walk(); } }
|
上面这段代码的输出结果为:
proxy make it walk
people is walking…
proxy make it walk
可以看到,静态代理实际上就是对被代理对象进行了一个包装。而代理类在程序运行时创建的代理方式被称为动态代理,在Java中,为我们提供了Proxy
类,我们可以通过Proxy
类来创建对应的代理对象。下面来看看在这个动态代理的实验中,能有什么样的收获吧。
首先定义一个Lion类,同样实现了Walkable
接口,然后我们定义一个JdkProxyHandler
类,实现InvocationHanlder
接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package top.devonte.stream.proxy;
public class Lion implements Walkable { @Override public void walk() { System.out.println("lion is walking..."); } }
package top.devonte.stream.proxy;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class JdkProxyHandler implements InvocationHandler {
private Walkable walkable;
public JdkProxyHandler(Walkable walkable) { this.walkable = walkable; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("current invocation is: " + method.getName()); System.out.println("invocationHandler start work..."); Object result = method.invoke(walkable, args); System.out.println("invocationHandler work completed..."); return result; } }
package top.devonte.stream.proxy;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) { Lion lion = new Lion(); InvocationHandler handler = new JdkProxyHandler(lion); Walkable walkable = (Walkable) Proxy.newProxyInstance(Lion.class.getClassLoader(), new Class[]{Walkable.class}, handler); System.out.println("======== walkable.equals(lion) ========"); System.out.println(walkable.equals(lion)); System.out.println("======== walkableProxyClass.getClassLoader() ========"); Class<? extends Walkable> walkableProxyClass = walkable.getClass(); ClassLoader classLoader = walkableProxyClass.getClassLoader(); System.out.println(classLoader); System.out.println("======== walkableProxyClass.getClassLoader() ========"); Method[] methods = walkableProxyClass.getMethods(); for (Method method : methods) { System.out.println(method.getName()); } System.out.println("======== walkable.walk() ========"); walkable.walk(); }
}
|
上述代码的输出结果为:
======== walkable.equals(lion) ========
current invocation is: equals
invocationHandler start work…
invocationHandler work completed…
true
======== walkableProxyClass.getClassLoader() ========
sun.misc.Launcher$AppClassLoader@18b4aac2
======== walkableProxyClass.getMethods() ========
equals
toString
hashCode
walk
isProxyClass
getInvocationHandler
getProxyClass
newProxyInstance
wait
wait
wait
getClass
notify
notifyAll
======== walkable.walk() ========
current invocation is: walk
invocationHandler start work…
lion is walking…
invocationHandler work completed…
将一系列的类属性打印出来后,我们可以得出以下几点:
- 调用对象的
equals
方法时,实际上调用的是JdkProxyHandler
的invoke
方法
- 使用的类加载器为
sun.misc.Launcher$AppClassLoader
- 包含的方法相对于普通的对象多了
isProxyClass
、getInvocationHandler
、getProxyClass
、newProxyInstance
当我们把创建代理对象的代码改成如下形式时,会产生java.lang.IllegalArgumentException: top.devonte.stream.proxy.Lion is not an interface
异常:
1
| Lion walkable = (Lion) Proxy.newProxyInstance(Lion.class.getClassLoader(),new Class[]{Lion.class}, handler);
|
Java的动态代理需要通过接口来实现,如果没有实现相应的接口是没办法创建代理对象的。接着我们通过代理对象来获取代理对象的Class信息,探究代理对象到底是一个怎么样的类。
1 2
| System.out.println("======== walkable.getClass() ========"); System.out.println(walkable.getClass());
|
得到的输出如下:
======== walkable.getClass() ========
class com.sun.proxy.$Proxy0
Java给我们动态生成了一个Proxy类,这个类对象是如何创建出来的呢?即Java是如何实现动态代理的呢?我们从获取类对象的方法开始探究。
执行newProxyInstance
时,会将我们传入的interfaces进行克隆,接着通过类加载器获取代理类对象,如果可以进行代理,则通过反射获取到这个类的构造器,使用newInstance
创建出代理类。
1 2 3 4 5
| final Class<?>[] intfs = interfaces.clone(); ...省略一部分代码... Class<?> cl = getProxyClass0(loader, intfs); ...省略一部分代码... return cons.newInstance(new Object[]{h});
|
分析到这里,我们对Java的动态代理应该有了一个比较深刻的认识,Java通过InvocationHandler
来实现了具体的代理功能,通过反射动态生成一个存在于内存中的Proxy类,最终的调用都是通过这个InvocationHandler
来实现方法的调用。如果想探究生成的代理类到底是一个怎么样的东西,可以通过反射将代理类输出到文件中,然后通过反编译来查看。下面给出将代理类输出到文件中的方法:
1 2 3 4 5 6 7 8 9
| byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Lion.class.getInterfaces()); String path = "LionProxy.class"; try(FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("代理类class文件写入成功"); } catch (Exception e) { System.out.println("写文件错误"); }
|
参考资料:
java动态代理实现与原理详细分析(友好版)
JAVA动态代理(详细)