最近在用java的动态代理,借此机会,把代理总结下。
1 静态代理
如下所示,接口为IProxy1
public interface IProxy1 {
void proxy1(String str);
}
被代理的类为Proxy1
public class Proxy1 implements IProxy1 {
@Override
public void proxy1(String str) {
System.out.println("Proxy1" + str);
}
}
代理类为ProxyImpl
public class ProxyImpl implements IProxy1 {
private IProxy1 proxy;
public ProxyImpl(IProxy1 proxy) {
this.proxy = proxy;
}
@Override
public void proxy1(String str) {
if (proxy != null) {
proxy.proxy1(str);
} else {
System.out.println("non-object is proxyed");
}
}
}
测试代码如下
public class Client {
public static void main(String[] args) {
ProxyImpl proxy = new ProxyImpl(new Proxy1());
proxy.proxy1("test");
}
}
测试结果:Proxy1test
通过测试可以发现,在静态代理中,代理类与被代理类都需要实现相同的接口。可以想象,如果被代理的类的接口很多,代理类也需要实现大量的接口才能完成代理工作,代码量巨大。
2 动态代理
在静态代理中,代理类和被代理类需要实现相同的接口,最终可能会造成代理和被代理类需要实现大量的接口,有没有一种方式可以避免呢,这正是下面的动态代理。说到动态代理,肯定会有一个疑问,一个是静态代理,一个是动态代理,动和静如何区分,带着这个疑问,我们开始探索。
首先由两个接口:IDynamic1和IDynamic2
interface IDynamic1 {
void say1();
}
public interface IDynamic2 {
void say2();
}
接着是被代理的类:
public class DynamicImpl implements IDynamic1, IDynamic2{
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public void say1() {
System.out.println("DynamicImpl say1");
}
@Override
public void say2() {
System.out.println("DynamicImpl say2");
}
}
接下来就是动态代理中非常关键的部分了—InvocationHandler,InvocationHandler可以从字面上理解为调用处理器,确实是这样,InvocationHandler将对方法的调用委托给被代理的对象。InvocationHandler的定义很简单,就申明了一个接口。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
ok,看下我们自定义的InvocationHandler是如何实现的
public class MyInvocationHandler implements InvocationHandler {
private Object mObject;
public MyInvocationHandler(Object obj) {
mObject = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(mObject, args);
}
}
其实也蛮好理解的,就是传递了一个对象,然后将对方法的调用代理到我们指定的mObject上。看下测试代码:
DynamicImpl d1 = new DynamicImpl();
MyInvocationHandler handler = new MyInvocationHandler(d1);
IDynamic1 d11 = (IDynamic1) Proxy.newProxyInstance(IDynamic1.class.getClassLoader(), new Class[]{IDynamic1.class, IDynamic2.class}, handler);
d11.say1();
测试结果为:DynamicImpl say1
具体原理先不去理会,对比下静态代理,我们可以发现动态代理不需要去生成代理类,直接声明InvocationHandler的实现,然后调用Proxy的newInstance方法便可以产生了一个代理对象去执行被代理类的方法。简单的比较就可以发现,动态代理确实在代码简洁上给了不少亮点,但是需要我们去深入理解下,动态代理是如何实现了,通过Proxy.newInstance为什么就能产生一个代理对象,为什么产生的代理对象就能执行接口里面的方法,被代理的对象是如何被调用的。。。。。。
2.1代理类的产生
既然有代理对象,根据常理可以推断,这其中一定有文章,不对,一定有class文件产生,但是就是Proxy.newProxyInstance这一句话实在是看不出class是如何产生的,好吧,那我就进去看看后面的细节,看看这其中代理类是如何产生的。
进入newProxyInstance
方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
......
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
......
}
我把非主要代码隐藏了,值显示了核心的代码。通过代码流程可以发现,newProxyInstance
的流程实际上就是先创建了代理的Class,然后调用Class的构造函数去创建一个代理对象。逻辑是清晰了,但是getProxyClass0
是如何返回代理的Class呢?在创建代理Class的过程中,有关于proxyCache的逻辑,我们就直接跳过,直接看产生代理Class的逻辑,具体代码在Proxy.java的内部类ProxyClassFactory的apply函数中,看下这个apply函数:
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
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");
}
/*
* 验证所传的参数interfaces中的每一个Class文件确实是接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 确保非public的被代理接口必须在同一个包内,否则会抛出异常
*/
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) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 设置代理类名:proxyPkg + $Proxy + num
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 利用ProxyGenerator的generateProxyClass方法产生代理类的字节码文件
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
/**
*加载字节码文件, 返回Class
*/
try {
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());
}
}
}
重要的代码我已经加了注解,通过代码可以知道生成代理类的过程:验证代理的Class[]数组内必须是接口,验证所有的非public接口必须在同一个包内,生成代理类名称,产生代理类的字节码文件,返回Class。到这里,代理类的Class已经产生了,可以发现,产生代理类的核心代码是ProxyGenerator.generateProxyClass(
,第一个参数为代理类的名称,第二个参数是被代理的接口数组,第三个是产生的代理类的访问标识,默认是public和final的,但是如果代理的接口是非public的,则访问标识为final。就这样一个函数就产生了代理类,太神奇了,
proxyName, interfaces, accessFlags)generateProxyClass
函数的具体实现我没有去看,但是我们可以自己尝试去单独调用下,看看最后产生的class文件是什么样的。
还是使用前面IDynamic1和IDynamic2接口,测试代码如下:
String path = "C:/Users/xxx/Desktop/demo_proxy.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",
new Class[] {IDynamic1.class, IDynamic2.class});
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
生成了demo_proxy.class文件,使用jd-gui打开看下里面的内容,类的具体声明如下:
public final class $Proxy0 extends Proxy implements IDynamic1, IDynamic2
可以看到,生成的class文件继承Proxy,实现了IDynamic1和IDynamic2接口,在其中我们找到了对应的方法,但是生成Class文件只是基础,具体是如何调用的我们接下来继续分析。
2.2代理调用
在第2.1节中,已经知道了class文件的生成,发现生成的class文件实现了我们需要代理的接口,但是具体代理调用时如何实现的呢?继续使用jd-gui查看生成的class文件的内容可以看到class文件中声明了几个类型为Method的变量,分别是m0, m1, m2, m3, m4:
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;
奇怪了,IDynamic1和IDynamic2这两个接口一共就声明了两个方法,为什么会有5个呢?不慌,我们看下这几个方法究竟是哪几个。查看class文件,我们发现有一个static块对m0, m1, m2, m3, m4进行了赋值:
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("com.rock.learn.dproxy.IDynamic2").getMethod("say2", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.rock.learn.dproxy.IDynamic1").getMethod("say1", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
原来如此,m3, m4是被代理的方法,m0是hashCode方法,m1是equal方法,m2是toString方法。其实可以测试,java动态代理默认都会将hashCode,equal,toString方法进行代理,大家可以试下,通过代理对象执行这三个函数返回的结果和直接使用代理对象执行返回的结果是一样的。
这些方法是找到了,但是究竟是如何代理的还是没有弄清楚。那我们就来分析下IDynamic1的say1方法是如何被代理的吧。找打class文件中的say1方法:
public final void say1()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
可以发现核心代码就是this.h.invoke(this, m3, null)
。h
是什么变量呢?我们回到Proxy.newProxyInstance
的函数调用可以发现有这样一句cons.newInstance(new Object[]{h})
, 在生成使用生成的代理Class创建对象时,我们传递了自定义的InvocationHandler,看下生成的class文件的构造函数:
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
调用了父类的构造函数,它的父类是Proxy,我们到Proxy.java看下它的构造函数:
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
我去,果然找到了,是不是感觉要接近终点了,确实,马上就到终点了!!!!!
再看下say1
方法,h就是我们自定义的InvocationHandler,然后执行invoke方法,传入了Method m3,进入invoke方法后就是执行我们自己的逻辑了。over!!!