设计模式:01-代理模式

设计模式:01-代理模式,第1张

设计模式:01-代理模式

一。使用代理模式的场景:对已经开发好的类进行功能增强(添加或修改),比如有一个接口:

package cn.edu.tju.service;

public interface Living {
    void eat();
    void drink();
}

以及该接口的一个实现类:

package cn.edu.tju.app;

import cn.edu.tju.service.Living;

public class ChinesePerson implements Living {
    @Override
    public void eat() {
        System.out.println("开始吃大餐!!!");
    }

    @Override
    public void drink() {
        System.out.println("开始喝啤酒!!!");
    }

}

现在要对ChinesePerson这个类进行增强,在调用eat和drink方法前后打印一些日志。可以创建ChinsePerson的一个代理类(ChinesePersonProxy),该代理类持有一个ChinesePerson对象

package cn.edu.tju.app;

import cn.edu.tju.service.Living;

public class ChinesePersonProxy implements Living {
    private ChinesePerson chinesePerson;
    public ChinesePersonProxy(ChinesePerson chinesePerson){
        this.chinesePerson=chinesePerson;
    }

    @Override
    public void eat() {
        System.out.println("准备大餐");
        chinesePerson.eat();
        System.out.println("收拾厨房");

    }

    @Override
    public void drink() {
        System.out.println("准备啤酒");
        chinesePerson.drink();
        System.out.println("收拾酒瓶");
    }
}

可以写一个主类,调用ChinesePersonProxy这个代理类的方法。

package cn.edu.tju.app;

public class ProxyTest01 {
    public static void main(String[] args) {
        ChinesePersonProxy personProxy=new ChinesePersonProxy(new ChinesePerson());
        personProxy.eat();
        personProxy.drink();
    }
}

运行程序输出如下:

上述方法称为静态代理,代理类中的所有代理方法都是由程序员手动写代码完成。
二。为什么要用动态代理?
假设有一个Living接口的实现类AmericanPerson,代码如下,

package cn.edu.tju.app;

import cn.edu.tju.service.Living;

public class AmericanPerson implements Living {
    @Override
    public void eat() {
        System.out.println("start eating hamburger");
    }

    @Override
    public void drink() {
        System.out.println("start drinking Cola");
    }

}

现在有一个需求,需要在调用AmericanPerson的eat()方法和drink方法前后都要打印系统当前时间。可以模仿上一小节中的方法来完成AmericanPersonProxy。但是有一个问题:假如Living()接口有10000个方法,而AmericanPerson类实现了Living接口中的10000个方法,要按照静态代理的方法来写AmericanPersonProxy类的话,就需要在该类中写10000个代理方法,显然是不合理的。合理的方法是使用jdk 动态代理。
使用jdk动态代理的AmericanPersonProxy类示例如下:

package cn.edu.tju.app;

import cn.edu.tju.service.Living;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;

public class AmericanPersonProxy  implements Living{
    AmericanPerson americanPerson=new AmericanPerson();
    Living living= (Living) Proxy.newProxyInstance(AmericanPerson.class.getClassLoader(),new Class[]{Living.class},new InvocationHandler(){
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("_______________________________________________________________________"+new Date().toLocaleString());
            method.invoke(americanPerson,args);
            System.out.println("_______________________________________________________________________"+new Date().toLocaleString());
            return null;
        }
    });

    @Override
    public void eat() {
        living.eat();
    }
    @Override
    public void drink() {
        living.drink();
    }
}

该代理类持有一个AmericanPerson类的对象: AmericanPerson americanPerson=new AmericanPerson();同时通过Proxy类(jdk的一个类)生成了一个动态类对象:Living living= (Living) Proxy.newProxyInstance(。。。),该代理类的方法eat()和drink()则分别调用这个动态类对象的方法:

living.eat();
living.drink();

写一个测试类,代码如下:

package cn.edu.tju.app;

public class ProxyTest02 {
    public static void main(String[] args) {
        //System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        AmericanPersonProxy americanPersonProxy=new AmericanPersonProxy();
        americanPersonProxy.eat();
        americanPersonProxy.drink();
    }
}

运行结果如下:

从运行结果可以看出,调用AmericanPersonProxy类对象的eat()和drink()方法时,都调用了该类中定义的动态类对象living构建时所传入的InvocationHandler中的invoke()方法。这是怎么发生的呢?需要看jdk动态生成的类的源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import cn.edu.tju.service.Living;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Living {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void eat() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void drink() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("cn.edu.tju.service.Living").getMethod("eat");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("cn.edu.tju.service.Living").getMethod("drink");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

该类对象通过构造方法,传入InvocationHandler类对象:

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

该动态类的父类为Proxy,Proxy类的构造方法如下:

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

而h定义如下:

  protected InvocationHandler h;

总结一下就是,Proxy0这个动态类对象在构造时会把传入的InvocationHander对象保存到自己的protected类型的变量h中。
再看动态类里的eat()方法:

    public final void eat() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

核心的一句是:

super.h.invoke(this, m3, (Object[])null);

其中super.h为刚才提到的h。那么m3呢?来看静态代码块里的定义

   static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("cn.edu.tju.service.Living").getMethod("eat");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("cn.edu.tju.service.Living").getMethod("drink");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

也就是主类中调用eat方法时,最终会调用jdk生成的动态对象的invoke()方法。

invoke(this, Class.forName("cn.edu.tju.service.Living").getMethod("eat"),(Object[])null)

而invoke方法的定义上边已经贴过:

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("_______________________________________________________________________"+new Date().toLocaleString());
            method.invoke(americanPerson,args);
            System.out.println("_______________________________________________________________________"+new Date().toLocaleString());
            return null;
        }

代码比较简单,就是反射,不再详述。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5671684.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存