代理模式——保护代理(三)
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
谢谢阅读。
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/88790510
- 点赞
- 收藏
- 关注作者
评论(0)