Java 基础:静态代理和动态代理

47 篇文章 0 订阅
12 篇文章 0 订阅

本文介绍了 Java 中的静态代理和动态代理,并明确了它们之间的关系。

一、概述

在某些情况下,我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去访问 A 达成目的,这种编码方式我们就称为代理模式。

这里对象 A 所属类我们称为委托类,也称为被代理类,对象 B 所属类称为代理类。

代理优点有:

  • 隐藏委托类的实现
  • 解耦,不改变委托类代码情况下做一些额外处理,比如添加初始判断及其他公共操作

根据程序运行前代理类是否已经存在,可以将代理分为静态代理和动态代理。

二、静态代理

2.1 普通静态代理

ClassA 是委托类。
ClassB 是代理类。它们有同样的方法,但没有继承接口。
Main 通过 ClassB 来调用 ClassA。

public class ClassA {
    public void op1() {
        System.out.println("ClassA op1");
    }

    public void op2() {
        System.out.println("ClassA op2");
    }

    public void op3() {
        System.out.println("ClassA op3");
    }
}

public class ClassB {
    private ClassA a;

    public ClassB() {
        a = new ClassA();
    }

    public void op1() {
        System.out.println("ClassB op1");
        a.op1();
    }

    public void op2() {
        System.out.println("ClassB op2");
        a.op2();
    }

    public void op3() {
        System.out.println("ClassB op3");
        a.op3();
    }
}

public class Main {
    public static void main(String[] args) {
        ClassB classB = new ClassB();
        classB.op1();
        classB.op2();
        classB.op3();
    }
}

输出:

ClassB op1
ClassA op1
ClassB op2
ClassA op2
ClassB op3
ClassA op3

2.2 接口静态代理

ClassA 是委托类。
ClassB 是代理类。它们实现同一个接口 Operate(或是一个抽象类,甚至是一个普通类,只是用它提供需要被代理的方法)。
Main 通过 ClassB 来调用 ClassA。

public interface Operate {
    void op1();

    void op2();

    void op3();
}

public class ClassA implements Operate {
	// 同上一节的 ClassA
	...
}

public class ClassB implements Operate {
	// 同上一节的 ClassB
	...
}

在 Main 中可以直接调用 Operate 接口

public class Main {
    public static void main(String[] args) {
        Operate classB = new ClassB();
        classB.op1();
        classB.op2();
        classB.op3();
    }
}

输出:

ClassB op1
ClassA op1
ClassB op2
ClassA op2
ClassB op3
ClassA op3

三、动态代理

动态代理是接口静态代理的升级版。

它同样需要一个接口。

public interface Operate {
    void op1();

    void op2();

    void op3();
}

一个接口的实现类 ClassA,作为委托类。

public class ClassA implements Operate {
    @Override
    public void op1() {
        System.out.println("ClassA op1");
    }

    @Override
    public void op2() {
        System.out.println("ClassA op2");
    }

    @Override
    public void op3() {
        System.out.println("ClassA op3");
    }
}

因为是动态生成的,所以不需要事先写好一个 ClassB 类。

ClassB 的实例生成时通过 Proxy.newProxyInstance() 来生成。

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

要在运行时生成一个类,需要这个类的类加载器、类的 class 文件。类的 class 文件用来提供很多东西:类的属性、类的方法、方法的处理逻辑…等等。

可能是为了方便点,Proxy.newProxyInstance() 只能生成一个实现了某个接口的类的实例。这样,类的加载器、类的方法都能够确定下来,类的属性不需要,我们只要提供类中方法的处理逻辑。而加载器、方法、方法逻辑正对应了 newProxyInstance() 方法所需的三个参数。

我们要生成一个实现了 Operate 接口的 ClassB 类。它的加载器就是 Operate 的加载器 Operate.class.getClassLoader(),它里面的方法就是 Operate 的方法 new Class[]{Operate.class},它的方法处理逻辑需要我们自定义。

方法处理逻辑使用 InvocationHandler 来处理。生成类的所有方法调用都会走 InvocationHandler 的 invoke 方法。

public class ClassBInvocationHandler implements InvocationHandler {

    private Object target;

    public ClassBInvocationHandler() {
        target = new ClassA();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("classB " + method.getName());
        Object obj = method.invoke(target, args);
        return obj;
    }
}

上面我们定义了一个 ClassBInvocationHandler,在它的 invoke 方法中打印了 “ClassB” 的信息,然后调用 ClassA 对象的对应方法。

public class Main {
    public static void main(String[] args) {
        Operate classB = (Operate) Proxy.newProxyInstance(
            Operate.class.getClassLoader(),
            new Class[]{Operate.class},
            new ClassBInvocationHandler());

        System.out.println(classB.getClass());

        classB.op1();
        classB.op2();
        classB.op3();
    }
}

输出:

classB op1
ClassA op1
classB op2
ClassA op2
classB op3
ClassA op3

可见,动态代理的效果与静态代理完全一致,同时提供了更好的扩展性。如给 Operate 接口再新增一个 op4() 方法,如果按照静态代理的方式写,ClassA、ClassB 的实现都要修改;而如果按动态代理的方式写,只需要修改 ClassA 的实现,动态生成的 ClassB 完全不用修改也能达到同样的效果。

四、代理的概念梳理

第三节中所述的动态代理。其中的委托类是 ClassA,代理类是动态生成的 ClassB(命名不是ClassB,这里只是代称),他们的委托代理关系是在 Invocation 中建立的。

如果 Invocation 中没有调用 ClassA 的方法,那委托代理关系不存在,所谓的动态代理也就不存在了。

但是按照 Proxy.newProxyInstance() 这个方法的名称来看,它产生一个新的代理实例。都没有代理了,怎么能产生一个代理实例呢。所以我认为,这个方法应该改为 Implement.newImplementInstance(),它不是动态生成代理,而是动态生成接口实现类

只是动态生成的接口实现类中,可以去做代理的逻辑(如在 ClassBInvocationHandler 的 invoke 方法中调用 ClassA 的相应方法)。

我们再来明确一下静态代理和动态代理的关系:

  • 代理是一种设计模式
  • 静态代理是通过手动编写代码,来实现代理模式
  • 动态代理是通过动态生成的接口实现类,来实现代理模式

所以,动态生成的接口实现类不一定要用来实现代理模式,也不一定需要一个委托类,可以只是单纯地,生成一个接口实现类。如 Retrofit 的 api 调用,我们只需要去编写一个包含 api 方法的接口,就可以直接去调用了,原因就是 Retrofit 内部会通过动态生成的接口实现类的方式来生成这个接口的实现类,而它的方法处理逻辑中并没有一个委托类。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值