代理模式Proxy深入 动态代理
2016-03-06 01:56
232 查看
前一篇文章中介绍了代理模式的概念和一个基本案例代理模式Proxy入门 之帮你追求心仪的姑娘,接下来继续深入学习代理模式一个变种,动态代理。
首先来回顾下代理概念:为一个对象提供代表以控制该对象的访问。上一文案例中代理仅仅是做到代理访问对象的方法,而动态代理是一种根据访问权限决定客户是否可访问对象的代理。
java动态代理类位于java.lang.reflect包下,API文档说明
InvocationHandler 是代理实例的调用处理程序 实现的接口。
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
定义会员行为,抽取接口
实现会员实体类
会员对自己资料是“拥有者”,我们先实现拥有者对象实例的调用处理程序,首先实现InvocationHandler 接口,复写invoke方法,在invoke方法根据逻辑判断访问权限。此处会员可以访问自己的所有信息,并且可以修改除了评分以外的信息。所以在invoke方法中,当用户尝试修改自己的评分是不允许,则抛出IllegalAccessException,具体见完整代码。
同理,访问其他会员信息的调用处理程序如下:
动态创建代理,实例对象类和 调用处理程序 都定义好了,接下来就定义我们的 动态代理。
利用Proxy的静态方法newProxyInstance创建代理
准备就绪,开始测试吧
观察结果:当我们尝试通过动态代理修改自己的评分时,OwnerInvocationHandler 调用处理程序 invoke方法中,通过反射判断该方法不无权限访问,返回异常,尝试给自己修改为10分,由于不满足可最终结果任然是6分。
此时我们动态代理完成了根据权限控制对象访问的目标,好了,完成,哎呀夜已深,好困,小哥去睡了。
首先来回顾下代理概念:为一个对象提供代表以控制该对象的访问。上一文案例中代理仅仅是做到代理访问对象的方法,而动态代理是一种根据访问权限决定客户是否可访问对象的代理。
UML图
使用Java API的代理,创建一个动态代理(代理保护)
动态代理是在运行时动态创建的一个代理类,它不会做任何实际的工作,会在生成时需要指定一个InvocationHandler对象,当代理对象方法的调用时,通过在InvocationHandler的invoke方法中做实际操作,换言之,InvocationHandler的工作就是响应代理的任何调用。java动态代理类位于java.lang.reflect包下,API文档说明
InvocationHandler 是代理实例的调用处理程序 实现的接口。
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
案例:社交平台会员管理
需求:平台上有多个会员,每个会员需要填写的自己的资料,查看其他人的资料,互相之间可以印象评分,但是不可以给自己评分,也不能修改其他人的资料。定义会员行为,抽取接口
/** 定义会员接口 */ public interface IPersonBean { /** 获取姓名 */ String getName(); /** 获取性别 */ String getGender(); /** 获取兴趣爱好 */ String getInterests(); /** 获取会员评分 */ int getRatting(); void setName(String name); void setGender(String gender); void setInterests(String interests); void setRatting(int ratting); }
实现会员实体类
/** 会员实现类 */ public class PersonBeanImp implements IPersonBean { private String name; private String gender; private String interests; private int ratting; @Override public String getName() { return name; } @Override public String getGender() { return gender; } @Override public String getInterests() { return interests; } @Override public int getRatting() { return ratting; } @Override public void setName(String name) { this.name = name; } @Override public void setGender(String gender) { this.gender = gender; } @Override public void setInterests(String interests) { this.interests = interests; } @Override public void setRatting(int ratting) { this.ratting = ratting; } public PersonBeanImp() { super(); } public PersonBeanImp(String name, String gender, String interests, int ratting) { super(); this.name = name; this.gender = gender; this.interests = interests; this.ratting = ratting; } }
会员对自己资料是“拥有者”,我们先实现拥有者对象实例的调用处理程序,首先实现InvocationHandler 接口,复写invoke方法,在invoke方法根据逻辑判断访问权限。此处会员可以访问自己的所有信息,并且可以修改除了评分以外的信息。所以在invoke方法中,当用户尝试修改自己的评分是不允许,则抛出IllegalAccessException,具体见完整代码。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 拥有者对象实例的调用处理程序 * * InvocationHandler 是代理实例的调用处理程序 实现的接口。 * * 每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 */ public class OwnerInvocationHandler implements InvocationHandler { private IPersonBean personBean; public OwnerInvocationHandler(IPersonBean personBean) { super(); this.personBean = personBean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (method.getName().startsWith("get")) { // 会员可以获得所有 自己的信息 return method.invoke(personBean, args); } else if (method.getName().equals("setRatting")) { // 会员没有权限 修改自己的评分 throw new IllegalAccessException(); } else if (method.getName().startsWith("set")) { // 有权限修改除了评分以外的其他信息 return method.invoke(personBean, args); } } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } }
同理,访问其他会员信息的调用处理程序如下:
/** * 被拥有者对象实例的调用处理程序 * * 可以查看其他会员的所有信息,并且可以打分,但是不允许修改其他个人信息 */ public class NonOnwerInvocationHandler implements InvocationHandler { private IPersonBean personBean; public NonOnwerInvocationHandler(IPersonBean personBean) { super(); this.personBean = personBean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (method.getName().startsWith("get")) { // 可以查看其他会员的所有信息 return method.invoke(personBean, args); } else if (method.getName().equals("setRatting")) { // 可以打分 return method.invoke(personBean, args); } else if (method.getName().startsWith("set")) { // 不允许修改其他个人信息 throw new IllegalAccessException(); } } catch (Exception e) { e.printStackTrace(); } return null; } }
动态创建代理,实例对象类和 调用处理程序 都定义好了,接下来就定义我们的 动态代理。
public IPersonBean getOwnerProxy(IPersonBean personBean) { // 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序 return (IPersonBean) Proxy.newProxyInstance(personBean.getClass() .getClassLoader(), personBean.getClass().getInterfaces(), new OwnerInvocationHandler(personBean)); }
利用Proxy的静态方法newProxyInstance创建代理
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于: Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler }); Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。 参数: loader - 定义代理类的类加载器 interfaces - 代理类要实现的接口列表 h - 指派方法调用的调用处理程序 返回: 一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口 抛出: IllegalArgumentException - 如果违反传递到 getProxyClass 的参数上的任何限制 NullPointerException - 如果 interfaces 数组参数或其任何元素为 null,或如果调用处理程序 h 为 null
准备就绪,开始测试吧
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
DynamicProxyTest dynamicProxyTest = new DynamicProxyTest();
dynamicProxyTest.ownerTest();
dynamicProxyTest.nonOwnerTest();
}
private void ownerTest() {
// 初始化一个 李狗蛋 的会员对象
IPersonBean personBean = new PersonBeanImp("李狗蛋", "男", "吃饭睡觉打豆豆", 6);
IPersonBean ownerProxy = getOwnerProxy(personBean);
System.out.println("name:" + ownerProxy.getName());
System.out.println("gender:" + ownerProxy.getGender());
System.out.println("interests:" + ownerProxy.getInterests());
System.out.println("ratting:" + ownerProxy.getRatting());
System.out.println("----------------修改个人信息----------------");
ownerProxy.setName("李白"); // 狗蛋不好听,改个名
ownerProxy.setInterests("吟诗作对,品美酒");
System.out.println("name:" + ownerProxy.getName());
System.out.println("gender:" + ownerProxy.getGender());
System.out.println("interests:" + ownerProxy.getInterests());
System.out.println("ratting:" + ownerProxy.getRatting());
System.out.println("----------------修改评分----------------");
// 嫌弃其他会员给的分太低了,自己给该高点,来个满分10分吧
try {
ownerProxy.setRatting(10);
} catch (Exception e) {
System.err.println("无权限修改自己的评分");
}
System.out.println("ratting:" + ownerProxy.getRatting());
}
private void nonOwnerTest() {
// 初始化一个 李狗蛋 的会员对象
IPersonBean personBean = new PersonBeanImp("李狗蛋", "男", "吃饭睡觉打豆豆", 6);
IPersonBean nonOwnerProxy = getNonOwnerProxy(personBean);
System.out.println("name:" + nonOwnerProxy.getName());
System.out.println("gender:" + nonOwnerProxy.getGender());
System.out.println("interests:" + nonOwnerProxy.getInterests());
System.out.println("ratting:" + nonOwnerProxy.getRatting());
System.out.println("----------------修改评分----------------");
nonOwnerProxy.setRatting(10);
// 我来给狗蛋打个高分,来个满分10分吧
System.out.println("ratting:" + nonOwnerProxy.getRatting());
System.out.println("----------------修改狗蛋的信息----------------");
nonOwnerProxy.setName("李白"); // 狗蛋不好听,改个名
nonOwnerProxy.setInterests("吟诗作对,品美酒");
System.out.println("name:" + nonOwnerProxy.getName());
System.out.println("interests:" + nonOwnerProxy.getInterests());
}
public IPersonBean getOwnerProxy(IPersonBean personBean) { // 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序 return (IPersonBean) Proxy.newProxyInstance(personBean.getClass() .getClassLoader(), personBean.getClass().getInterfaces(), new OwnerInvocationHandler(personBean)); }
public IPersonBean getNonOwnerProxy(IPersonBean personBean) {
// 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
return (IPersonBean) Proxy.newProxyInstance(personBean.getClass()
.getClassLoader(), personBean.getClass().getInterfaces(),
new NonOnwerInvocationHandler(personBean));
}
}
观察结果:当我们尝试通过动态代理修改自己的评分时,OwnerInvocationHandler 调用处理程序 invoke方法中,通过反射判断该方法不无权限访问,返回异常,尝试给自己修改为10分,由于不满足可最终结果任然是6分。
此时我们动态代理完成了根据权限控制对象访问的目标,好了,完成,哎呀夜已深,好困,小哥去睡了。
相关文章推荐
- FTP(File Transfer Protocol)是什么?
- windows驱动开发推荐书籍
- 随笔 --- 深夜反思、重定向
- IOS uitableview中cell的分割线设置距离,并且去掉cell的边框,要不然多了一条细线
- 实现MDK5.14编译C51
- Linux 软中断后台线程的创建过程及tasklet的使用
- sh -s用法
- 四轴飞行器原理与双闭环PID控制
- Android Studio 各种配置(更新中)
- 快速排序
- Linux curl使用简单介绍 (转)
- [手游新项目历程]第14天-Release-在已损坏了程序内部状态的 WorldServerR.exe 中发生了缓冲区溢出
- 《Linux内核分析》课程第二周学习总结
- 怎样锻炼胸肌?
- 读《大学有感》④
- ubutu14.04选中文本就会删除问题解决
- 最长回文子串-----“马拉车”算法
- ./configure,make,make install的作用(转)
- IOS之UI--动态设置NavigationBar的颜色以及透明度
- 安卓学习笔记(2)