代理模式
Posted jnba
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式相关的知识,希望对你有一定的参考价值。
设计模式-----代理模式
一、定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
说明:例如我想租房子,虽然我可以去统计有哪些房子正在出租,每个房子是什么样的户型,房子的价格等。。。,但我就想租个房子,只想选择租或不租,不想去了解房源信息等一系列事,于是就有了租房中介,他会给你收集好这些信息,你只用选择租或不租就行了。
代理模式包含如下角色:
ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。
RealSubject:真实主题角色,是实现抽象主题接口的类。
Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
二、静态代理和动态代理
我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。
1)静态代理
/** * 抽象主题角色 * @author rdb * */ public interface Sleep { void sleep(); } /** * 真实主题角色 * @author rdb * */ public class SleepImpl implements Sleep { @Override public void sleep() { System.out.println("熟睡中"); } } /** * 代理角色 * @author rdb * */ public class SleepProxy implements Sleep { private Sleep sleep; public SleepProxy(Sleep sleep) { this.sleep = sleep; } @Override public void sleep() { System.out.println("睡觉前要刷牙"); sleep.sleep(); System.out.println("睡醒后要吃早饭"); } } /** * 测试代码 * @author rdb * */ public class ProxyTest { public static void main(String[] args) { Sleep sleep = new SleepImpl(); Sleep sleepProxy = new SleepProxy(sleep); sleepProxy.sleep(); } } 结果:睡觉前要刷牙 熟睡中 睡醒后要吃早饭
2)动态代理(JDK)
静态代理有一个明显的缺点:一个主题类和一个代理类一一对应,所以我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。 在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建。
/** * 抽象主题角色 * @author rdb * */ public interface Sleep { void sleep(); } /** * 真实主题角色 * @author rdb * */ public class SleepImpl implements Sleep { @Override public void sleep() { System.out.println("熟睡中"); } } /** * 动态处理器 * @author rdb * */ public class DynamicProxyHandler implements InvocationHandler { private Object obj ; public DynamicProxyHandler(final Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("睡觉前要刷牙"); Object result = method.invoke(obj, args); System.out.println("睡醒后要吃早饭"); return null; } } /** * 测试代码 * @author rdb * */ public class ProxyTest { public static void main(String[] args) { Sleep sleep = new SleepImpl(); DynamicProxyHandler dph = new DynamicProxyHandler(sleep); Sleep sleepProxy = (Sleep) Proxy.newProxyInstance(sleep.getClass().getClassLoader(), sleep.getClass().getInterfaces(), dph); sleepProxy.sleep(); } } 结果:睡觉前要刷牙 熟睡中 睡醒后要吃早饭
注意:Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,动态处理器
JDK动态代理的一般步骤:(1)创建动态处理器实现InvocationHandler接口,实现invoke()方法
(2)创建被代理的类及接口
(3)调用Proxy的静态方法,创建代理类
(4)通过代理类调用方法
JDK生成的代理类本身就继承了Proxy类,Java只允许单继承,所以JDK的动态代理不能完成继承式动态代理(只能完成接口的动态代理,不能实现类的动态代理),但是我们可以用CGLIB的方式实现继承式的动态代理
3)动态代理(CGLIB)
//具体主题 public class Sleep1 { public void sleep() { System.out.println("熟睡中"); } } /** * * @author rdb * */ public class CGLibProxy implements MethodInterceptor { private Object obj; //创建代理类 public Object getInstance(Object obj) { this.obj = obj;//主题对象 Enhancer enhancer = new Enhancer();//创建加强器,用来创建动态代理类 enhancer.setSuperclass(this.obj.getClass()); //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦 enhancer.setCallback(this); //返回代理类 return enhancer.create(); } //回调方法实现 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("睡觉前要刷牙"); Object result = methodProxy.invokeSuper(obj, args); System.out.println("睡醒后要吃早饭"); return result; } } /** * 测试代码 * @author rdb * */ public class ProxyTest { public static void main(String[] args) { Sleep1 sleep = new Sleep1(); CGLibProxy proxy = new CGLibProxy(); Sleep1 s = (Sleep1) proxy.getInstance(sleep); s.sleep(); } } 结果:睡觉前要刷牙 熟睡中 睡醒后要吃早饭
总结:静态代理虽然能完成代理功能,但我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
JDK动态代理很美很强大,但由于java的单继承,只能实现接口的动态代理,不能实现类的动态代理
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
以上是关于代理模式的主要内容,如果未能解决你的问题,请参考以下文章