您的位置:首页 > 其它

代理模式Proxy深入 动态代理

2016-03-06 01:56 232 查看
前一篇文章中介绍了代理模式的概念和一个基本案例代理模式Proxy入门 之帮你追求心仪的姑娘,接下来继续深入学习代理模式一个变种,动态代理。

首先来回顾下代理概念:为一个对象提供代表以控制该对象的访问。上一文案例中代理仅仅是做到代理访问对象的方法,而动态代理是一种根据访问权限决定客户是否可访问对象的代理

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分。

此时我们动态代理完成了根据权限控制对象访问的目标,好了,完成,哎呀夜已深,好困,小哥去睡了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: