代理模式学习

2020/03/15

Categories: 笔记 Tags: Java

静态代理

  1. 创建接口

  2. 创建被代理类

    实现接口

  3. 创建代理类

    也要实现同样的接口,同时还要持有被代理对象的引用

  4. 代理类发挥作用

    代理类执行方法(其实真正执行的核心方法是被代理类的方法)

interface Print{//接口
    public void print();
}

class Printer implements Print{//被代理类
    @Override
    public void print() {
        System.out.println("打印中。。。");
    }
}

class ProxyPrinter implements Print{//代理类
    private Printer printer;//要得到被代理类的引用
    ProxyPrinter(Printer printer) {
        this.printer = printer;
    }
    @Override
    public void print() {
        System.out.println("打印前。。。");
        printer.print();//调用被代理类的方法
        System.out.println("打印后。。。");
    }
}


public class Main {
    public static void main(String[] args){
        Print p = new ProxyPrinter(new Printer());//这里用了向上转型
        p.print();
    }
}
打印前。。。
打印中。。。
打印后。。。

动态代理

和静态代理相比,创建接口、创建真实的被代理类都不变。

  1. 创建接口
  2. 创建被代理类

实现接口

  1. 创建一个实现InvocationHandler接口的类(为了好记,我叫它拦截类,可以拦截被代理对象的方法)

跟静态代理里面代理类一样,要获得被代理类对象的引用(但不是代理类)
还必须实现invoke()方法,这里面可以对被代理类的方法进行增强

  1. 如何运用?

创建拦截类对象,传入真实的被代理类对象
由Proxy类的静态方法_newProxyInstance(3个参数)获得真正的代理对象,并向上转型为接口类型_
代理类对象执行方法

interface Print{//接口
    public void print();
}

class Printer implements Print{//被代理类
    @Override
    public void print() {
        System.out.println("打印中。。。");
    }
}

class ProxyInvocationHandler implements InvocationHandler{

    /**
     *  被代理的引用
     *  这里用Printer printer也可以运行,但没有用object有意义
     *  这样更灵活,才能体现动态代理的动态
    */
    private Object object;

    ProxyInvocationHandler(Object object) {
        this.object = object;
    }

    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("打印前。。。");
        Object result = method.invoke(object,args);
        System.out.println("打印后。。。");
        return result;
    }
}

public class Main {
    public static void main(String[] args){
        ProxyInvocationHandler pih = new ProxyInvocationHandler(new Printer());
        Print printer = (Print)Proxy.newProxyInstance(
            Printer.class.getClassLoader(),Printer.class.getInterfaces(),pih);
        printer.print();
    }
}
打印前。。。
打印中。。。
打印后。。。

对于动态代理,重点要理解下面2个方法:

🔨Proxy.newProxyInstance(3个参数)方法
此方法用于生成真正的代理对象

static Object    newProxyInstance(ClassLoader loader, <?>[] interfaces, InvocationHandler h) 

3个参数:

  1. loader 被代理类(目标对象)的类加载器
  2. interface 目标对象实现的接口
  3. h “拦截类”对象

注意返回的真正的代理对象需要转型。

🔨“拦截类”里必须重写的invoke(参数)方法
其实就是要理解method.invoke(参数),利用了反射,可以简单地认为用来执行某个的对象的目标方法

public Object invoke(Object obj,
                     Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException

ClassLoader是干什么的?

它是用来加载 Class 的。它负责将 Class 的字节码形式转换成内存形式的 Class 对象。字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class等。字节码的本质就是一个字节数组 []byte。

每个 Class 对象里面都有一个 classLoader 属性记录了当前的类是由谁来加载的。

✨3个重要的ClassLoader
JVM 中内置了三个重要的 ClassLoader,分别是

💤

ExtensionClassLoader 负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中,有很多 jar 包。

💦

AppClassLoader 才是直接面向我们用户的加载器,它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的。

双亲委派

每个 ClassLoader 都很懒,尽量把工作交给父亲做,父亲干不了了自己才会干。每个 ClassLoader 对象内部都会有一个 parent 属性指向它的父加载器。

🛴

AppClassLoader 在加载一个未知的类名时,它并不是立即去搜寻 Classpath,它会首先将这个类名称交给 ExtensionClassLoader 来加载,如果 ExtensionClassLoader 可以加载,那么 AppClassLoader 就不用麻烦了。否则它就会搜索 Classpath 。
而 ExtensionClassLoader 在加载一个未知的类名时,它也并不是立即搜寻 ext 路径,它会首先将类名称交给 BootstrapClassLoader 来加载,如果 BootstrapClassLoader 可以加载,那么 ExtensionClassLoader 也就不用麻烦了。否则它就会搜索 ext 路径下的 jar 包。

getInterfaces()

以下是**java.lang.Class.getInterfaces()**方法的声明

public Class<?>[] getInterfaces()

此方法返回这个类中实现接口的数组。

>> Home