您的位置:首页 > 运维架构 > 网站架构

架构设计之设计模式 (二) 静态代理和动态代理--间接“美”

2013-07-22 00:51 471 查看
生活中有很多例子是间接来控制和访问的,比如你找一个人不自己亲自去,而是让别人代替去做这就是最简单的代理模式,是一种间接通信的例子,对象间的间接通信也同样是面向对象设计中的一条重要的“审美观”。间接通信可以让对象间耦合性降低,以及易于复用的架构设计。

间接控制对象的交互是一个重要的编程思想,有很多的模式都体现了这种思想,比如装饰模式、适配器模式、代理模式,都是通过间接的方式实现某一目的。

这里主要介绍一下代理模式,无论是在现实生活中还是计算机技术中用到代理的地方非常多,主要分为静态代理和动态代理。

我们都做过机房收费系统就那这个系统来举例子,这个系统中有对用户操作的用户接口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中的反射是一重要机制,是很多问题变得灵活,如果对反射原理比较熟悉,那么理解很多别的东西也会容易理解一些。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: