【设计模式】动态代理
来源:互联网 发布:淘宝跑腿服务 编辑:程序博客网 时间:2024/05/11 15:57
代理模式是设计模式中一个非常重要的模式,代理模式有两个角色,一个是代理类,一个是委托类,委托类也是真正的业务类,两者都有相同的接口;
代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
Spring中的AOP基本原理就是动态代理。
代理模式可以根据代理类创建时期的不同分为两种:
- 静态代理:程序员需要编写特定的源代码,在程序运行前,.class已存在
- 动态代理:在系统运行时,Java反射自动生成
静态代理就不记录了,值得好好学学的是动态代理。学习动态代理首先要学一下Java反射,它的实现常见的有两种:
- JDK提供的InvocationHandler 接口和java.lang.reflect 包中的Proxy类
- Cglib实现的动态代理
动态代理的原理是利用Java反射在运行阶段动态生成任意类型的动态代理类,这不仅简化了程序员的编程工作,也提高了系统的可扩展性,不管我们的业务接口和委托类如何变,代理类都可以不变化。
JDK提供的InvocationHandler 接口和java.lang.reflect 包中的Proxy类
实例:在现在常见的系统中,登录和退出是用户必备的操作之一,下面我就设计这么一个接口,用于处理用户的登录和退出操作,
User.java
public interface User { /** * 登录 * @param name 用户名 * @param pwd 密码 * @return */ public boolean login(String username, String pwd); /** * 退出 */ public void logout(String username);}
实现类如下:
UserImpl.java
public class UserImpl implements User{ @Override public boolean login(String username, String pwd) { // 简化问题,直接登录成功 System.out.println(username+" 登录成功."); return true; } @Override public void logout(String username) { System.out.println(username+" 成功退出."); }}
好了,通过接口我们成功实现了用户的登录和退出,但现在有了一个新功能,我们需要统计一下当前登录后的在线人数,怎么办?除了实现类里的方法加东西外还有其他办法吗?
当然有!下面我们就用动态代理来解决这个问题。
我们给UserImpl类添加一个代理类,真实的登录操作和退出操作还是在UserImpl中进行,但在代理对象中,我们还进行一点其他的操作,比如统计一下登录成功的次数,logout的调用次数,使用一个静态的int变量记录,这不就把在线人数统计出来了吗?
下面就是我们的代理类:
UserDynamicProxy.java
package 结构型模式.代理模式;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 继承动态代理接口的代理类 */public class UserDynamicProxy implements InvocationHandler{ // 在线人数 public static int count = 0; // 委托对象 private Object target; /** * 返回代理对象 * @param target * @return */ @SuppressWarnings("unchecked") public <T> T getProxyInstance(Object target) { // 委托对象,真正的业务对象 this.target = target; // 获取Object类的ClassLoader ClassLoader cl = target.getClass().getClassLoader(); // 获取接口数组 Class<?>[] cs = target.getClass().getInterfaces(); // 获取代理对象并返回 return (T)Proxy.newProxyInstance(cl, cs, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object r = null; // 执行之前 r = method.invoke(target, args); // 判断如果是登录方法 if("login".equals(method.getName())) { if((boolean)r == true) { // 当前在线人数+1 count += 1; } } // 判断如果是退出方法 else if("logout".equals(method.getName())) { // 当前在线人数-1 count -= 1; } showCount(); // 输出在线人数 // 执行之后 return r; } /** * 输出在线人数 */ public void showCount() { System.out.println("当前在线人数:"+count+" 人."); }}
InvocationHandler接口时JDK的,Proxy是java.lang.reflect包中,通过这两者可以实现动态代理。getProxyInstance是返回一个代理对象,通过这个代理对象可以做与原委托对象一样的事,因为他们都是实现的同一接口。
场景类:
Main.java
package 结构型模式.代理模式;public class Main { public static void main(String[] args) { // 真实角色,委托人 User user = new UserImpl(); // 可执行真正的登录退出功能 // 代理类 UserDynamicProxy proxy = new UserDynamicProxy(); // 获取委托对象user的代理对象 User userProxy = proxy.getProxyInstance(user); // 系统运行,用户开始登录退出 userProxy.login("小明", "111"); userProxy.login("小红", "111"); userProxy.login("小刚", "111"); userProxy.logout("小明"); userProxy.logout("小刚"); userProxy.login("小黄", "111"); userProxy.login("小黑", "111"); userProxy.logout("小黄"); userProxy.login("小李", "111"); userProxy.logout("小李"); userProxy.logout("小黄"); userProxy.logout("小红"); }}
输出结果如下:
小明 登录成功.当前在线人数:1 人.小红 登录成功.当前在线人数:2 人.小刚 登录成功.当前在线人数:3 人.小明 成功退出.当前在线人数:2 人.小刚 成功退出.当前在线人数:1 人.小黄 登录成功.当前在线人数:2 人.小黑 登录成功.当前在线人数:3 人.小黄 成功退出.当前在线人数:2 人.小李 登录成功.当前在线人数:3 人.小李 成功退出.当前在线人数:2 人.小黄 成功退出.当前在线人数:1 人.小红 成功退出.当前在线人数:0 人.
Cglib实现的动态代理
上面有一句话是“代理对象和委托对象继承的是同一接口”,这表明了JDK实现的动态代理需要委托对象必须是继承了接口的,那如果我们的委托类就是一个类,没有继承哪个接口怎么办? 这个时候我们就要使用Cglib来实现动态代理。
和上面的问题一样,我们现在有一个业务类,它没有继承接口,
UserMange.java
package 结构型模式.代理模式;public class UserManage { public boolean login(String username, String pwd) { // 简化问题,直接登录成功 System.out.println(username+" 登录成功."); return true; } public void logout(String username) { System.out.println(username+" 成功退出."); }}
没有继承接口,JDK的动态代理就实现不了了,下面我们就使用Cglib来实现。Cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
cglib需要在网上先下载一下jar包(cglib和asm),这是我上传的两个包的下载地址
UserCglibProxy.java
package 结构型模式.代理模式;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class UserCglibProxy implements MethodInterceptor { // 在线人数 public static int count = 0; // 委托类对象 private Object target; /** * 创建代理对象 * @param target * @return */ @SuppressWarnings("unchecked") public <T>T getProxyInstance(Object target) { this.target = target; // 增强类对象 Enhancer enhancer = new Enhancer(); // 设置其超类为target的类类型 enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return (T)enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object r = null; // 执行之前 r = proxy.invokeSuper(obj, args); // 判断如果是登录方法 if("login".equals(method.getName())) { if((boolean)r == true) { // 当前在线人数+1 count += 1; } } // 判断如果是退出方法 else if("logout".equals(method.getName())) { // 当前在线人数-1 count -= 1; } showCount(); // 输出在线人数 // 执行之后 return r; } /** * 输出在线人数 */ public void showCount() { System.out.println("当前在线人数:"+count+" 人."); }}
场景类:
Main2.java
package 结构型模式.代理模式;public class Main2 { public static void main(String[] args) { // 委托类对象 UserManage userManage = new UserManage(); // 代理类 UserCglibProxy proxy = new UserCglibProxy(); // 获取委托对象user的代理对象 UserManage userProxy = proxy.getProxyInstance(userManage); // 系统运行,用户开始登录退出 userProxy.login("小明", "111"); userProxy.login("小红", "111"); userProxy.login("小刚", "111"); userProxy.logout("小明"); userProxy.logout("小刚"); userProxy.login("小黄", "111"); userProxy.login("小黑", "111"); userProxy.logout("小黄"); userProxy.login("小李", "111"); userProxy.logout("小李"); userProxy.logout("小黄"); userProxy.logout("小红"); }}
附录
- cglib.jar和asm-3.3.1.jar下载地址
- asm包Maven下载地址
- cglib包Maven下载地址
- 设计模式-动态代理
- 动态代理设计模式
- [ 设计模式 ] 动态代理!
- 动态代理设计模式
- 动态代理设计模式
- 动态代理设计模式
- 【设计模式】动态代理
- 设计模式 - 动态代理
- 动态代理设计模式
- 动态代理设计模式
- 设计模式-动态代理
- 【设计模式】动态代理模式
- 设计模式--动态代理模式
- 【设计模式】动态代理模式
- 设计模式-动态代理模式
- 设计模式-动态代理模式
- [设计模式]动态代理模式
- 设计模式(代理模式--动态代理)
- 第二节--中值滤波VC++实现
- Windows下安装苹果iPhone开发环境xcode图文教程
- 获取当前时间是今年的第几天
- 什么情况使用 weak 关键字,相比 assign 有什么不同?怎么用 copy 关键字?
- IOS的WebView请求远程html并加载本地资源
- 【设计模式】动态代理
- java.util.Collections方法大全
- Android布局优化
- 第三节--一种改进的中值滤波策略的实现
- Android编码规范
- C++中给类初始化与赋值的区别
- 单例模式
- jdk+tomcat部署脚本
- socket新特性之SO_REUSEPORT