生活中有很多例子是间接来控制和访问的,比如你找一个人不自己亲自去,而是让别人代替去做这就是最简单的代理模式,是一种间接通信的例子,对象间的间接通信也同样是面向对象设计中的一条重要的“审美观”。间接通信可以让对象间耦合性降低,以及易于复用的架构设计。
间接控制对象的交互是一个重要的编程思想,有很多的模式都体现了这种思想,比如装饰模式、适配器模式、代理模式,都是通过间接的方式实现某一目的。
这里主要介绍一下代理模式,无论是在现实生活中还是计算机技术中用到代理的地方非常多,主要分为静态代理和动态代理。
我们都做过机房收费系统就那这个系统来举例子,这个系统中有对用户操作的用户接口IUser,以及实现了这个接口的类UserImp,Java代码如下。
/**
* 用户表接口
* @author LLS
*
*/
public interface IUser
{
//添加用户
void addUser();
//删除用户
void delUser();
}
用户实现类
/**
* 实现用户接口类
* @author LLS
*
*/
public class UserImpl implements IUser
{
public void addUser() {
// 添加用户代码
}
public void delUser() {
// 删除用户代码
}
}
在这个例子中,我们可能需要在添加用户或者删除用户的时候进行权限检查,符合权限的才能执行相关动作,否则不能执行,那么该如何修改代码才能更加贴切,而且在实际的编写过程中,虽然我们需要权限模块,但有时候为了更好地快速测试,我们常常希望暂时关闭权限模块,如何才能让这样的临时需求变得更加容易处理呢?我们现在使用代理模式来完成这样的任务,现在继续编写一个类叫 UserImplProxy.
用户代理类
/**
* 用户实现类的代理
* @author LLS
*
*/
public class UserImplProxy implements IUser
{
//对用户实现类的引用
private UserImpl userImpl;
//添加用户
public void addUser()
{
//调用添加之前进行权限验证
preIdentify();
if( userImpl == null )
{
userImpl = new UserImpl();
}
//调用源对象的添加
userImpl.addUser();
//添加完后,执行操作
postIdentify();
}
public void delUser()
{
preIdentify();
if( userImpl == null )
{
userImpl = new UserImpl();
}
userImpl.addUser();
postIdentify();
}
//验证方法
private void preIdentify()
{
System.out.println("添加之前验证代码!");
}
//验证
private void postIdentify()
{
System.out.println("添加后执行操作");
}
}
这样就可以很容易的实现权限验证功能,很灵活。
但是问题又出现了,如果还有IStudent、ICard、IOnline……等很多接口,也许要同样的权限验证,是不是还要再为每一个接口都写一个代理类吗?
当然不是了,这个时候就需要用到动态代理了,动态代理模式可以在程序运行时为很多类做代理。
静态代理不足:一个被代理类对应一个代理类,当被代理类增多时,代理类会变多从而增加系统耦合度。
为了提高类的复用性和系统设计灵活性,使得代码更简洁,可以提取高层抽象类或接口。
Java提供了一个接口Java.lang.reflect.InvocationHandler和Proxy类支持动态代理,首先,介绍一下Proxy类,它有一个方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) ,这个方法返回一个代理类的对象。
ClassLoader loader:指定被代理对象的类加载器。
Class[] interfaces: 指定被代理对象所实现的接口。
InvocationHandler h:指定需要调用的InvocationHandler对象。
Java.lang.reflect.InvocationHandler接口,它只有一个方法invoke(),为代理类的抽象方法。有三个参数
Object proxy :代理类对象
Method method :被代理对象的方法
Object[] args :该方法的参数数组
JDK中实现原理
1.产生代理类$Proxy0类
执行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
将产生$Proxy0类,它继承Proxy对象,并根据第二个参数,实现了被代理类的所有接口,自然就可以生成接口要实现的所有方法了(这时候会重写hashcode,toString和equals三个方法),但是还没有具体的实现体;
2.将代理类$Proxy0类加载到JVM中
这时候是根据Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一个参数----就是被代理类的类加载器,把当前的代理类加载到JVM中;
3.创建代理类$Proxy0类的对象
调用的$Proxy0类的$Proxy0(InvocationHandler)构造函数,生成$Proxy0类的对象。参数就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三个参数。这个参数就是我们自己实现的InvocationHandler对象,我们知道InvocationHandler对象中组合加入了代理类所代理的接口类的实现类;所以,$Proxy0对象调用所有要实现的接口的方法,都会调用InvocationHandler对象的invoke()方法实现。
我们增加ICard接口和Card类
package com.proxy; /** * 卡接口 * @author LLS * */ public interface ICard { /** * 注册卡号 */ public void registerCard(); /** * 注销卡号 */ public void cancelCard(); }
Card实现类
package com.proxy;
public class CardImpl implements ICard {
@Override
public void registerCard() {
System.out.println("卡注册类");
}
@Override
public void cancelCard() {
System.out.println("卡取消类");
}
}
动态代理类DynamicProxy
package com.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态生成代理类
* @author LLS
*
*/
public class DynamicProxy implements InvocationHandler {
/**
* 对要代理对象的引用
*/
private Object object=null;
/**
* 给引用复制
* @param object
*/
public DynamicProxy(Object object)
{
this.object=object;
}
/**
*
* @return
*/
public Object newProxyInstance(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
}
/**
* 通过代理对象,执行被代理对象的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//进行权限验证
System.out.println("执行操作之前先进行权限验证,如果权限符合,则执行操作!");
Object res = method.invoke(object, args);
return res;
}
}
客户端测试类
package com.proxy;
/**
* 客户端类
* @author LLS
*
*/
public class Client
{
static public void main(String[] args)
{
//实例化被代理对象
UserImpl userImpl=new UserImpl();
//实例化一个产生代理对象的类
DynamicProxy dynamicProxyUser=new DynamicProxy(userImpl);
//得到代理类对象
IUser proxyUser=(IUser)dynamicProxyUser.newProxyInstance();
//通过代理调用用户添加方法
proxyUser.addUser();
//同上
CardImpl cardImpl=new CardImpl();
DynamicProxy dynamicProxyCard=new DynamicProxy(cardImpl);
ICard proxyCard=(ICard)dynamicProxyCard.newProxyInstance();
//通过代理调用,注册卡号方法
proxyCard.registerCard();
}
}
运行结果为:
动态代理有点像多态一样,可以在程序运行时决定实例化哪一个对象,多态是利用接口向上转型来实现,觉得也可以用反射来实现,Java中的反射是一重要机制,是很多问题变得灵活,如果对反射原理比较熟悉,那么理解很多别的东西也会容易理解一些。