所谓动态,也就是说这个东西是可变的,或者说不是一生下来就有的。提到动态就不得不说静态,静态代理,个人觉得是指一个代理在程序中是事先写好的,不能变的,就像上一篇"Java学习笔记——RMI"中的远程代理,其中客户端服务对象就是一个远程服务对象的代理,这个代理可以使得客户在操作时感觉像在操作本地对象一样,远程对象对于客户是透明的。我们可以看出这里的远程代理,是在程序中事先写好的,而本节我们要讨论的远程代理,是由JVM根据反射机制,在程序运行时动态生成的。(以上是本人的理解,如果有不正确的地方,还望读者指正)

  上面的类图展示了Java动态代理的中类之间的关系。其中Proxy就是由JVM产生的动态代理类,但是我们不能直接在其中定义我们想要的方法,因为这是由JVM自动生成的,而我们无权去修改,既然我们不能去修改Proxy,那么我们怎么才能让代码为我们服务呢?眼睛往右边看一点点,myInvocationHandler,对!我们可以把我们的代码放在这里。实际上我们每次去找代理为我们办事的时候,代理总会把我们的请求交给myInvocationHandler去处理。

  下面以一个例子来详细讲解。本例子摘自《Head First 设计模式》,本人对文章的代码进行少许的修改,并加入了自己的理解说明。

  例子中的要求:假设现在有一个在线交友社区系统,每个人可以在线浏览自己和他人的信息,还可以为他人进行打分,但是用户不能为自己进行打分。每个人都会将自己的基本信息展示出来,这些信息可以被自己修改,但是任何他人不得修改自己的个人信息,情理之中嘛。所以这里我们需要创建两个代理,来分别为自己和他人服务,从而可以实现权限控制的目的,使得每个人都可以执行自己权限以内的操作,而对于权限之外的操作是绝对不允许的。

  下面开始我们的设计:

  首先我们需要定义一个公共接口,这个接口用来被代理和真正的主题所使用,从而可以控制代理和正在的主题具有相同的操作方法。公共接口的代码如下: 

 package pattern.proxy.dynamic;

 /**
* 人物对象接口
* @author CS_Xiaochao
*
*/
public interface Person { /*
* 声明了一系列的方法
*/
String getName();
String getGender();
String getInterests();
double getHotOrNotRating(); //用于获取用户的投票信息 void setName(String name);
void setGender(String gender);
void setInterests(String interests);
void setHotOrNotRating(int rating); //设置用户的投票信息
}

然后我们来实现它,实现代码如下:

 package pattern.proxy.dynamic;

 /**
* 主题
* @author CS_Xiaochao
*
*/
public class PersonImpl implements Person{ private String name;
private String gender;
private String interests;
private int rating;
private int ratingCount = 0; public PersonImpl() {
super();
// TODO Auto-generated constructor stub
} public PersonImpl(String name, String gender, String interests, int rating,
int ratingCount) {
super();
this.name = name;
this.gender = gender;
this.interests = interests;
this.rating = rating;
this.ratingCount = ratingCount;
} /**
* 重载了toString方法,方便打印信息
*/
@Override
public String toString() { return "name:" + name + "\n" +
"gender:" + gender + "\n" +
"interests:" + interests + "\n" +
"rating:" + rating + "\n" +
"ratingCount:" + ratingCount;
} public double getHotOrNotRating() {
if (ratingCount == 0) {
return 0;
} else {
return (double)rating / ratingCount;
}
} public void setHotOrNotRating(int rating) {
this.rating = rating;
ratingCount++;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getGender() {
return gender;
} public void setGender(String gender) {
this.gender = gender;
} public String getInterests() {
return interests;
} public void setInterests(String interests) {
this.interests = interests;
} public int getRating() {
return rating;
} public void setRating(int rating) {
this.rating = rating;
} public int getRatingCount() {
return ratingCount;
} public void setRatingCount(int ratingCount) {
this.ratingCount = ratingCount;
}
}

  真正的干货来啦,让我们开始为Person创建动态代理。之前说过,我们需要两个代理来为我们服务,一个是为自己服务的OwnerInvocationHandler,这个是真正为代理做实际工作的对象,由前面类图中我们可以看到,当用户请求代理为我们服务时,代理实际上是把用户请求转交给了InvocationHandler,对于这里就是OwnerInvocationHandler,它控制用户自己可以修改、查看个人信息,但是绝对不允许为自己投票。具体代码如下:

 package pattern.proxy.dynamic;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; /**
* 服务于自己的做实际工作的代理对象
* @author CS_Xiaochao
*
*/
public class OwnerInvocationHandler implements InvocationHandler { private Person person; public OwnerInvocationHandler() {
super();
// TODO Auto-generated constructor stub
} public OwnerInvocationHandler(Person person) {
super();
this.person = person;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {
Object result = null;
try{
if(method.getName().equals("setHotOrNotRating")){
//对于自己,是不允许为自己投票的
throw new IllegalAccessException();
}else{
//但是自己可以修改、查看自己的个人信息
result = method.invoke(person, args);
}
}catch(InvocationTargetException e){
e.printStackTrace();
}
return result;
} }

  另一个是为他人服务的对象NonOwnerInvocationHandler,它允许别人查看我的个人信息,并为我投票,但是绝对不能修改我的个人信息。具体代码如下:

 package pattern.proxy.dynamic;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; /**
* 为他人服务的真正做实际工作的对象
* @author CS_Xiaochao
*
*/
public class NonOwnerInvocationHandler implements InvocationHandler { private Person person; public NonOwnerInvocationHandler() {
super();
// TODO Auto-generated constructor stub
} public NonOwnerInvocationHandler(Person person) {
super();
this.person = person;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {
Object result = null;
try{
if(method.getName().equals("setHotOrNotRating")
|| method.getName().startsWith("get")){
//对于他人,可以查看我的个人信息,也可以为我投票
result = method.invoke(person, args);
}else if(method.getName().startsWith("set")){
//但是绝对不可以修改我的个人信息
throw new IllegalAccessException();
}
}catch (InvocationTargetException e) {
e.printStackTrace();
}
return result;
}
}

  最后,我们用一个驱动类来测试我们的代码,具体代码如下:

 package pattern.proxy.dynamic;

 import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map; /**
* 驱动测试类
*/
public class MatchMakingTestDrive { //模拟数据库
private static Map<String, Person> tb_person;
static{
tb_person = new HashMap<String, Person>();
tb_person.put("jim", new PersonImpl("jim", "M", "basketball", 0, 0));
tb_person.put("lucy", new PersonImpl("lucy", "F", "dance", 0, 0));
tb_person.put("lily", new PersonImpl("lily", "F", "sing", 0, 0));
tb_person.put("jone", new PersonImpl("jone", "M", "football", 0, 0));
tb_person.put("kitty", new PersonImpl("kitty", "F", "play piano", 0, 0));
}
/**
* 模拟根据用户名来从数据库库中查找个人信息
* @param name 用户名
* @return
*/
private static Person getPersonInfo(String name){
Person person = null;
if(tb_person != null){
person = tb_person.get(name);
}
return person;
} /**
* 创建一个Person的代理
* @param person
* @return
*/
private static Person getOwnerProxy(Person person){
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
} /**
* 创建一个Person的代理
* @param person
* @return
*/
private static Person getNonOwenerProxy(Person person){
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new NonOwnerInvocationHandler(person));
} /**
* @param args
*/
public static void main(String[] args) {
Person jim = getPersonInfo("jim");
System.out.println(jim.getName() + "的个人信息:\n" + jim.toString());
Person ownerProxy = getOwnerProxy(jim);
ownerProxy.setInterests("LOL");
try{
ownerProxy.setHotOrNotRating(0); //设置自己的投票信息
}catch (Exception e) {
System.err.println("对不起,不允许设置自己的投票信息!");
}
System.out.println(jim.getName() + "的个人信息(修改):\n" + jim.toString()); Person kitty = getPersonInfo("kitty");
System.out.println(kitty.getName() + "的个人信息:\n" + kitty.toString());
Person nonOwnerProxy = getNonOwenerProxy(kitty);
ownerProxy.setInterests("DOTA");
try{
nonOwnerProxy.setHotOrNotRating(5); //设置别人的投票信息
}catch (Exception e) {
System.err.println("对不起,不允许设置自己的投票信息!");
}
System.out.println(kitty.getName() + "的个人信息(修改):\n" + kitty.toString());
} }

  程序的执行结果如下:

 jim的个人信息:
name:jim
gender:M
interests:basketball
rating:0
ratingCount:0
对不起,不允许设置自己的投票信息!
jim的个人信息(修改):
name:jim
gender:M
interests:LOL
rating:0
ratingCount:0
kitty的个人信息:
name:kitty
gender:F
interests:play piano
rating:0
ratingCount:0
kitty的个人信息(修改):
name:kitty
gender:F
interests:play piano
rating:5
ratingCount:1

接下来,我们来大致分析一下动态代理的执行过程:

当我们执行代码:

Person ownerProxy = getOwnerProxy(jim);

的时候,实际上我们是在执行:

private static Person getOwnerProxy(Person person){
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}

上面的代码调用了Proxy类的newProxyInstance方法,其源码如下:

 public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
} /*
* Look up or generate the designated proxy class.
*/
Class cl = getProxyClass(loader, interfaces); /*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}

其本质上是创建了一个代理类的实例,然后我们可以将其当做被代理类来使用,只是中间加入了一些我们事先定义的权限控制。然后我们通过代理来调用主题的相关方法:

ownerProxy.setInterests("LOL");

我们的目的是希望通过代理来执行我们需要的操作,但是代理会接着调用InvocationHandler的invoke方法,在这里也就是:

public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {
Object result = null;
try{
if(method.getName().equals("setHotOrNotRating")){
//对于自己,是不允许为自己投票的
throw new IllegalAccessException();
}else{
//但是自己可以修改、查看自己的个人信息
result = method.invoke(person, args);
}
}catch(InvocationTargetException e){
e.printStackTrace();
}
return result;
}

其中参数中的proxy对应ownerProxy,method对应setInterests,args对应"LOL",这里就是真正的权限控制的地方,invoke中的代码会决定将当前操作拦截,还是转发给真正的主题去处理。这就达到了我们之前所要求的的利用代理来进行权限控制的目的。

Java学习笔记——动态代理的更多相关文章

  1. Java学习笔记--动态代理

    动态代理 1.JDK动态代理 JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy ...

  2. Java学习之动态代理篇

    Java学习之动态代理篇 0x00 前言 在后面的漏洞研究的学习中,必须要会的几个知识点.反射机制和动态代理机制.至于反射的前面已经讲到过了,这里就不做更多的赘述了. 0x01 动态代理 这里先来讲一 ...

  3. java学习之动态代理模式

    package com.gh.dynaproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Metho ...

  4. JAVA学习之动态代理

    JDK1.6中的动态代理 在Java中Java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成一个动态代理对象.JDK提供 ...

  5. Mybatis进阶学习笔记——动态代理方式开发Dao接口、Dao层(推荐第二种)

    1.原始方法开发Dao Dao接口 package cn.sm1234.dao; import java.util.List; import cn.sm1234.domain.Customer; pu ...

  6. java学习笔记13--反射机制与动态代理

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...

  7. Java学习笔记--JDK动态代理

    1.JDK动态代理     JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和 ...

  8. java学习笔记(中级篇)—JDK动态代理

    一.什么是代理模式 相信大家都知道代理商这个概念,在商业中,代理商无处不在.假设你要去买东西,你不可能去找真正的厂家去买,也不可能直接跟厂家提出需求,代理商就是这中间的一桥梁,连接买家和厂商.你要买或 ...

  9. Java笔记--动态代理

    Java动态代理 1.概念 代理: 有时我们并不想直接访问对象A,或者不能直接访问对象A.而是通过访问一个中间对象B,让中间对象B去访问A.这种方式就称为代理. 这里的对象A所属的类就为委托类,或者被 ...

随机推荐

  1. 转 ---- Asp.net mvc项目分页功能

    1.定义一个分页用的Page<T>类 1 /* 使用示例: 2 var pager = new Pager<Article>( 3 this.ControllerContext ...

  2. Windows 8 关闭无线后无法打开WIFI的解决办法

    转:http://blog.sina.com.cn/s/blog_49d02ed101016b4n.html 在使用 Windows 8 的过程中,遇到过几次使用键盘上的功能键(笔者笔记本上的快捷键是 ...

  3. 配置Myeclipse中的项目部署到服务器,报the selected server is enabled, but is not configured properly.

    the selected server is enabled, but is not configured properly. deployment to it will not be permitt ...

  4. ubuntu 安装配置JDK

    总的原则:将压缩包解压至/usr/lib/jdk,设置jdk环境变量并将其修改为系统默认的jdk 1.将jdk-7u5-linux-x64.tar.gz拷贝到/usr/lib/jdk/目录下面,这里如 ...

  5. iOS设备隐藏StateBar

    //隐藏StateBar - (BOOL)prefersStatusBarHidden {     returnYES; }

  6. 不要在精确计算中使用float和double类型

    http://blog.csdn.net/androiddevelop/article/details/8478879 一  问题描述 float和double类型不能用于精确计算,其主要目的是为了科 ...

  7. 关于在VMware上装lFEDORA系统

    VMware虚拟机启动当中某个操作系统的时候,会出现如下提示 Cannot connect virtual device floppy0. No corresponding device is ava ...

  8. eclipse GWT开发环境的离线布置方法

    安装方法http://blog.csdn.net/u011029071/article/details/10143841 用eclipse自动更新安装失败N次,还是得手动来 以Google Plugi ...

  9. Androidannotation使用之@Rest与server交互的JSON数据转换(二)

    开篇 之前的一篇博客:Androidannotation使用之@Rest获取资源及用户登录验证(一):http://blog.csdn.net/nupt123456789/article/detail ...

  10. UML进行Linux内核调试

    http://www.lenky.info/ http://blog.csdn.net/ztz0223/article/details/7874759 http://user-mode-linux.s ...