代理模式

Posted 温布利往事

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式相关的知识,希望对你有一定的参考价值。

一、概念

  1、定义

  为其他对象提供一种代理以控制对这个对象的访问,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

  2、优点

  • 真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事物。
  • 代理对象可以在客户端和目标对象之间起到中介作用、
  • 高扩展性

二、代理模式组成

  在代理模式中,通常涉及到下面4中角色:

  ISubject:该接口是对被访问者或被访问资源的抽象

  SubjectImpl:被访问者的具体实现类

  SubjectProxy:被访问者的代理实现类,SubjectProxy持有SubjectImpl的实例

  Client:代表访问者的角色,Client将会访问ISubject类型的对象或资源,Client必须通过ISubject资源的访问代理类SubjectProxy进行

三、代理模式实现方式

  1、静态代理

  SubjectImpl和SubjectProxy都实现了相同的接口,而SubjectProxy内部持有SubjectImpl的引用,当Client通过request()请求服务的时候,SubjectProxy将请求转发给SubjectImpl。在将请求转发给被代理对象SubjectImpl之前或者之后,都可以根据情况插入其他处理逻辑,比如在转发之前记录方法执行开始时间,在转发之后记录结束时间。示例代码如下:

/*
 * ISubject角色
 */
public interface Aithmetic
{
     public int add(int i,int j);
     public int sub(int i,int j);
}
/**
 * ISubjectImpl角色
 */
public class AithmeticImpl implements Aithmetic
{
    public int add(int i, int j)
    {
        int result=i+j;
        return result;
    }
    public int sub(int i, int j)
    {    
        int result=i-j;
        return result;
    }
}
/*
 * SubjectProxy角色
 */
public class AithmeticProxy implements Aithmetic
{
    private Aithmetic aithmetic;
    public AithmeticProxy(Aithmetic aithmetic)
    {
        this.aithmetic=aithmetic;
    }
    public int add(int i, int j)
    {
        //加入自己的逻辑
        System.out.println("自定义逻辑开始");
        int sum=aithmetic.add(i, j);
        return sum;
    }
    public int sub(int i, int j)
    {
        System.out.println("自定义逻辑开始");
        int sub=aithmetic.sub(i, j);
        return sub;
    }
}
/*
 * Client角色
 */
public class AithmeticProxyTest
{
    @Test
    public void test()
    {
        Aithmetic aithmetic=new AithmeticImpl();
        AithmeticProxy aithmeticProxy=new AithmeticProxy(aithmetic);
        int sum=aithmeticProxy.add(2, 3);
        System.out.println(sum);
    }
}

  

 

  静态代理的缺点:针对不一样的目标对象类型,我们要为其单独实现一个代理对象,而实际上,这些代理对象所要添加的横切逻辑是一样的。

  2、动态代理

  我们利用动态代理机制,可以为指定的接口在系统运行期间动态的生成代理对象。

  动态代理机制的实现主要由一个类和一个接口组成,即Java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler接口,下面给出示例为两种不同的类型提供代理对象。

  假设现在有两个接口,都有request方法,我们想在request方法执行之前加入自己的处理逻辑。两个接口如下:

public interface Aithmetic
{
     public int add(int i,int j);
     public int sub(int i,int j);
     public void request();
}
public interface IRequestable
{
     public void request();
}
public class AithmeticImpl implements Aithmetic
{
    public int add(int i, int j)
    {
        int result=i+j;
        return result;
    }
    public int sub(int i, int j)
    {    
        int result=i-j;
        return result;
    }
    public void request()
    {
        System.out.println("执行AithmeticImpl的request函数");
    }  
}
public class IRequestableImpl implements IRequestable
{

    public void request()
    {
        System.out.println("执行IRequestableImpl的request函数");
    }
}

  为了达到目的,我们需要实现一个InvocationHandler:

public class MyInvocationHandler implements InvocationHandler
{

    private Object target;
    public MyInvocationHandler(Object target)
    {
        this.target=target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        if(method.getName().equals("request"))
        {
            System.out.println("加入自己的逻辑");
        }
        return method.invoke(target, args);
    }
}

  然后就可以使用Proxy类,根据MyInvocationHandler的逻辑,为Aithmetic和IRequestable两种类型生成相应的代理对象实例:

public class MyInvocationHandlerTest
{
    @Test
    public void test()
    {
        Aithmetic aithmetic=(Aithmetic) Proxy.newProxyInstance(MyInvocationHandlerTest.class.getClassLoader(), new Class[]{Aithmetic.class}, new MyInvocationHandler(new AithmeticImpl()));
        aithmetic.request();
        System.out.println(aithmetic.add(2,3));
        
        IRequestable iRequestable=(IRequestable) Proxy.newProxyInstance(MyInvocationHandlerTest.class.getClassLoader(), new Class[]{IRequestable.class}, new MyInvocationHandler(new IRequestableImpl()));
        iRequestable.request();
    }
}

  执行结果为:

  

  动态代理的缺点:动态代理的只能对实现了相应的接口的类使用,如果某个类没有实现任何接口,就无法使用 动态代理机制为其生成相应的动态代理对象。

  3、动态字节码生成

  原理:我们可以对目标对象进行继承扩展,为其生成相应的子类,而子类可以通过覆写来扩展父类的行为,只要将横切逻辑的实现放在子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式相同的效果了。我们借助于CGLIB这样的动态字节码生成库,在系统运行期间为目标对象生成相应的扩展子类。

  注:需要用到以下三个包

  

  下面是示例代码:

  首先编写需要代理的目标对象:

public class TargetObject
{
    public int add(int i, int j)
    {
        int result = i + j;
        return result;
    }
}

  创建cglib代理的工厂类:

public class CglibProxy implements MethodInterceptor  
{
     //设置需要创建子类的类
    private Object targetObject;
    public Object createProxyInstance(Object target)
    {
        this.targetObject = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.targetObject.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //通过字节码技术动态创建子类实例
        return enhancer.create();     
    }
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
    {
        /*obj:目标类实例
         *method:目标类法方法的反射对象
         *args:方法的动态入参
         *methodProxy:代理类实例 
        */
        System.out.println("加入自己的逻辑");        
        //通过代理类调用父类中的方法
        Object result = methodProxy.invoke(targetObject, args);        
        System.out.println("操作结束");
        return result;
    }
    
}

  测试:

public class CglibProxyTest
{
    @Test
    public void test()
    {
         CglibProxy cglibProxy=new CglibProxy();
         TargetObject t=(TargetObject) cglibProxy.createProxyInstance(new TargetObject());
         System.out.println(t.add(2, 3));
    }
}

  执行结果:

  

  注:CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法不要声明成final。

以上是关于代理模式的主要内容,如果未能解决你的问题,请参考以下文章

scrapy按顺序启动多个爬虫代码片段(python3)

用于从 cloudkit 检索单列的代码模式/片段

java代码实现设计模式之代理模式

代理模式(静态代理动态代理)代码实战(详细)

Java设计模式-代理模式之动态代理(附源代码分析)

代理模式(静态代理)