美文网首页
代理模式

代理模式

作者: code希必地 | 来源:发表于2020-09-16 17:13 被阅读0次

1、什么是代理模式

定义:给目标对象提供一个代理对象,并由代理对象控制目标对象的引用。
在代理模式中需要代理对象和目标对象实现同一个接口,看下UML图

image.png

2、为什么要用代理

代理的作用:使用代理模式,可以在不修改目标对象方法的前提下,对方法进行包装增强。
举个例子

public interface IPerson{
        void say();
    }
    public class Student implements IPerson{

        @Override
        public void say() {
            Log.e("TAG","Student Say");
        }
    }

Student类实现了IPerson接口,如果我们想在say()被调用时记录下调用的时间,最粗暴的方式就是在say()中直接增加时间记录的方法,但是如果项目中有很多类都实现了IPerson那么就需要修改很多类,而且并不是所有类都需要记录调用时间,那该怎么处理呢?肯定是使用代理啊。

3、静态代理

public interface IPerson {
        void say();
    }


    public class Student implements IPerson {

        @Override
        public void say() {
            Log.e("TAG", "Student Say");
        }
    }


    public class StudentProxy implements IPerson {

        private IPerson target;

        public IPerson getTarget() {
            return target;
        }

        public void setTarget(IPerson target) {
            this.target = target;
        }

        @Override
        public void say() {
            Log.e("TAG", "记录目标对象调用say的时间");
            target.say();
        }
    }

我们需要创建一个代理类StudentProxy同样需要实现接口IPerson,将要代理的对象传进来,这样就可以不需要修改目标对象Studentsay()方法来实现我们的需求了。这就是静态代理,由于静态代理类和接口是一一对应的,如果有多个接口需要代理的话,就需要创建多个代理类,所以动态代理就应运而生。

静态代理的特点

  • 1、一个静态代理类只能对应一种目标对象,即静态代理类和接口一一对应。
  • 2、如果有多个接口需要代理,就需要创建多个静态代理类对象。

4、动态代理

4.1、动态代理的原理

  • 1、设计动态代理类时,不需要显式的实现和目标类对象相同的接口。而是将这种实现推迟到运行时进行实现。
  • 2、调用动态代理类对象的方法时,会通过反射机制method.invoke(),从而自动调用目标类对象的方法。

4.2、使用步骤

  • 1、声明 调用处理器类
  • 2、声明 目标对象类的接口
  • 3、声明 目标对象类
  • 4、通过动态代理对象调用目标对象的方法
    步骤1:声明 调用处理器类
/**
 * 作用:
 * 1、生成 动态代理类对象
 * 2、指定 代理对象调用目标对象方法前要完成的任务
 * 注:需要实现调用处理器接口即InvocationHandler,故该类是调用处理器类
 */
public class InvocationHandlerImpl implements InvocationHandler {

    //声明 被代理对象
    //作用:指定该对象类实现的一组接口中,那些方法被调用时,执行invoke
    private Object proxyObject;

    public Object newInstance(Object proxyObject) {
        this.proxyObject = proxyObject;
        return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this);
        //Proxy.newProxyInstance() 根据指定的类加载器、目标类实现的一组接口和调用处理器类 创建动态代理类对象
        //参数1:类加载器 需要和目标类对象为同一个类加载器
        //参数2:一组接口 目标类实现的接口,动态代理类会默认实现这组接口
        //参数3:调用处理器类
    }


    /**
     * 动态代理类对象调用目标类方法前会调用invoke()
     * @param proxy 动态代理对象
     * @param method 目标对象被调用的方法
     * @param args 目标对象被调用方法的参数
     * @return 目标对象被调用方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result=null;
        Log.e("Proxy","目标方法调用前");
        //通过反射调用目标类对象方法
        result=method.invoke(proxyObject,args);
        Log.e("Proxy","目标方法调用后");
        return result;
    }
}

步骤二:声明目标类要实现的接口

/**
 * 目标对象类要实现的接口
 */
public interface Subject {

    //定义目标对象类的接口方法
    public void buy(String name);

}

步骤三:声明目标类

/**
 * 声明目标对象类并实现接口
 */
public class RealObject1 implements Subject {
    @Override
    public void buy(String name) {
        Log.e("Proxy",name+"想买电脑");
    }

}

public class RealObject2 implements Subject {
    @Override
    public void buy(String name) {
        Log.e("Proxy", name+"想买平板");
    }
}

步骤四:通过动态代理对象调用目标类方法

InvocationHandlerImpl dynamicProxy = new InvocationHandlerImpl();
        RealObject1 realObject1 = new RealObject1();
        Subject realObject1Proxy = (Subject) dynamicProxy.newInstance(realObject1);
        realObject1Proxy.buy("小红");

        RealObject2 realObject2 = new RealObject2();
        Subject realObject2Proxy = (Subject) dynamicProxy.newInstance(realObject2);
        realObject2Proxy.buy("小兰");

5、源码解析

看到这里会有如下2个疑问:

  • 1、动态代理类对象是如何生成的?
  • 2、如何通过调用动态代理类对象,来调用目标类对象?

5.1、动态代理类和对象是如何动态生成的

Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this)

上面是创建动态代理类对象的代码,我们就从Proxy.newProxyInstance()入手

 private final static Class[] constructorParams =
        { InvocationHandler.class };
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        //通过类加载器(和目标类对象同一个类加载器)和一组接口(目标类实现的接口)创建动态代理类
        Class<?> cl = getProxyClass0(loader, interfaces);

        try {
            //通过反射获取动态代理类的带参(参数类型为InvocationHandler)构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            //通过动态代理类的构造函数,创建动态代理类对象,参数为h
            return newInstance(cons, h);
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

该方法主要做了如下几件事:

  • 1、根据类加载器和一组接口创建动态代理类
  • 2、获取动态类的带参构造函数
  • 3、反射创建动态代理类对象

5.2、如何通过调用动态代理对象方法来调用目标类方法

//使用代码
realObject1Proxy.buy("小红");

下面直接看动态代理类的实现


/**
 * 动态代理类的实现
 */
public class ProxySubject extends Proxy implements Subject {

    //构造函数 在动态代理类的创建时获取带有一个参数(调用处理器类型)的构造函数就是这个
    protected ProxySubject(InvocationHandler h) {
        super(h);
    }

    //buy是目标类实现接口(Subject)中的方法,所以动态代理类也需要实现该接口

    @Override
    public final void buy(String name) {
        try {
            //该方法实际上调用了父类Proxy类中的h的invoke方法
            //h即是在Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this);中传入的第三个参数InvocationHandler对象
            //即调用了调用处理器的InvocationHandler.invoke()
            //而在复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)
            //从而调用目标对象的的方法
            super.h.invoke(this, method, args);
            return;
        } catch (Error e) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

总结:

  • 1、动态代理类实现了和目标类相同的接口,并实现了接口的方法
  • 2、方法中的实现逻辑为:通过super.h.invoke()调用InvocationHandlerinvoke()方法
  • 3、InvocationHandler中的invoke中通过反射调用目标类的方法。

相关文章

  • 设计模式

    单例模式 模板方法模式 工厂模式 代理模式 静态代理 JDK动态代理

  • 设计模式

    单例模式 代理模式 静态代理 jdk动态代理 cglib动态代理 工厂模式 适配器模式 建造者模式 观察者模式

  • kube-proxy的3种模式

    userspace代理模式 iptables代理模式 IPVS代理模式 https://kubernetes.io...

  • 第4章 结构型模式-代理模式

    一、代理模式简介 二、代理模式3个角色 三、代理模式的优点 四、代理模式的实例(游戏代练)

  • 理解代理模式

    原创博客地址 简介 代理模式,也叫做委托模式,分为:静态代理动态代理 代理模式也是平时比较常用的设计模式之一,代理...

  • 结构型 代理模式(文末有项目连接)

    1:什么是代理模式 2:没用代理模式时的实例 3:使用代理模式将其解耦(静态代理) 3:使用代理模式将其解耦(动态...

  • 设计模式-动态代理模式

    之前介绍了代理模式,大家也都了解了代理模式,不过之前介绍的代理模式是静态代理,静态代理什么意思?静态代理指的是代理...

  • 代理模式

    一、什么是代理模式 代理模式(Proxy pattern):代理模式又叫委托模式,是为某个对象提供一个代理对象,并...

  • 设计模式之代理模式(Proxy模式)

    代理模式的引入 代理模式的实例程序 代理模式的分析 代理模式的引入 Proxy是代理人的意思,指的是代替别人进行工...

  • Java设计模式之代理模式

    Java设计模式之代理模式 代理模式 静态代理 动态代理 为什么需要代理 通过代理,我们能够不用知道委托人是谁,而...

网友评论

      本文标题:代理模式

      本文链接:https://www.haomeiwen.com/subject/mpscyktx.html