• [置顶] J2EE (九) 静态代理和动态代理--间接“美”


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

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

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

            我们都做过机房收费系统就那这个系统来举例子,这个系统中有对用户操作的用户接口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中的反射是一重要机制,是很多问题变得灵活,如果对反射原理比较熟悉,那么理解很多别的东西也会容易理解一些。

  • 相关阅读:
    LOJ2565. 「SDOI2018」旧试题
    位运算
    Arrays.sort()原理
    LinkedList源码解析
    二维数组排序
    数据结构和算法-五大常用算法:贪心算法
    数据结构和算法-五大常用算法:分支限界法
    数据结构和算法-五大常用算法:分治算法
    数据结构和算法-二分查找
    Arrays.copyOf()&Arrays.copyOfRange()&System.arraycopy
  • 原文地址:https://www.cnblogs.com/lilongsheng/p/3226037.html
Copyright © 2020-2023  润新知