2017-10-4(动态代理)

196 阅读4分钟

代理分为静态代理和动态代理。

  • 静态代理 可以看下面代码
/**
 * 静态代理中代理类和委托类也常常继承同一父类或实现同一接口。
 * 普通业务类
 * 通过接口更加灵活实现代理模式
 */
public interface Subject {

    void visit();

}

/**
 * 委托类
 * 代理类和真实业务类都需要实现业务类的接口
 */
public class RealSubject implements Subject{
    @Override
    public void visit() {
        //真实业务具体逻辑
        System.out.println("Real Subject");
    }
}

/**
 * 代理类
 * 代理类和委托类都需要实现业务类的接口
 */
public class ProxySubject implements Subject {

    private Subject iSubject;//委托类

    public ProxySubject(Subject subject){
        //关键:关联委托类
        this.iSubject = subject;
    }

    @Override
    public void visit() {
        //调用真实业务类的方法
        iSubject.visit();
    }
}

public class Client {

    public static void main(String[] args){
        //构造一个真实主题对象
        RealSubject realSubject = new RealSubject();

        //通过真实主题对象构造一个代理对象
        ProxySubject proxySubject = new ProxySubject(realSubject);

        //调用代理类的相关方法
        proxySubject.visit();
    }
}

其实可以看个几个关键点:

  • 代理类和委托类公共实现同一个接口。(其实我觉得这个不是必然的。代理类可以不实现接口。而委托类实现接口是基于面向接口编程这个原则,实现接口使编程更加灵活而已)
  • 这个是核心:就是代理类构建实例需要关联委托类。这样才能调用代理方法,实则是调用委托类的方法实现功能。

#其实静态代理:在实际中不方便直接构建委托对象,而需要代理类实现具体的方法。

  • 动态代理
public interface DynamicInterface {
    //实现逻辑
    void implement();
}

/**
  委托类
 * 实现接口
 */
public class DynamicRealSubject implements DynamicInterface{

    @Override
    public void implement() {
        Log.e("DynamicRealSubject","DynamicRealSubject___动态代理模式");
    }
}

/**
 * 实现InvocationHandler的类是运行时将生成的代理类需要完成的具体任务即invoke方法里面
 * 动态代理
 */
public class DynamicProxySubject implements InvocationHandler{

    private Object sub;//需要被代理的对象

    public DynamicProxySubject(Object sub){
        this.sub = sub;
    }

    public DynamicProxySubject(){}


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.e("DynamicProxySubject","动态代理前");

        //真正被代理类的方法被调用 method就是被代理的类的方法
        method.invoke(sub,args);
        //proxy这个参数在方法里写会内存溢出
//        method.invoke(proxy,args);
        Log.e("DynamicProxySubject","动态代理后");
        return null;
    }
}


//实现结果
 public static void main(String[] args) throws Exception {
        DynamicRealSubject realSubject = new DynamicRealSubject();

        InvocationHandler handler = new DynamicProxySubject(realSubject);
    

        //一次性生成
        DynamicInterface subject = (DynamicInterface)Proxy
                .newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),handler);
       

        //实现动态代理被代理类的方法
        subject.implement();
    }
  • java JDK就提供了实现动态代理的方法。涉及到两个类: 1、InvocationHandler 实现委托类的方法的一个接口 2、Proxy 代理类

动态代理与静态代理最大的不同就是代理类非程序员预先定义好,而是在运行时才生成代理类。

提示: 我开始时候以为实现InvocationHandler接口的就是代理类,其实是错误的。该类其实提供给Proxy类构造时关联用到的。每当生成代理类实例调用代理方法就会调用实现InvocationHandler接口的类中的invoke方法。

实现InvocationHandler接口的类

  • 这里构造函数需要关联一下委托类。目的就是在invoke方法里面通过反射调用委托类的方法,其实实现的目的与静态类是一致的。
  • invoke有三个参数 Object这个是代理类。Method 就是通过Proxy生成实例接口里面的方法。而args数组就是method的参数

看看这个InvocationHandler类到底如何与Proxy有联系的

DynamicRealSubject realSubject = new DynamicRealSubject();
InvocationHandler handler = new DynamicProxySubject(realSubject);
Class cls = realSubject.getClass();
 DynamicInterface subject = 
(DynamicInterface)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),handler);          

这样就将InvocationHandler类与Proxy类关联起来。 看看Proxy.newProxyInstance这个方法是做什么的

newProxyInstance方法内部

看到了cl这个变量明显就是一个类对象然后在生成构造函数再构建对象。构建对象时通过h即InvocationHandler这个实例关联一起。整个过程就是这样生成一个代理实例。

看看getProxyClass0方法生成类对象

Paste_Image.png

由于该方法太长截取关键部分: 这里的generateProxy就是生成类对象,之前的代码是进行缓存。

看看生成代理对象那代码:

Paste_Image.png

Paste_Image.png

生成代理对象时,把InvocationHandler实例传进去 所以它最终生成的代理类是会有InvocationHandler的痕迹的。这里我看了网上的说,生成的代理类是继承Proxy实现委托接口。在代理方法里面InvocationHandler通过反射调用invoke方法。