zoukankan      html  css  js  c++  java
  • 第二十六部分_代理模式与动态代理详解

    与IoC类似的是,AOP也使用了一种设计模式,这种设计模式叫做代理模式。

    代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。

    在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

    掌握代理模式对于Spring AOP的学习是至关重要的,甚至比Spring AOP本身的学习还要重要。

    代理模式一般涉及到的角色有:

    • 抽象角色:声明真实对象和代理对象的共同接口
    • 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操纵真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
    • 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

    自己动手实现代理模式:

    抽象角色:Subject

    package com.test.proxy;
    
    public abstract class Subject
    {
    	public abstract void request();
    }
    

    真实角色:RealSubject

    package com.test.proxy;
    
    public class RealSubject extends Subject
    {
    	@Override
    	public void request()
    	{
    		System.out.println("from real subject");
    	}
    }
    

    代理角色:ProxySubject

    package com.test.proxy;
    
    public class ProxySubject extends Subject
    {
    	private RealSubject realSubject;
    	
    	@Override
    	public void request()
    	{
    		this.preRequest();
    		
    		if(null == realSubject)
    			realSubject = new RealSubject();
    		
    		realSubject.request();
    		
    		this.postRequest();
    	}
    	
    	private void preRequest()
    	{
    		System.out.println("pre request");
    	}
    	
    	private void postRequest()
    	{
    		System.out.println("post request");
    	}
    }
    

    客户端:

    package com.test.proxy;
    
    public class Client
    {
    	public static void main(String[] args)
    	{
    		Subject subject = new ProxySubject();
    		
    		subject.request();
    	}
    }
    

    运行Client,输出:

    pre request
    from real subject
    post request
    
    • 由以上代码可以看出,客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理RealSubject类,同样达到目的,同时还封装了其他方法(preRequest(), postRequest()),可以处理一些其他问题。
    • 另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

    上面介绍的设计模式实际上叫做静态代理,下面我们介绍动态代理,它其实就是Spring AOP的底层实现。

    动态代理类:

    所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来使用。当然这个Dynamic Proxy其实就是一个Proxy,他不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

    Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

    • Interface InvocationHandler:该接口仅定义了一个方法
      • public Object invoke(Object proxy, Method method, Object[] args)
      • 在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
    • Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容
      • protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
      • static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
      • static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在Subject接口中声明过的方法)

    下面我们看下如何实现在程序中实现动态代理(在使用动态代理类时,我们必须实现InvocationHandler接口):

    被代理的类以及接口:

    package com.test.dynamicproxy;
    
    public interface Subject
    {
    	public void request();
    }
    
    ----------------------------------------------------------------------------
    package com.test.dynamicproxy;
    
    public class RealSubject implements Subject
    {
    	public void request()
    	{
    		System.out.println("from real subject");
    	}
    }
    

    代理类:

    package com.test.dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    /**
     * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象
     * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要
     * 执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实对象的方法前后
     * 加入自己的一些额外方法。
     *
     */
    public class ProxySubject implements InvocationHandler
    {
    	private Object sub;
    	
    	public ProxySubject(Object object)
    	{
    		this.sub = object;
    	}
    
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable
    	{
    		System.out.println("pre processing");
    		
    		method.invoke(sub, args);
    		
    		System.out.println("post processing");
    		
    		return null;
    	}
    
    }
    

    客户端:

    package com.test.dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class Client
    {
    	public static void main(String[] args)
    	{
    		// 模拟从Spring配置文件中得到的subject
    		RealSubject subject = new RealSubject();
    		
    		InvocationHandler ih = new ProxySubject(subject);
    	
    		Class<?> clazz = subject.getClass();
    		
    		// 一次性生成代理
    		Subject s = (Subject)Proxy.newProxyInstance(clazz.getClassLoader(), 
    				clazz.getInterfaces(), ih);	
    		
    		s.request(); // 由InvocationHandler的invock()方法执行真正的调用	
    	}
    }
    

    运行结果:

    pre processing
    from real subject
    post processing
    

    不管有多少具体的真实角色,代理可以只有一个,这一个代理可以用于处理所有的真实角色。

    • 通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(ProxySubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。
    • 使用场合:
      • 调试
      • 远程方法调用(RMI)
      • AOP

    总结:动态代理的创建步骤(重要)

    • 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    • 创建被代理的类以及接口
    • 通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建一个代理
    • 通过代理调用方法

    程序实践:

    package com.test.dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.List;
    import java.util.Vector;
    
    public class VectorProxy implements InvocationHandler
    {
    	private Object proxyobj;
    	
    	public VectorProxy(Object obj)
    	{
    		proxyobj = obj;
    	}
    
    	public static Object factory(Object obj)
    	{
    		Class<?> cls = obj.getClass();
    		
    		return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new VectorProxy(obj));
    	}
    	
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable
    	{
    		System.out.println("before calling " + method);
    		
    		if(args != null)
    			for(int i = 0; i < args.length; i++)
    				System.out.println(args[i]);
    		
    		Object object = method.invoke(proxyobj, args);
    		
    		return object;
    	}
    	
    	public static void main(String[] args)
    	{
    		
    		List v = (List)factory(new Vector());
    		
    		v.add("hello");	
    		v.add("world");
    		System.out.println(v);
    		
    		v.remove(0);
    		System.out.println(v);
    	}
    
    }
    

    输出:

    before calling public abstract boolean java.util.List.add(java.lang.Object)
    hello
    before calling public abstract boolean java.util.List.add(java.lang.Object)
    world
    before calling public java.lang.String java.lang.Object.toString()
    [hello, world]
    before calling public abstract java.lang.Object java.util.List.remove(int)
    0
    before calling public java.lang.String java.lang.Object.toString()
    [world]
    

    可以看到与Vector操作相关的默认行为已经发生了变化,这是由于我们施加了相关的代理。

    程序实践2:代理多个类的实例

    package com.test.dynamicproxy;
    
    public interface Foo
    {
        void doAction();
    }
    
    
    
    
    package com.test.dynamicproxy;
    
    public class FooImpl implements Foo
    {
        public FooImpl()
        {
        }
    
        public void doAction()
        {
            System.out.println("in FooImp1.doAction()");
        }
    }
    
    
    
    
    package com.test.dynamicproxy;
    
    public class FooImpl2 implements Foo
    {
        public FooImpl2()
        {
        }
    
        public void doAction()
        {
            System.out.println("in FooImp2.doAction()");
        }
    
    }
    
    
    
    package com.test.dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class CommonInvocationHandler implements InvocationHandler
    {
    
        // 动态执行对象,需要回调的对象
        private Object target;
    
        // 支持构造方法注射
        public CommonInvocationHandler()
        {
    
        }
    
        // 支持构造方法注射
        public CommonInvocationHandler(Object target)
        {
            setTarget(target);
        }
    
        /**
         * 
         * 采用setter方法注射
         * 
         * @param target
         * 
         */
        public void setTarget(Object target)
        {
            this.target = target;
        }
    
        /**
         * 
         * 调用proxy中指定的方法method,并传入参数列表args
         * 
         * @param proxy
         *            代理类的类型,例如定义对应method的代理接口
         * 
         * @param method
         *            被代理的方法
         * 
         * @param args
         *            调用被代理方法的参数
         * 
         * @return
         * 
         * @throws java.lang.Throwable
         * 
         */
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            return method.invoke(target, args);
        }
    
    }
    
    
    
    
    
    package com.test.dynamicproxy;
    
    import java.lang.reflect.Proxy;
    
    public class Demo
    {
    	public static void main(String[] args)
    	{
    
    		// 1.通用的动态代理实现
    
    		CommonInvocationHandler handler = new CommonInvocationHandler();
    
    		Foo f;
    
    		// 2.接口实现1
    
    		handler.setTarget(new FooImpl());
    
    		// 方法参数说明:代理类、代理类实现的接口列表、代理类的处理器
    
    		// 关联代理类、代理类中接口方法、处理器,但代理类中接口方法被调用时,会自动分发到处理器的invoke方法
    
    		// 如果代理类没有实现指定接口列表,会抛出非法参数异常
    
    		f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
    
    		new Class[] { Foo.class },
    
    		handler);
    
    		f.doAction();
    
    		// 3.接口实现2
    
    		handler.setTarget(new FooImpl2());
    
    
    		f.doAction();
    	}
    }
    

      

  • 相关阅读:
    docker基本命令
    vscode 保存提示运行"XXX"的保存参与者: 快速修复
    Vue 2.6 插槽
    代码大全-PartOne-变量命名
    Axure 8.0.1.3388 注册码 授权码 破解
    乱七八糟记一下乱七八糟的碎片化知识
    JavaScript需记的一些细节
    Python3.6问题
    python3.6- shape mismatch: objects cannot be broadcast to a single shape
    Angular+ng-zorro遇坑记
  • 原文地址:https://www.cnblogs.com/Code-Rush/p/4901651.html
Copyright © 2011-2022 走看看