一、使用代理模式的场景:对已经开发好的类进行功能增强(添加或修改),比如有一个接口:
package com.proxy;
public interface Animal {
void dog();
void cat();
void monkey();
}
以及该接口的一个实现类:
package com.proxy;
public class Dog implements Animal {
@Override
public void dog() {
System.out.println("I am a dog");
}
@Override
public void cat() {
System.out.println("I am a dog, I like cat!");
}
@Override
public void monkey() {
System.out.println("I am a dog, I dont not like monkey!");
}
}
现在要对Dog这个类进行增强,在调用dog、cat和monkey方法前后打印一些日志。可以创建Dog的一个代理类(DogProxy),该代理类持有一个Dog对象。
package com.proxy;
public class DogProxy implements Animal {
private final Dog dog;
public DogProxy(Dog dog) {
this.dog = dog;
}
@Override
public void dog() {
System.out.println("---------------before dog");
dog.dog();
System.out.println("---------------after dog");
}
@Override
public void cat() {
System.out.println("---------------before cat");
dog.cat();
System.out.println("---------------after cat");
}
@Override
public void monkey() {
System.out.println("---------------before monkey");
dog.monkey();
System.out.println("---------------after monkey");
}
}
可以写一个主类,调用DogProxy这个代理类的方法。
package com.proxy;
public class ProxyTest {
public static void main(String[] args) {
DogProxy dogProxy = new DogProxy(new Dog());
dogProxy.dog();
dogProxy.cat();
dogProxy.monkey();
}
}
运行程序输出如下:
上述方法称为静态代理,代理类中的所有代理方法都是由程序员手动写代码完成。
二、为什么要用动态代理?
假设有一个Animal接口的实现类Monkey,代码如下:
package com.proxy;
public class Monkey implements Animal {
@Override
public void dog() {
System.out.println("I am a monkey, I dont not like dog!");
}
@Override
public void cat() {
System.out.println("I am a cat, I like dog!");
}
@Override
public void monkey() {
System.out.println("I am a monkey, I like cat!");
}
}
现在有一个需求,需要在调用Monkey的dog()方法、cat()方法和monkey()方法前后都要打印系统当前时间。可以模仿上一小节中的方法来完成MonkeyProxy。但是有一个问题:假如Animal()接口有10000个方法,而Monkey类实现了Animal接口中的10000个方法,要按照静态代理的方法来写MonkeyProxy类的话,就需要在该类中写10000个代理方法,显然是不合理的。合理的方法是使用jdk 动态代理。
使用jdk动态代理的MonkeyProxy类示例如下:
package com.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
public class MonkeyProxy implements Animal {
private Monkey monkey = new Monkey();
Animal animal = (Animal) Proxy.newProxyInstance(Monkey.class.getClassLoader(), new Class<?>[]{Animal.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----------------" + new Date().toLocaleString());
method.invoke(monkey, args);
System.out.println("-----------------" + new Date().toLocaleString());
return null;
}
});
@Override
public void dog() {
animal.dog();
}
@Override
public void cat() {
animal.cat();
}
@Override
public void monkey() {
animal.monkey();
}
}
该代理类持有一个Monkey类的对象: private Monkey monkey = new Monkey();
同时通过Proxy类(jdk的一个类)生成了一个动态类对象:Animal animal= (Animal) Proxy.newProxyInstance(。。。),该代理类的方法dog()、cat()和monkey()则分别调用这个动态类对象的方法:
animal.dog();
animal.cat();
animal.monkey();
写一个测试类,代码如下:
package com.proxy;
public class ProxyTest {
public static void main(String[] args) {
// 旧版jdk版本,在本地生成动态代理类,否则只存在与内存
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 新版jdk版本
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
MonkeyProxy monkeyProxy = new MonkeyProxy();
monkeyProxy.cat();
monkeyProxy.dog();
monkeyProxy.monkey();
}
}
运行结果如下:
从运行结果可以看出,调用MonkeyProxy类对象的dog()、cat()和monkey()方法时,都调用了该类中定义的动态类对象animal构建时所传入的InvocationHandler中的invoke()方法。这是怎么发生的呢?需要看jdk动态生成的类的源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.proxy.Animal;
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 Animal {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m3;
private static Method m5;
private static Method m0;
public $Proxy0(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 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 void cat() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void monkey() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void dog() throws {
try {
super.h.invoke(this, m5, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.proxy.Animal").getMethod("cat");
m3 = Class.forName("com.proxy.Animal").getMethod("monkey");
m5 = Class.forName("com.proxy.Animal").getMethod("dog");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
该类对象通过构造方法,传入InvocationHandler类对象:
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
该动态类的父类为Proxy,Proxy类的构造方法如下:
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
而h定义如下:
protected InvocationHandler h;
总结一下就是,Proxy0这个动态类对象在构造时会把传入的InvocationHander对象保存到自己的protected类型的变量h中。
再看动态类里的monkey()方法:
public final void monkey() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
核心的一句是:
super.h.invoke(this, m3, (Object[])null);
其中super.h为刚才提到的h。那么m3呢?来看静态代码块里的定义:
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.proxy.Animal").getMethod("cat");
m3 = Class.forName("com.proxy.Animal").getMethod("monkey");
m5 = Class.forName("com.proxy.Animal").getMethod("dog");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
也就是主类中调用eat方法时,最终会调用jdk生成的动态对象的invoke()方法。
invoke(this, Class.forName("com.proxy.Animal").getMethod("monkey");, (Object[])null);
而invoke方法的定义上边已经贴过:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----------------" + new Date().toLocaleString());
method.invoke(monkey, args);
System.out.println("-----------------" + new Date().toLocaleString());
return null;
}
代码比较简单,就是反射,不再详述。