本笔记内容:
1. Java动态代理,反射机制
2. 保护代理
3. 应用保护代理实现的约会系统
----------------------------------------------------------------------------
走进保护模式
我们知道,相亲节目已经在天朝的电视上已经火了多年了,说明了有广大同胞需要这个配对服务,更别说咱们程序猿这个群体了。
现在要实现一个约会服务系统。你希望在这个服务系统中,增加一个评分功能,类似于豆瓣上的评分系统。
浏览一个人资料后,可以给对方进行评分。
你希望这套系统能帮助顾客找到一个理想的对象,这也会让事情更有趣。
很快,你应该就能想到服务系统需要一个PersonBean的接口,来设置或取得一个人的信息:
- //这个接口可以设置和取得人的名字,性别,兴趣,和评分
- public interface PersonBean {
- String getName(); // 获取名字
- String getGender(); // 过去性别
- String getInterests(); // 获取兴趣
- int getHotOrNotRating(); // 获取评分值
- void setName(String name); // 设置姓名
- void setGender(String gender); // 设置性别
- void setInterests(String interests); // 设置兴趣
- void setHotOrNotRating(int rating); // 设置评分
- }
然后实现这个接口:
- public class PersonBeanImpl implements PersonBean {
- String name;
- String gender;
- String interests;
- int rating;
- int ratingCount = 0;
- public String getName() {
- return name;
- }
- public String getGender() {
- return gender;
- }
- public String getInterests() {
- return interests;
- }
- public int getHotOrNotRating() {
- if (ratingCount == 0) return 0;
- return (rating/ratingCount);
- }
- public void setName(String name) {
- this.name = name;
- }
- public void setGender(String gender) {
- this.gender = gender;
- }
- public void setInterests(String interests) {
- this.interests = interests;
- }
- public void setHotOrNotRating(int rating) {
- this.rating += rating;
- ratingCount++;
- }
- }
想想如果系统直接使用这个类会有什么问题?
如果真的直接用这个类,而不考虑其他题,那么估计很快就会大量用户抱怨:我的兴趣竟然被别人篡改了!!有人竟然给自己评高分!!太不公平了!!
没错,在这个系统中,应该不允许用户篡改别人的数据,如果直接按照上面的类定义,任何客户都可以调用任何方法了。
在这个例子中,使用保护代理是最合适不过了。
什么是保护代理?是提供某些级别的保护,根据客户的权限和决定客户可否访问哪些特定的方法,所以保护代理可能只提供给客户部分接口。
比如在我们的约会系统中,我们之希望顾客可以设置自己的信息,同时又防止他人更改这些信息。而HotOrNot评分则相反,你不可以更改自己的评分,但是他人可以设置你的评分。
Java动态代理
Java在java.lang.reflect(反射)包中有自己的代理支持,利用这个包你可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。因为实际的代理类是在运行时创建的,所以我们称这个Java技术为:动态代理。
Invocation的工作原理是响应代理的任何调用,可以看成是代理收到方法调用后,请求做实际工作的对象。
我们要用Java的动态代理创建这个约会系统的保护代理。
下面来看怎样用Java动态代理实现保护模式。
实现保护代理的约会系统
顾客不可以改变自己的HotOrNot评分,也不可以改变其他顾客的个人信息。为了达到这个目标,必须要创建两个代理:一个用来访问你自己的PersonBean对象,另一个用来访问另一个顾客的PersonBean对象。这样,代理就可以控制在每一种情况下允许哪一种请求了。
创建这个代理,就必须使用Java API的动态代理。Java会为我们创建两个代理,而我们只需要提供handler来处理代理转来的方法。
步骤一:创建两个InvocationHandler
InvocationHandler实现了代理的行为,Java负责创建真实代理类和对象。我们只需要提供在方法调用发生时知道做什么的handler。我们需要写两个InvocationHandler(调用处理器),其中一个给拥有者使用,另一个给非拥有者使用。究竟什么是InvocationHandler呢?可以这样想:当代理方法被调用时,代理就会把这个调用转发给InvocationHandler,但是这并不是通过调用InvocationHandler的对应方法做到的。
给拥有者使用的InvocationHandler:
- // 拥有者的InvocationHandler
- // InvocationHandler在java.lan.reflect包中
- import java.lang.reflect.*;
- // 所有调用处理器都实现InvocationHandler接口
- public class OwnerInvocationHandler implements InvocationHandler {
- PersonBean person;
- // 将person传入构造器,并保持引用
- public OwnerInvocationHandler(PersonBean person) {
- this.person = person;
- }
- // 每次代理的方法被调用,就会导致代理调用此invoke方法
- public Object invoke(Object proxy, Method method, Object[] args)
- throws IllegalAccessException {
- try {
- if (method.getName().startsWith("get")) {
- // 如果方法是一个getter,就允许调用
- return method.invoke(person, args);
- } else if (method.getName().equals("setHotOrNotRating")) {
- // 否则,如果方法是HotOrNotRating(), 因为不能给自己打分,所以就抛出异常,表示不允许
- throw new IllegalAccessException();
- } else if (method.getName().startsWith("set")) {
- // 如果是setter,可以设置自己的信息,所以就给调用
- return method.invoke(person, args);
- }
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- return null; //其他方法都是未定义的,返回null
- }
- }
给非拥有者使用的InvocationHandler:
- import java.lang.reflect.*;
- public class NonOwnerInvocationHandler implements InvocationHandler {
- PersonBean person;
- public NonOwnerInvocationHandler(PersonBean person) {
- this.person = person;
- }
- public Object invoke(Object proxy, Method method, Object[] args)
- throws IllegalAccessException {
- try {
- if (method.getName().startsWith("get")) {
- // 可以查看其他人的信息
- return method.invoke(person, args);
- } else if (method.getName().equals("setHotOrNotRating")) {
- // 可以给其他人评分
- return method.invoke(person, args);
- } else if (method.getName().startsWith("set")) {
- // 不可以设置别人的信息,所以返回异常
- throw new IllegalAccessException();
- }
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
步骤二:创建Proxy类并实例化Proxy对象
现在,只剩下创建动态Proxy类,并实例化Proxy对象了。让我们开始编写一个以PersonBean为参数,并知道如何为PersonBean对象创建拥有者代理的方法,也就是说,我们要创建一个代理,将它的方法调用转发给 OwnerInvocationHandler。
下面的方法是调用拥有者的方法
- // 此方法需要一个person对象作为参数,然后返回该对象的代理
- // 因为代理和主题有相同的接口,所以我们返回接口PersonBean
- PersonBean getOwnerProxy(PersonBean person) {
- // 此代码创建了代理(代码有点丑)
- // 我们利用Proxy类的静态newProxyInstance方法创建代理对象(Java反射机制)
- return (PersonBean) Proxy.newProxyInstance(
- person.getClass().getClassLoader(), // 将personBean的类载入器当作参数
- person.getClass().getInterfaces(), // 代理需要实现的接口
- new OwnerInvocationHandler(person)); // 调用非拥有者的处理器
- }
下面方法获取非拥有者的方法
- PersonBean getNonOwnerProxy(PersonBean person) {
- return (PersonBean) Proxy.newProxyInstance(
- person.getClass().getClassLoader(),
- person.getClass().getInterfaces(),
- new NonOwnerInvocationHandler(person));
- }
步骤三:测试配对服务系统。利用适当的代理包装任何PersonBean对象。
- import java.lang.reflect.*;
- import java.util.*;
- public class MatchMakingTestDrive {
- // 实例变量, 当作是保存顾客的“数据库”
- Hashtable datingDB = new Hashtable();
- public static void main(String[] args) {
- MatchMakingTestDrive test = new MatchMakingTestDrive();
- test.drive();
- }
- public MatchMakingTestDrive() {
- // 在构造器中初始化数据库
- initializeDatabase();
- }
- public void drive() {
- PersonBean joe = getPersonFromDatabase("Joe Javabean"); //从数据库中取出一个人
- PersonBean ownerProxy = getOwnerProxy(joe); // 创建这个人的拥有者代理
- System.out.println("Name is " + ownerProxy.getName()); // 输出这个人的名字
- ownerProxy.setInterests("bowling, Go"); // 使用拥有者代理来设置自己的兴趣
- System.out.println("Interests set from owner proxy");
- try {
- // 尝试用拥有者代理来给自己评分
- ownerProxy.setHotOrNotRating(10);
- } catch (Exception e) {
- // 如果给自己评分会出错
- System.out.println("Can't set rating from owner proxy");
- }
- System.out.println("Rating is " + ownerProxy.getHotOrNotRating());
- // 创建一个非拥有者的代理
- PersonBean nonOwnerProxy = getNonOwnerProxy(joe);
- System.out.println("Name is " + nonOwnerProxy.getName());
- try {
- // 尝试用非拥有者代理来设置兴趣
- nonOwnerProxy.setInterests("bowling, Go");
- } catch (Exception e) {
- // 不可以给别人设置兴趣
- System.out.println("Can't set interests from non owner proxy");
- }
- // 可以给别人评分
- nonOwnerProxy.setHotOrNotRating(3);
- System.out.println("Rating set from non owner proxy");
- System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating());
- }
- // 此方法需要一个person对象作为参数,然后返回该对象的代理
- // 因为代理和主题有相同的接口,所以我们返回接口PersonBean
- PersonBean getOwnerProxy(PersonBean person) {
- // 此代码创建了代理(代码有点丑)
- // 我们利用Proxy类的静态newProxyInstance方法创建代理对象(Java反射机制)
- return (PersonBean) Proxy.newProxyInstance(
- person.getClass().getClassLoader(), // 将personBean的类载入器当作参数
- person.getClass().getInterfaces(), // 代理需要实现的接口
- new OwnerInvocationHandler(person)); // 调用非拥有者的处理器
- }
- PersonBean getNonOwnerProxy(PersonBean person) {
- return (PersonBean) Proxy.newProxyInstance(
- person.getClass().getClassLoader(),
- person.getClass().getInterfaces(),
- new NonOwnerInvocationHandler(person));
- }
- PersonBean getPersonFromDatabase(String name) {
- return (PersonBean)datingDB.get(name);
- }
- // 初始化“数据库”
- void initializeDatabase() {
- PersonBean joe = new PersonBeanImpl();
- joe.setName("Joe Javabean");
- joe.setInterests("cars, computers, music");
- joe.setHotOrNotRating(7);
- datingDB.put(joe.getName(), joe);
- PersonBean kelly = new PersonBeanImpl();
- kelly.setName("Kelly Klosure");
- kelly.setInterests("ebay, movies, music");
- kelly.setHotOrNotRating(6);
- datingDB.put(kelly.getName(), kelly);
- }
- }
输出:
小结:
动态代理之所以称为动态代理,是因为运行时才将它的类创建出来。代码开始时,还没有proxy类, 它是根据需要从你传入的接口集创建的。
InvocationHandler不是一个proxy,它只是一个帮助proxy的类,proxy会把调用转发给它处理.Proxy本身是利用静态的Proxy.newProxyInstance()方法在运行时动态创建的。
代理模式一共记了三篇的笔记:远程代理,虚拟代理,和保护代理。代理模式的还远远不止这些:
防火墙代理(Firewall Proxy):
控制网络资源的访问,保护主题免于“坏客户”的侵害。常用在公司的防火墙系统
智能引用代理(Smart Reference Proxy):
当主题被引用时,进行额外的动作,例如计算一个对象被引用的次数。
缓存代理(Caching Proxy):
为开销大的运算结果提供暂时存储:它也可以允许多个客户共享结果,以减少计算或网络延迟。常用在Web服务器代理,以及内容管理与出版系统。
同步代理(Synchronizationi Proxy):
在多线程的情况下为主题提供安全的访问。常用在JavaSpaces,为分散式环境内的潜在对象集合提供同步访问控制。
复杂隐藏代理(Complexity Hiding Proxy):
用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Facade Proxy),这不难了解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口.
写入时复制代理(Copy-On-Write Proxy):
用来控制对象的复制,方法是延迟对象的复制,知道客户真的需要为止。这是虚拟代理的变体。
原文:http://blog.csdn.net/shuangde800/article/details/10381495
相关推荐
JAVA的反射机制与动态代理
利用Java的反射与代理实现IOC模式 在Java中,其反射和动态代理机制极其强大,我们可以通过其反 射机制在运行时获取信息。而代理是一种基本的设计模式,它是一种为了提供额外的或不同的操作而插入到真 实对象中的...
本文详尽地论述了Java反射,工厂模式,动态代理模式
Java的反射与代理实现IOC模式,供大家一起参考学习。
java 模式 设计 代理模式 ,动态代理跟反射机制的内容相关
主要讲述Java反射机制与设计模式之一:代理模式的原理与应用;同时详细讲述了Java对代理模式的支持以及Java中动态代理的原理,应用与实践。
几个Java反射和动态代理的小例子。可以学习如何通过Java的反射机制实例化对象、调用对象的方法、操作对象的私有成员变量、改变...可以学习Java的动态代理模式、学习Java工厂模式以及如何将工厂模式与属性文件相结合。
介绍JAVA反射机制,及代理模式,展示相应示例.
Java代理模式模板代码,包含动态代理与静态代理。 静态代理使用了传统的代理类来代理,动态代理中使用了jdk的反射原理进行代理
reflection是一系列的API,用于表示或者处理当前JVM中的类,接口和对象. java.lang.reflect/java.lang.Class 在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
首先,下载此资源要明白什么是反射。 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它...3.反射代理(设计模式) 4.反射数据库操作 5.反射框架 6.反射其他的 希望大家可以给我好评
* 代理模式 * 在开发者的角度来看,创建一个代理对象,提供给用户使用,避免用户直接访问真正的对象 * 在用户角度来看,就是普通的类方法调用 * * 作用 * 1.保护被代理对象 * 2.增强被代理对象 * 3.完全...
NULL 博文链接:https://arne3166.iteye.com/blog/1046340
JAVA反射机制与动态代理综合里面自带了23个类的例子,用代码详细地解说了JAVA反射机制与动态代理原理,此资料对走java这条线的朋友有极大的帮助....
利用java中的反射机制和代理机制,实现IOC模式,有详细的代码和实例
在代理模式中,主要有两种类型:静态代理和动态代理。 1.静态代理: 静态代理是指在编译期间就已经确定代理类和被代理类的关系,需要手动编写代理类。代理类需要实现与被代理类相同的接口,并且在代理类中持有一个被...
JAVA 中的代理模式介绍,课本上一般讲的比较晦涩简单,这个文档讲的比较生动易懂,很有帮助。
主要讲述Java反射机制与设计模式之一:代理模式的原理与应用同时详细讲述了Java对代理模式的支持以及Java中动态代理的原理,应用与实践 本课程要求大家对Java泛型知识有所了解,因为程序代码中大量使用了泛型相关...
java.util.regex 用于匹配字符序列与正则表达式指定模式的类。 java.util.spi java.util 包中类的服务提供者类。 java.util.zip 提供用于读写标准 ZIP 和 GZIP 文件格式的类。 javax.accessibility 定义了用户...