JAVA Proxy代理模式

代理模式

给目标对象提供一个代理对象,通过代理访问目标对象,以控制访问者对目标对象的访问。
好处是将访问者和目标类隔离,保护了目标类对象、扩展了目标类的功能。

分类

  1. 静态代理:在程序运行前,class文件就已经被创建了,代理类和目标类的关系在运行前就确定了。
  2. 动态代理:在程序运行时,使用JVM反射等机制动态生成的代理类,代理类和目标类的关系在运行时确定。动态代理实现方案:JDK动态代理、ASM代理【Spring AOP代理】、CGlib代理。

使用场景

  1. Spring AOP。【Bean工厂+灵活装配+动态行为拼接】。
  2. 消息的处理、过滤。在消息被目标类处理前,可在代理类中进行预处理、过滤。
  3. 在业务功能前后加入一些公共的服务,比如通过代理,给业务模块提供记录日志、权限控制的功能。
  4. 其他:Http远程代理、防火墙代理、Copy-on-Write 代理。都是在访问者和目标直接加了一层,进行功能增强或访问限制。

静态代理

在程序运行前,根据目标类接口,创建静态代理类。搬一张菜鸟教程的类图:
在这里插入图片描述

  1. 定义目标类和代理类共同的接口方法 Image。
  2. 基于Image接口实现目标类Real。
  3. 基于Image接口实现代理类Proxy,且Proxy持有Real对象,通过调用Real对象的接口方法实现代理方法。
  4. 访问者调用Proxy类的代理方法。

静态代理DEMO

主方法类:

//静态代理 DEMO
public class Proxy_v1 {
    public static void main(String[] args) throws InterruptedException {
        //访问者访问代理类,使用相关功能
        Proxy p = new Proxy();
        p.ShowImage();
    }
}

代理类及接口:

//静态代理接口
public interface Image {
    void ShowImage();
}

//被代理的目标类
public class Real implements Image{
    @Override
    public void ShowImage() {
        System.out.println("Real Image !");
    }
}

//代理类
public class Proxy implements Image{

    private Real real;
    public Proxy()
    {
        this.real = new Real();
    }
    @Override
    public void ShowImage() {
        System.out.println("~~~~ Show begin ! ~~~~");
        this.real.ShowImage();
        System.out.println("~~~~ End ! ~~~~");
    }
}

JDK动态代理

基于 JDK中的Proxy类实现运行时的动态代理。DEMO是创建一个car对象,car对象实现Movable接口,可实现Move和Jump功能,现创建一个动态代理proxyM,实现对car对象的动态代理。构建步骤主要有:

  1. 定义接口方法Movable,以及要被代理的类Car。
  2. 在主方法中 使用 Movable proxyM = Proxy.newProxyInstance创建动态代理。
  3. Proxy.newProxyInstance需要传入的参数有3个,分别是:
    • 目标类的ClassLoader:Car.class.getClassLoader()
    • 目标类实现的接口类:new Class[]{Movable.class}
    • 实现InvocationHandler接口的实例对象
  4. InvocationHandler接口的实现对象中,在invoke方法中可扩展实现代理类自己的逻辑,并可通过调用method.invoke(car,args) 方法 执行目标类car的行为。

JDK动态代理DEMO

定义接口方法Movable,以及要被代理的类Car:

public interface Movable {
    void Move();
    void Jump();
}

public class Car implements Movable{
    @Override
    public void Move() {
        System.out.println("Car is moving !");
    }

    @Override
    public void Jump() {
        System.out.println("Car is Jumping !");
    }
}

动态代理主方法:

/JDK动态代理 DEMO
public class Proxy_v2 {
    public static void main(String[] args) throws InterruptedException {

        //创建目标类对象
        Car car = new Car();
        //设置生成的动态代理文件到本地
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        //打印代理类生产路径
        System.out.println(Proxy.getProxyClass(Movable.class.getClassLoader(), Movable.class));
        //在com.sun.proxy.$Proxy0

        //使用JDK动态代理方法Proxy.newProxyInstance【1强转,3参数】
        //类型强制转换为(Movable)
        //需要目标类Car的Classloader
        //目标类的接口Movable
        //需要实现invocationHandle,ProxyHandler
        Movable proxyM = (Movable) Proxy.newProxyInstance(Car.class.getClassLoader(),
                new Class[]{Movable.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) 
                    throws Throwable {
                        System.out.println("Before ......");
                        //调用的是car对象中的Move方法,传入args参数,返回结果o
                        //由于Move方法没有参数,因此args和o不起作用
                        Object o = method.invoke(car,args);
                        System.out.println("After ......");
                        return o;
                    }
                }
        );

        //使用代理方法,代理了一个接口的2个方法
        proxyM.Move();
        proxyM.Jump();
    }
}

Tips

通过System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,“true”);可将运行时创建的动态代理文件保存到本地磁盘【默认是不保存的】,再使用IDE反编译功能对CLASS文件查看,看到动态代理类的源码。

  1. proxyM指向的就是$Proxy0类动态生成的代理对象。
  2. proxyM.Move方法实现的关键是调用super.h.invoke(this, m4, (Object[])null);其中super.h指向的是我们实现 InvocationHandler接口的对象。
  3. 因此 super.h.invoke调用的是我们重写的invoke方法。
  4. 在重写的invoke方法中,Object o = method.invoke(car,args) 调用了car中定义的 method【m4】方法,方法参数是args,返回结果保存在o中。
public final class $Proxy0 extends Proxy implements Movable {
    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 Jump() 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 Move() 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("com.wabinogi.Proxy_v2.Movable").getMethod("Jump");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.wabinogi.Proxy_v2.Movable").getMethod("Move");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

优点

  1. 高扩展性:在目标类基础上,扩展新的功能。
  2. 符合开闭原则:增加功能时只需要修改代理类,不需要修改目标类。

缺点

  1. 动态代理:在某些动态代理类的技术方案中,无法脱离Interface【使用动态代理必须定义接口】。
  2. 静态代理:一次只能代理一个业务类,如果要对多个类进行代理,需要新增代理类。

Proxy和Adaptor不同之处

静态代理和适配器在语法和写法上很相近,区别主要是体现在语义【动机】上的不同。

  • Adaptor:对于访问者而言,目标类的功能方法是清晰、可知的,要解决的问题是接口转换。
  • Proxy:对于访问者而言,目标类的功能方法不一定是可知的,因为代理的目的就是控制访问者对目标类的访问。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值