代理模式
一,静态代理(房产中介)
在代理模式中的角色:
● 抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
● 目标对象角色:定义了代理对象所代表的目标对象。
● 代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。
/** * 抽象角色,真实角色和代理角色都要实现的接口 * @author marcopan */ public interface IAbstractSubject { // 代理对象和真实角色共有的方法,租房() public void rentHouse(); } /** * 真实角色--房东 * @author marcopan * */ public class RealSubject implements IAbstractSubject { @Override public void rentHouse() { System.out.println("rentHouse from RealSubject"); } } package proxy.staticProxy; /** * 代理角色,中介,含有真实角色的引用 * @author marcopan * 代理角色做两件事: * 1. 关联真实角色对象 * 2. 调用真实对象方法 */ public class StaticProxySubject implements IAbstractSubject { // 含有真实角色的引用 private RealSubject realSubject = null; public StaticProxySubject(RealSubject subject) { this.realSubject = subject; } @Override public void rentHouse() { realSubject.rentHouse(); } } public class Client { public static void main(String[] args) { IAbstractSubject proxySubject = new StaticProxySubject(new RealSubject()); proxySubject.rentHouse(); } }
二,动态代理
public class DynamicProxySubject implements InvocationHandler { private ISubject target = null; public DynamicProxySubject(ISubject target) { this.target = target; } public Object getProxyInstance() { Class<?> clazz = target.getClass(); /** * Proxy这个类的作用就是用来动态创建一个代理对象的类 * Proxy.newProxyInstance返回的是一个代理对象:com.sun.proxy.$Proxy0 */ return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } /** * proxy:指被代理的对象,$Proxy0是系统自动生成的实现ISubject接口的代理类,并持有InvocationHandler引用 * method:指代的是我们所要调用真实对象的某个方法的Method对象 * args:指代的是调用真实对象某个方法时接受的参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 增强消息...... return method.invoke(target, args); // 增强消息...... } } /** * // byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IAbstractSubject.class}); * // try { * // String filePath = IAbstractSubject.class.getResource("").getPath(); * // FileOutputStream fos = new FileOutputStream(filePath + "/$Proxy0.class"); * // fos.write(bytes); * // fos.close(); * // } catch (Exception e) { * // e.printStackTrace(); * // } */ public class Client { public static void main(String[] args) { ISubject target = new RealSubject(); ISubject dynamicInstance = (ISubject) new DynamicProxySubject(target).getProxyInstance(); dynamicInstance.requestHouse1(); dynamicInstance.requestHouse2(); } }
三、Cglib动态代理:
public class CglibProxySubject implements MethodInterceptor { public Object getCglibInstance(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // before Object obj = methodProxy.invokeSuper(o, objects); // after return obj; } } public class Client { public static void main(String[] args) { String filePath = ISubject.class.getResource("").getPath(); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, filePath); ISubject target = new RealSubject(); ISubject cglibInstance = (ISubject) new CglibProxySubject().getCglibInstance(target.getClass()); System.out.println(cglibInstance); cglibInstance.requestHouse1(); cglibInstance.requestHouse2(); } }
CGLib 和 JDK 动态代理对比:
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高。