美文网首页
java静态代理和动态代理

java静态代理和动态代理

作者: JerrysCode | 来源:发表于2021-02-17 11:50 被阅读0次

所谓代理模式就是当对象A无法直接引用对象B时,引入对象C作为中间人,对象A调用对象C,对象C调用对象B,这个中间对象C就是代理。


代理模式

代理模式应用场景

  1. 隔离变化:当对象B发生变化是,只需要修改代理对象C, 对象A不受影响。
  2. 一些“横切”功能可以放到代理对象中实现,如安全认证,鉴权、日志打印等。
    代理模式在软件开发中应用非常广泛,Srping AOP, gRPC远程调用都使用了代理模式。

java中的代理分为静态代理 和 动态代理。

静态代理

静态代理就是代理类是写死的,代理关系和代理类处理逻辑是“静态”的,无法在运行时更改

代码实现

  1. 先定义一个接口Movable,表示可以移动行驶的物体。
public interface Movable
{
    void move() throws Exception;
}
  1. 小汽车是可以行驶的,所以Car可以实现Movable接口
public class Car implements Movable
{
    @Override
    public void move()
    {
        System.out.println("汽车行驶中…,模拟核心业务");
    }
}
  1. 在main方法中执行代码
public static void main(String[] args) throws Exception
    {
        Movable car = new Car();
        car.move();
    }

控制台会输出

汽车行驶中…

这时候有新需求,希望在move方法前后分别记录汽车启动和停止的时间,该如何实现呢?

第一种方法:直接修改Car类的代码

在move方法的第一行和最后一行记录汽车开始和停止时间

public class Car implements Movable
{
    @Override
    public void move()
    {
        System.out.println("汽车启动:" + System.currentTimeMillis());
        
        System.out.println("汽车行驶中…,模拟核心业务");

        System.out.println("汽车停止:" + System.currentTimeMillis());
    }
}

这样做有两个缺点:
一是违反了“对扩展开放,对修改封闭的”开闭原则,我们修改了类Car的move方法,无法保证这个修改不会影响既有功能,move方法相关的业务逻辑都需要重新测试。
二是“记录启动时间”不属于核心业务,应该从核心类Car中分离

第二种方法是静态代理模式

  1. 首先定义一个静态代理类CarProxy,CarProxy记录汽车的启动和停止时间,真正的业务调用委托给realMovable
public class CarProxy implements Movable {
    private Movable realMovable;
    public CarProxy(Movable realMovable){
        this.realMovable = realMovable;
    }
    @Override
    public void move() throws Exception {
        //前置代理逻辑
        System.out.println("汽车启动:" + System.currentTimeMillis());

        //真实对象调用
        realMovable.move();

        //后置代理逻辑
        System.out.println("汽车停止:" + System.currentTimeMillis());
    }
}
  1. 执行代码
public static void main(String[] args) throws Exception {
    Movable realCar=new Car();    //创建真实业务对象,真正执行业务操作
    Movable proxyLogCar = new CarProxy(realCar);    //创建代理对象,真实对象作为参数传入
    proxyLogCar.move();
}

执行代码结果如下

汽车启动:1609428420257
汽车行驶中…,模拟核心业务
汽车停止:1609428420258

我们通过使用静态代理模式,在不修改Car类的情况下加入“记录汽车启动停止时间”的业务逻辑。


image.png

上面的例子中,Car和CarProxy要实现共同的接口Movable,意味着CarProxy只能代理实现了Movable接口的对象,不能代理其他接口。 而实际项目中的接口成百上千,为每个接口都创建一个代理类工作量太大,于是就有了动态代理。

动态代理

代理类不是预先写好的,而是在程序运行时动态创建。相比静态代理,动态代理更加灵活高效。

代码实现

JDK通过实现InvocationHandler接口定义代理逻辑,通过Proxy类来生成动态代理对象。

  1. 首先定义代理逻辑
public class LogHanlder implements InvocationHandler
{
    private Object target;

    //构造函数中传入要代理的对象
    public LogHanlder(Object object)
    {
        this.target = object;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        //前置代理逻辑
        System.out.println("汽车启动:" + System.currentTimeMillis());

        //真实对象调用
        method.invoke(target, args);

        //后置代理逻辑
        System.out.println("汽车停止:" + System.currentTimeMillis());

        return null;
    }
}
  1. 定义代理类
public static void main(String[] args) throws Exception
    {
        //要被代理的真实对象是car
        Movable car = new Car();

        //定义代理逻辑处理类,通过代理调用,会转发到这里
        InvocationHandler handler = new LogHanlder(car);
        
       //通过java反射机制,生成动态代理对象
        Movable proxyCar = (Movable) Proxy.newProxyInstance(
                car.getClass().getClassLoader(),   //指定动态代理类的classloader
                new Class[]{Movable.class},        //动态代理类要实现的接口
                handler                            //代理逻辑处理类
        );
        proxyCar.move();
    }

运行代码

汽车启动:1609429682889
汽车行驶中…,模拟核心业务
汽车停止:1609429682890

原理分析

这些类是怎么协同工作的呢?动态代理类在哪里呢?
我们在上述代码main函数中加入如下代码

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

重新运行程序,就能在java工程的 ./sun/proxy目录下看到 $Proxy0.class文件,反编译可以看到$Proxy0实现了Movable接口,在move方法中调用了InvocationHandler 实例的invoke方法,是不是和静态代理类长得很像呀??

public final class $Proxy0 extends Proxy implements Movable {
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    public final void move() throws Exception {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (Exception | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}
  1. java动态代理就是在程序运行时生成代理类的class文件
  2. JVM加载动态class文件
  3. 通过反射生成动态代理对象。

基于jdk实现动态代理,要求被代理类必须实现一个接口,这个条件大多数情况下都可以满足,因为“面向接口编程”要求每个业务类都要实现一个接口,但是个别场景下一些类可能没有实现任何接口,该如何实现动态代理呢?
这个时候就需要借助cglib, asm,javassist等第三方库直接操作字节码生成class文件。

总结

代理模式应用广泛,经常用于隔离变化,以及在代码中增加横切功能。本文介绍了java实现静态代理和动态代理的方法。动态代理的实现原理就是动态生成class文件,利用反射实例化代理对象。实现动态代理可以使用jdk自带的库,也可以借助于cglib, asm,javassist等第三方库。

相关文章

  • 动态代理的两种方式

    静态代理就不说了,基本用到的都是动态代理。 Java中动态代理有JDK动态代理和CGLIB动态代理。 JDK代理的...

  • 浅谈Java和SAP ABAP的静态代理和动态代理,以及ABAP

    文章目录 Java的静态代理 静态代理的优缺点 ABAP的静态代理 Spring AOP的动态代理 JDK动态代理...

  • java反射和动态代理

    java动态代理Proxy.newProxyInstance 详解java代理机制(静态代理、动态代理)以及使用场景

  • 编程常用的设计模式

    动态代理和静态代理 静态代理 动态代理 静态代理与动态代理的区别 JDK中的动态代理和CGLIB 实现动态代理的方...

  • Java动态代理从入门到原理再到实战

    目录 前言 什么是动态代理,和静态代理有什么区别 Java动态代理的简单使用 Java动态代理的原理解读 动态代理...

  • Java基础系列-静态代理和动态代理

    原创文章,转载请标注出处:《Java基础系列-静态代理和动态代理》 1、动态代理(Dynamic Proxy) 代...

  • 代理简记

    Java静态代理 委托类和代理类,实现共同接口 共同接口: 委托类: 代理类: 测试结果: Java动态代理 通过...

  • java的动态代理详解(javassist,cglib)

    俗话说:Coder不知动态代理,走在路上没人理!!!所以本文尝试说明白java代理模式,代理中的静态代理和动态代理...

  • 静态代理、动态代理

    代理分为静态代理和动态代理 按照代理创建的时期,可以分为静态代理和动态代理: (1) 静态代理:由程序员或者...

  • JAVA动态代理的实现方式

    1. 静态代理VS动态代理 代理类可以增强被代理对象的方法。可分为静态代理和动态代理。 1.1 静态代理 静态代理...

网友评论

      本文标题:java静态代理和动态代理

      本文链接:https://www.haomeiwen.com/subject/fafdoktx.html