设计模式——代理模式

129 阅读3分钟

简述:代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。

需求:现在需要实现加减乘除功能。

interface ICal{

    int add(int a,int b);
    int sub(int a,int b);
    int mul(int a,int b);
    int div(int a,int b);
}

class CalImlp implements ICal{

    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int sub(int a, int b) {
        return a-b;
    }

    @Override
    public int mul(int a, int b) {
        return a*b;
    }

    @Override
    public int div(int a, int b) {
        return a/b;
    }
}

/*===================客户端=============*/
public class negtive {
        public static void main(String[] args) {
           Calculator c = new MyCalculator();
           System.out.println(c.add(2, 3));
           System.out.println(c.sub(10, 3));
           System.out.println(c.mul(8, 3));
           System.out.println(c.div(99, 3));
    }
}

看起来十分简单

但是,变化来了,想要给每个方法加入一些输出提示

难道要每个方法都加上输出提示???

那如果需求又变了,输出提示显示为英文,又要全部改一遍???

No!!!

此时我们就需要引入动态代理

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  1. 第1个参数:ClassLoader(动态代理的对象的类加载器)

我们都知道,要实例化一个对象,是需要调用类的构造器的,在程序运行期间第一次调用构造器时,就会引起类的加载,加载类的时候,就是jvm拿着ClassLoader去加载类的字节码的,只有字节码被加载到了内存中,才能进一步去实例化出类的对象。

简单来说,就是只要涉及实例化类的对象,就一定要加载类的字节码,而加载字节码就必须使用类加载器!下面我们使用的是动态代理的api来创建一个类的对象,这是一种不常用的实例化类对象的方式,尽管不常用,但毕竟涉及实例化类的对象,那就一定也需要加载类的字节码,也就一定需要类加载器,所以我们手动把类加载器传入!

  1. 第2个参数:Class[](需要调用其方法的接口) 我们已经知道,下面的代码,是用来实例化一个对象的,实例化对象,就一定是实例化某一个类的对象,问题是,到底是哪个类呢?类在哪里?字节码又在哪里?这个类,其实并不在硬盘上,而是在内存中!是由动态代理在内存中"f动态生成的!要知道,这个在内存中直接生成的字节码,会去自动实现下面方法中的第2个参数中,所指定的接口!所以,利用动态代理生成的代理对象,就能转成Calculator接口类型!那么这个代理对象就拥有add、 sub、 mul 、div方法!

  2. 第3个参数:InvocationHandler(调用方法时的处理程序) 我们已经知道,下面的代理对象porxy所属的类,实现了Calculator接口,所以,这个代理对象就拥有add、 sub、 mul 、div方法!我们就可以通过代理对象调用add、 sub、 mul 、div方法!注意,每次对代理对象任何方法的调用,都不会进入真正的实现方法中。而是统统进入第3个参数的invoke方法中!

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }

Object proxy 代理对象

Method代理对象调用的方法

Object[] args调用方法的参数

实现

public class MyHandler implements InvocationHandler {
    private Calculator calculator ;
    public MyHandler(Calculator c){
        this.calculator = c;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用"+method.getName()+",  参数是"+ Arrays.toString(args));
        int res = (int) method.invoke(calculator, args);
        System.out.println("结果是 "+res);
        return res;
    }
}
//先把InvocationHandler的实现类设计好。在实现类的内部关联Calculator,用于调用Calculator的方法。

Copy
public class postive {
    public static void main(String[] args) {
        Calculator c = new MyCalculator();

        ClassLoader loader = postive.class.getClassLoader();
        Calculator proxy = (Calculator)Proxy.newProxyInstance(loader, new Class[]{Calculator.class}, new MyHandler(c));

        proxy.add(22,33);
        proxy.sub(55,22);
        proxy.div(10,2);
        proxy.mul(50,5);
    }
}

总结 代理模式是代理对象通过在其内部关联被代理对象,对被代理对象的方法实施扩展。