Java中的静态代理、JDK动态代理和CGLIB动态代理

Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理。

使用代理有两个好处,一是可以隐藏委托类的实现;二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。

举个很常见的例子。工厂会生产很多的玩具,但是我们买玩具都是到商店买的,而不是到工厂去买的,工厂怎么生产我们并不关心,我们只知道到商店可以买到自己想要的玩具,并且,如果我们需要送人的话商店还可以把这些玩具使用礼品盒进行包装。在这里,这个工厂就是委托类,商店就是代理类,我们就是客户类,礼品盒就是代理类对委托类做的一些额外处理。

在Java中有很多场景需要使用代理类,比如远程RPC调用的时候就是通过代理类去实现的,还有Spring的AOP切面中,我们也是为切面生成了一个代理类等等。

代理类主要分为静态代理、JDK动态代理和CGLIB动态代理,它们各有优缺点,没有最好的,存在就是有意义的,在不同的场景下它们会有不同的用途。

Java的静态代理

静态代理首先是定义接口和接口的实现类,然后定义接口的代理对象,并将接口的实例注入到代理对象中,然后通过代理对象去调用真正的实现类,实现过程非常简单也比较容易理解。静态代理的代理关系在编译期间就已经确定了的,适合于代理类较少且确定的情况,可以实现在不修改委托类的情况下做一些额外的处理,比如包装礼盒,实现客户类与委托类的解耦等。但是缺点是只适用于委托方法少的情况下,试想一下,如果委托类有几百上千个方法,岂不是要在代理类中写一堆的代理方法,于是就有了动态代理来解决这个问题,动态代理在后面说。

定义接口和接口的实现类

// 委托接口
public interface SayHelloService {
    void sayHello(String userName);
}

// 委托实现类
public class SayHelloServiceImpl implements SayHelloService {
    public void sayHello(String userName) {
        System.out.println("hello, " + userName);
    }
}

接口的代理类

// 代理类
public class SayHelloProxy implements SayHelloService {
    private SayHelloService sayHelloService = new SayHelloServiceImpl();

public void sayHello(String userName) {
        // 代理事前做一些事情
        System.out.println("do something before proxy...");

// 调用委托类的方法
        sayHelloService.sayHello(userName);

// 代理事后做一些事情
        System.out.println("do something after proxy...");
    }
}

通过代理对象访问委托类

// 测试静态代理类
public class SayHelloTest {
    public static void main(String[] args) {
        SayHelloProxy sayHelloProxy = new SayHelloProxy();
        sayHelloProxy.sayHello("yanggb");
    }
}

Java的动态代理技术

代理类在程序运行时创建的代理方式,就叫做动态代理。在了解动态代理之前,先要回顾JVM的类加载机制中的加载阶段要做的三件事情:

1.通过一个类的全名或其他途径来获取这个类的二进制字节流。

2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3.在内存中生成一个代表这个类的Class对象,作为方法区中对这个类访问的入口。

而动态代理主要就发生在第一个阶段,这个阶段类的二进制字节流的来源可以有很多,比如ZIP包、网络、运行时计算生成、其他文件生成(JSP)和数据库获取等。其中的运行时计算生成就是Java的动态代理技术,在Proxy类中,就是运用了ProxyGenerator.generateProxyClass来为特定接口生成形式为*$Proxy的代理类的二进制字节流。所谓的动态代理就是想办法根据接口或者目标对象计算出代理类的字节码然后加载进JVM中。实际计算的情况会很复杂,因此一般是借助一些诸如JDK动态代理实现、CGLIB第三方库来完成的。因此为了让生成的代理类与目标对象(就是委托类)保持一致,通常有两种做法:通过接口的JDK动态代理和通过继承类的CGLIB动态代理。

因为前面说的两种动态代理都是基于反射来实现的,在运行时查找对象属性、方法、修改作用域、通过方法名称调用方法等。而在线的应用不会频繁使用反射,因为反射的性能开销较大,因此另外还有使用ASM框架的JAVASSIST,相对开销很小,下次用另外的篇幅去说。

JDK动态代理

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/24809789f1cfabf8c6f7da24a50869d5.html