Java学习笔记——动态代理
所谓动态,也就是说这个东西是可变的,或者说不是一生下来就有的。提到动态就不得不说静态,静态代理,个人觉得是指一个代理在程序中是事先写好的,不能变的,就像上一篇"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学习笔记——动态代理的更多相关文章
- Java学习笔记--动态代理
动态代理 1.JDK动态代理 JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy ...
- Java学习之动态代理篇
Java学习之动态代理篇 0x00 前言 在后面的漏洞研究的学习中,必须要会的几个知识点.反射机制和动态代理机制.至于反射的前面已经讲到过了,这里就不做更多的赘述了. 0x01 动态代理 这里先来讲一 ...
- java学习之动态代理模式
package com.gh.dynaproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Metho ...
- JAVA学习之动态代理
JDK1.6中的动态代理 在Java中Java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成一个动态代理对象.JDK提供 ...
- Mybatis进阶学习笔记——动态代理方式开发Dao接口、Dao层(推荐第二种)
1.原始方法开发Dao Dao接口 package cn.sm1234.dao; import java.util.List; import cn.sm1234.domain.Customer; pu ...
- java学习笔记13--反射机制与动态代理
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...
- Java学习笔记--JDK动态代理
1.JDK动态代理 JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和 ...
- java学习笔记(中级篇)—JDK动态代理
一.什么是代理模式 相信大家都知道代理商这个概念,在商业中,代理商无处不在.假设你要去买东西,你不可能去找真正的厂家去买,也不可能直接跟厂家提出需求,代理商就是这中间的一桥梁,连接买家和厂商.你要买或 ...
- Java笔记--动态代理
Java动态代理 1.概念 代理: 有时我们并不想直接访问对象A,或者不能直接访问对象A.而是通过访问一个中间对象B,让中间对象B去访问A.这种方式就称为代理. 这里的对象A所属的类就为委托类,或者被 ...
随机推荐
- Windows下父进程监视子进程状态
最近研究自动化测试,需要获取程序的运行状态及结果,下面是些参考资料. 原文地址:http://blog.csdn.net/ariesjzj/article/details/7226443 Linux下 ...
- JVM性能调优-GC内存模型及垃圾收集算法
JVM内存管理模型: http://developer.51cto.com/art/201002/184385.htm 一 JVM内存模型 1.1 Java栈 Java栈是与每一个线程关联的,JVM在 ...
- 安装 PLSQL笔记
安装 PLSQL Developer 1. 下载PLSQL Developer 安装文件.安装文件类型是exe,直接安装就可以. 这个时候打开PLSQL Developer ,打开 help –> ...
- Java GC专家系列4:Apache的MaxClients设置及其对Tomcat Full GC的影响
本文是GC专家系列中的第四篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.所以,你应该已经了解了JDK 7中的5种GC类型,以及每种GC ...
- 全国各城市Uber客服联系方式(电话、邮箱、微博)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- oracle sysdba用户远程登录
sysdba远程登录需要两个条件: 1.remote_login_passwordfile =exclusive时,启用口令文件,允许远程登录: 查看remote_login_passwordfile ...
- XML认识
XML概念 XML是eXtensible Markup Langguage 缩写,称之为可扩展标记语言.XML 被设计用来传输和存储数据.与HTML不同的是: HTML被设计用来显示数据,其焦点是数据 ...
- Android中的FrameLayout帧布局
帧布局由FrameLayout所代表,FrameLayout直接继承了ViewGoup组件. 帧布局容器为每一个增加当中的组件创建一个空白的区域(称为一个帧),每一个子组件占领一帧,这些帧都会依据gr ...
- Android 颜色渲染(九) PorterDuff及Xfermode详解
版权声明:本文为博主原创文章,未经博主允许不得转载. Android 颜色渲染(九) PorterDuff及Xfermode详解 之前已经讲过了除ComposeShader之外Shader的全部子类 ...
- phpmailer邮件类下载(转)
PHPMailer是一个用于发送电子邮件的PHP函数包.它提供的功能包括:*.在发送邮时指定多个收件人,抄送地址,暗送地址和回复地址*.支持多种邮件编码包括:8bit,base64,binary和qu ...