代理模式——保护代理(三)

举报
yd_221104950 发表于 2020/12/03 00:59:20 2020/12/03
【摘要】 Java在java.lang.reflect包中有自己的代理支持,利用这个包我们可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类(继承了InvocationHandler的处理器类),因为实际的代理类是在运行时创建的,我们称这个java技术为:动态代理。在代码开始执行时,还没有proxy类,它是根据需要从我们传入的接口集创建的。 ...

Java在java.lang.reflect包中有自己的代理支持,利用这个包我们可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类(继承了InvocationHandler的处理器类),因为实际的代理类是在运行时创建的,我们称这个java技术为:动态代理。在代码开始执行时,还没有proxy类,它是根据需要从我们传入的接口集创建的。

从Java1.2开始RMI可以利用reflection API直接将客户调用分派给远程服务,我们不需要真的产生skeleton。

到了Java5,连stub都不需要产生了,因为此时的RMI和动态代理搭配使用,动态代理动态产生stub,远程对象的stub是java.lang.reflect.Proxy实例,连同一个调用处理器,它是自动产生的,用来处理所有把客户的本地调用变成远程调用的细节。所以我们不再需要rmic。客户和远程对象的沟通的一切都在幕后处理掉了。

在本章,我们就利用java的动态代理创建一个保护代理。创建保护代理,我们必须使用Java API的动态代理。保护代理就是保护对象不要直接访问主题。保护代理可以控制在每一种情况下允许哪一种请求。
保护模式的类图:
在这里插入图片描述
类图中的代理包含两个类,一个是Proxy类,另一个是RealInvocationHandler类。其中Proxy类是由Java产生的,而且实现了完整的Subject接口。接口InvocationHandler也是java提供的,RealInvocationHandler实现了InvocationHandler接口,Proxy上的任何方法调用都会被传入此类。RealInvocationHandler控制对象RealSubject方法的访问

我们举一例子来说明保护代理模式。假如我们想控制一个博客的作者不能为自己的文章投票,其他人才可以投票,这样一个场景。我们用保护代理模式控制这两种人可以操作的权限。

第一步:我们设计一个ArticleBean。

接口ArticleBean:

public interface ArticleBean { String getAuthor(); String getGender(); String getArticleName(); int getTicket(); void setAuthor(String author); void setGender(String gender); void setArticleName(String articleName); void setTicket(int ticket);
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

实现ArticleBeanImpl:

package impl;

import inter.ArticleBean;

public class ArticleBeanImpl implements ArticleBean { private String author; private String gender; private String articleName; private int ticket = 0; @Override public String getAuthor() { return author; } @Override public String getGender() { return gender; } @Override public String getArticleName() { return articleName; } @Override public int getTicket() { return ticket; } @Override public void setAuthor(String author) { this.author = author; } @Override public void setGender(String gender) { this.gender = gender; } @Override public void setArticleName(String articleName) { this.articleName = articleName; } @Override public void setTicket(int ticket) { this.ticket = this.ticket + ticket; }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

第二步:创建InvocationHandler

这里要创建两个处理器类。其中一个是OwnerInvocationHandler文章拥有者,在处理器中要阻止它调用setTicket()投票方法;另外一个是NonOwnerInvocationHandler投票人,在它的处理器中要阻止它调用除了setTicket方法外的set方法,这样是为了禁止投票人去修改文章的相关信息,投票人是没有权利这么做的。这些处理器类都要继承Java提供的InvocationHandler接口,实现其中的invoke方法,到时通过代理类Proxy调用的方法都会被传入处理器类中来。处理器类是我们唯一能直接访问到真实主题的地方。
注意:
invoke(Object proxy,Method method,Object[] args)的第一个参数proxy其实是没有用处的,因为它一直都是null,methd.invoke()方法,需要把原来的具体实现类作为参数传递进去,method.invoke(articleBean,args)相当于articleBean.method(args)。

OwnerInvocationHandler文章拥有者:

package impl;

import inter.ArticleBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class OwnerInvocationHandler implements InvocationHandler { private ArticleBean articleBean; public OwnerInvocationHandler(ArticleBean articleBean){ this.articleBean = articleBean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if(method.getName().startsWith("get")){ return method.invoke(articleBean,args); }else if(method.getName().startsWith("setTicket")){ //作者不能投自己票 System.out.println("不能给自己的文章投票!"); throw new IllegalAccessException(); }else if(method.getName().startsWith("set")){ return method.invoke(articleBean,args); } }catch (Exception e){ e.printStackTrace(); } return null; }
}



  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

NonOwnerInvocationHandler投票人:

package impl;


import inter.ArticleBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class NonOwnerInvocationHandler implements InvocationHandler { private ArticleBean article; public NonOwnerInvocationHandler(ArticleBean article){ this.article = article; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try{ if(method.getName().startsWith("get")){ return method.invoke(article,args);//对查询是全部提供的 }else if(method.getName().startsWith("setTicket")){ return method.invoke(article,args); //对投票方法是开放的 }else if(method.getName().startsWith("set")){ throw new IllegalAccessException(); //对于除作者以外的人,其他set方法是关闭的 } }catch (Exception e){ e.printStackTrace(); } return null; }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

第三步:创建Proxy类并实例化Proxy对象

获取文章拥有者的代理:

 //文章所有者的代理 private ArticleBean getOwnerProxy(ArticleBean articleBean){ ArticleBean ownerProxy = (ArticleBean)Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new OwnerInvocationHandler(articleBean)); return ownerProxy; }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

获取投票人的代理:

//投票人的代理 private ArticleBean getNonOwnerProxy(ArticleBean articleBean){ ArticleBean nonOwnerProxy = (ArticleBean) Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new NonOwnerInvocationHandler(articleBean)); return nonOwnerProxy; }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

通过代理来控制访问权限。Proxy代理可以访问的方法就是newProxyInstance方法的第二个参数传入的接口列表,具体调用哪个对象的这些接口就是第一个参数传入的对象,第三个参数是处理器类实例。往后,通过Proxy代理调用的方法都会被传入处理器类中。因此真正访问到真实主题的类就是处理器类。

第四步:测试。

import impl.ArticleBeanImpl;
import impl.NonOwnerInvocationHandler;
import impl.OwnerInvocationHandler;
import inter.ArticleBean;

import java.lang.reflect.Proxy;
public class TestProtectionProxy { public static void main(String[] args) { TestProtectionProxy t = new TestProtectionProxy(); t.test(); } public void test(){ ArticleBean articleBean = getActicle();  //获取一篇文章 ArticleBean ownerProxy = getOwnerProxy(articleBean); //获取文章拥有者的代理 ArticleBean nonOwnerProxy = getNonOwnerProxy(articleBean);获取投票人的代理 //投票人开始投票 try{ nonOwnerProxy.setTicket(1); String articleN = nonOwnerProxy.getArticleName(); int num = nonOwnerProxy.getTicket(); String s = articleN +"已获得:" + num  + "票"; System.out.println(s); }catch (Exception e){ e.printStackTrace(); } //文章拥有者投自己一票,会报错,因为不能投自己的 try{ ownerProxy.setTicket(1); }catch (Exception e){ System.out.println("不能给自己的文章投票!"); } } //文章所有者的代理 private ArticleBean getOwnerProxy(ArticleBean articleBean){ ArticleBean ownerProxy = (ArticleBean)Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new OwnerInvocationHandler(articleBean)); return ownerProxy; } //投票人的代理 private ArticleBean getNonOwnerProxy(ArticleBean articleBean){ ArticleBean nonOwnerProxy   (ArticleBean) Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new NonOwnerInvocationHandler(articleBean)); return nonOwnerProxy; } //测试需要 private ArticleBean getActicle(){ ArticleBean bean = new ArticleBeanImpl(); bean.setArticleName("论人文精神的重要性"); bean.setAuthor("Wongkyunban"); bean.setGender("Boy"); bean.setTicket(0); return bean; }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

测试结果:

论人文精神的重要性已获得:1票
不能给自己的文章投票!


  
 
  • 1
  • 2
  • 3

最后给出Github上的demo代码

谢谢阅读。

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/88790510

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。