代理模式

2020-09-16  本文已影响0人  code希必地

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()方法来实现我们的需求了。这就是静态代理,由于静态代理类和接口是一一对应的,如果有多个接口需要代理的话,就需要创建多个代理类,所以动态代理就应运而生。

静态代理的特点

4、动态代理

4.1、动态代理的原理

4.2、使用步骤

/**
 * 作用:
 * 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个疑问:

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());
        }
    }

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

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);
        }
    }

总结:

上一篇下一篇

猜你喜欢

热点阅读