在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 我们创建具有现有对象的对象,以便向外界提供功能接口。
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层,即增加中间层。
优点:高扩展性。
缺点:客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
代理模式分为静态代理、动态代理。
1. 静态代理
静态代理,代理类在编译时生成;即,需要编码代理类。
代理接口:
package man.kuke.proxy.demo;
public interface Action {
void watchTV();
}
被代理类:
package man.kuke.proxy.demo;
public class Person implements Action{
@Override
public void watchTV() {
System.out.println("you are watching 《the dark world》!");
}
}
静态代理类:
package man.kuke.proxy.demo;
public class StaticProxy implements Action{
private Action action;
public StaticProxy(Action action) {
this.action = action;
}
@Override
public void watchTV() {
actiionBefore();
action.watchTV();
actionAfter();
}
private void actiionBefore() {
System.out.println("you are drink water");
}
private void actionAfter() {
System.out.println("you are shitting");
}
}
2.动态代理
代理类在程序运行时创建的代理方式被成为动态代理。
2.1JDK 代理
public class DynamicProxy {
@SuppressWarnings("unchecked")
public static <T> T getProxy(Object obj,Class<T> klass) {
return (T) Proxy.newProxyInstance(klass.getClassLoader(),new Class[]{klass},new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
actiionBefore();
method.invoke(obj,args);
actionAfter();
return null;
}
});
}
private static void actiionBefore() {
System.out.println("you are drink water");
}
private static void actionAfter() {
System.out.println("you are shitting");
}
}
2.1.1 JDK动态代理
生成代理对象的字节码方法:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
public final class $Proxy0 extends Proxy implements Action {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("man.kuke.proxy.demo.Action").getMethod("watchTV");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
}
可以看到,代理对象通过反射获被代理对象所有的方法。
实现了接口方法:
public final void watchTV() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
这里调用自己定义的invoke方法。逻辑流程与下图图一致
代理对象也重写了:
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 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);
}
}
该方法不可重写,针对执行结果进行不同处理。
2.2 CGLIb代理
public class CGLIBProxy {
@SuppressWarnings("unchecked")
public static <T> T getProxy(final Object object) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(object.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return method.invoke(object, args);
}
});
return (T) enhancer.create();
}
}
3.小结
- 代理模式(Proxy Pattern)中,一个类代表另一个类的功能;在访问此对象时加上一个对此对象的访问层,控制对代理对象的访问。
- 优点:高扩展性。缺点:处理速度慢。
- 静态代理,代理类在编译时生成
- 代理类在程序运行时创建的代理。
- JDK动态代理创建,需要接口,CGLIb动态代理创建不需要接口。
- CGLIb动态代理缺陷:被代理对象final方法不可被重写。
参考:菜鸟教程:代理模式