1 简述
代理模式的思想在生活中还是很常见。
比如,老王想买美版的苹果机,但他不会英语,出国买不方便,于是就找代购商,购买美版的苹果机。
这个过程中,代购商就是代理对象,老王通过代购商,购买了自己想要的东西。
这就是代理模式的典型体现。
代理模式又分静态代理、动态代理,下面详细来看看。
2 静态代理
public class ProxyDemo1 {
public static void main(String[] args) {
User laoWang = new User("老王");
IPhoneProxy iPhoneProxy = new IPhoneProxy(laoWang);
iPhoneProxy.buy();
}
public static class User implements Buy {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void buy() {
System.out.println(this.name + "想买东西");
}
}
public static class IPhoneProxy implements Buy {
User user;
public IPhoneProxy(User user) {
this.user = user;
}
@Override
public void buy() {
user.buy();
System.out.println("苹果机代购商帮" + user.getName() + "购买苹果机");
}
}
public interface Buy {
public void buy();
}
}
// ---- 打印 ----
老王想买东西
苹果机代购商帮老王购买苹果机
从上述代码可以看出,
IPhoneProxy代理类实现了接口Buy,老王想要买苹果机,他自己无法去国外购买,就通过代理商买到了手机。
这就是静态代理模式的简单运用。
我们更进一步,还可以加入其他代理类,这个老王就可以通过这些代理,买到很多买不到的东西。
public static class ComputerProxy implements Buy {
...
}
...
但这样实现一堆代理类也会造成使用上的不变。如果老王想买苹果机、苹果电脑等等,就需要写多行buy()。
可以改造一下,实现嵌套。
package com.yzeng.proxy;
public class ProxyDemo1 {
public static void main(String[] args) {
User laoWang = new User("老王");
IPhoneProxy iPhoneProxy = new IPhoneProxy(new MacProxy(laoWang));
iPhoneProxy.buy();
}
public static class User implements Buy {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void buy() {
System.out.println(this.name + "想买东西");
}
}
public static class IPhoneProxy implements Buy {
Buy buy;
public IPhoneProxy(Buy buy) {
this.buy = buy;
}
@Override
public void buy() {
buy.buy();
System.out.println("苹果机代购商帮购买苹果机");
}
}
public static class MacProxy implements Buy {
Buy buy;
public MacProxy(Buy buy) {
this.buy = buy;
}
@Override
public void buy() {
buy.buy();
System.out.println("Mac代购商帮购买Mac");
}
}
public interface Buy {
public void buy();
}
}
// ---- 打印 ----
老王想买东西
Mac代购商帮购买Mac
苹果机代购商帮购买苹果机
不知道,各位读者看到上面的代码有没有什么想法?
非常像装饰器模式
这样的嵌套形式貌似还是显得冗余,我们是否可以让代购苹果机的,还能去代购Mac或其他商品?
下面看看动态代理模式。
3 动态代理
利用反射机制在运行时创建代理类。
public static void main(String[] args) {
User laoWang = new User("老王");
BuyIphone buyIphone = (BuyIphone)Proxy.newProxyInstance(User.class.getClassLoader(),
laoWang.getClass().getInterfaces(),
new IPhoneHandler(laoWang));
BuyMac buyMac = (BuyMac)Proxy.newProxyInstance(User.class.getClassLoader(),
laoWang.getClass().getInterfaces(),
new IPhoneHandler(laoWang));
buyIphone.buyIphone();
buyMac.buyMac();
}
public static class User implements BuyIphone, BuyMac {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void buyIphone() {
System.out.println(this.name + "想买iphone");
}
@Override
public void buyMac() {
System.out.println(this.name + "想买Mac");
}
}
public static class IPhoneHandler implements InvocationHandler {
User user;
public IPhoneHandler(User user) {
this.user = user;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o = null;
if (method.getName().endsWith("buyIphone")) {
o = method.invoke(user, args);
System.out.println("苹果机代购商帮购买苹果机");
} else if (method.getName().endsWith("buyMac")) {
o = method.invoke(user, args);
System.out.println("苹果机代购商帮购买Mac");
}
return o;
}
}
public interface BuyIphone {
public void buyIphone();
}
public interface BuyMac {
public void buyMac();
}
通过Proxy.newProxyInstance在运行时生成了代理类。
动态代理的原理也比较简单。我们可以通过配置看到生成的代理类源码。
// 旧版
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 新版
// System.getProperties().put(“jdk.proxy.ProxyGenerator.saveGeneratedFiles”, “true”);
运行上面的Java代码,就可以在com.sun.proxy目录下得到生成的代理类$Proxy0
public final class $Proxy0 extends Proxy implements BuyIphone, BuyMac {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
...
public final void buyIphone() 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 buyMac() throws {
try {
super.h.invoke(this, m4, (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"));
m3 = Class.forName("com.yzeng.proxy.ProxyDemo2$BuyIphone").getMethod("buyIphone");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.yzeng.proxy.ProxyDemo2$BuyMac").getMethod("buyMac");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从源码中可以看到,
super.h.invoke(this, m4, (Object[])null);
就是执行的我们传入的IphoneHandler方法,并通过发射传入了该接口。
4 总结
不管是动态代理还是静态代理,思想上都是在代理“客户”的行为。
另外,有了动态代理,我们就可以在方法执行前,和执行后分别嵌入更多的逻辑。
例如,Spring的AOP就是通过动态代理来实现切面编程的。