首页 > 编程知识 正文

第三代代理问题,几种常见的代理模式

时间:2023-05-06 18:03:44 阅读:265378 作者:4775

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);}

实现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; }} 第二步:创建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; }}

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; }} 第三步:创建Proxy类并实例化Proxy对象

获取文章拥有者的代理:

//文章所有者的代理 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; }

通过代理来控制访问权限。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票不能给自己的文章投票!

最后给出Github上的demo代码。

谢谢阅读。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。