# Java Proxy 和 CGLIB 动态代理

Java Proxy & CGLIB

好的博客：<https://www.cnblogs.com/huhx/p/dynamicTheoryAdvance.html>

源码：<https://blog.csdn.net/mhmyqn/article/details/48474815>

CGLIB博客：<http://www.importnew.com/27772.html>

代理比较：<http://www.importnew.com/22015.html>

代理与RPC介绍:<http://www.importnew.com/29359.html>

**场景：**&#x52A8;态代理在Java中有着广泛的应用，比如Spring AOP，Hibernate数据查询、测试框架的后端mock、RPC，Java注解对象获取等

应用：1、代理模式/装饰器模式的动态实现；2、实现AOP；3、实现RPC。

将分散的对对象不同方法的调用转发到一个同一的处理函数中来。

**动态代理**在Java中有着广泛的应用，比如Spring AOP，Hibernate数据查询、测试框架的后端mock、RPC，Java注解对象获取等。**静态代理**的代理关系在编译时就确定了，而动态代理的代理关系是在编译期确定的。静态代理实现简单，适合于代理类较少且确定的情况，而动态代理则给我们提供了更大的灵活性。

1、**静态代理**：

弊端：

> 如果要想为多个类进行代理，则需要建立多个代理类，维护难度加大。
>
> 仔细想想，为什么静态代理会有这些问题，是因为代理在编译期就已经决定，如果代理哪个发生在运行期，这些问题解决起来就比较简单，所以动态代理的存在就很有必要了。

2、**JDK原生动态代理**：

Java动态代理为我们提供了非常灵活的代理机制，但Java动态代理是基于接口的，如果对象没有实现接口我们该如何代理呢？CGLIB登场。

> 1、首先实现一个InvocationHandler，方法调用会被转发到该类的invoke()方法。
>
> 2、然后在需要使用Hello的时候，通过JDK动态代理获取Hello的代理对象。
>
> 好处：
>
> 当动态生成的代理类调用方法时，会触发invoke方法，在invoke方法中可以对被代理类的方法进行增强。
>
> 通过动态代理可以很明显的看到它的好处，在使用静态代理时，如果不同接口的某些类想使用代理模式来实现相同的功能，将要实现多个代理类，但在动态代理中，只需要一个代理类就好了。
>
> 除了省去了编写代理类的工作量，动态代理实现了可以在原始类和接口还未知的时候，就确定代理类的代理行为，当代理类与原始类脱离直接联系后，就可以很灵活地重用于不同的应用场景中。
>
> 弊端：
>
> 代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是，事实上使用中并不是遇到的所有类都会给你实现一个接口。因此，对于没有实现接口的类，就不能使用该机制。
>
> ### 动态代理与静态代理的区别 <a href="#h3_5" id="h3_5"></a>
>
> 1. Proxy类的代码被固定下来，不会因为业务的逐渐庞大而庞大；
> 2. 可以实现AOP编程，这是静态代理无法实现的；
> 3. 解耦，如果用在web业务下，可以实现数据层和业务层的分离。
> 4. 动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题，如果类方法数量越来越多的时候，代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

3、**CGLIB动态代理**

[CGLIB](https://github.com/cglib/cglib)(*Code Generation Library*)是一个基于[ASM](http://www.baeldung.com/java-asm)的字节码生成库，它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。

cglib需要指定父类和回调方法。当然cglib也可以与Java动态代理一样面向接口，因为本质是继承。

cglib是针对类来实现代理的，他的原理是对指定的目标类生成一个子类，并覆盖其中方法实现增强，但因为采用的是继承，所以不能对final修饰的类进行代理。

> 1、首先实现一个MethodInterceptor，方法调用会被转发到该类的intercept()方法。\
> 2、然后在需要使用HelloConcrete的时候，通过CGLIB动态代理获取代理对象。\
> 我们通过CGLIB的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象，最终通过调用create()方法得到代理对象，对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法，在intercept()方法里我们可以加入任何逻辑，比如修改方法参数，加入日志功能、安全检查功能等；通过调用MethodProxy.invokeSuper()方法，我们将调用转发给原始对象。
>
> ```
> final方法是不能重载的，所以也不能通过CGLIB代理，遇到这种情况不会抛异常，而是会跳过final方法只代理其他方法。
> ```

JDK原生动态代理是Java原生支持的，不需要任何外部依赖，但是它只能基于接口进行代理；CGLIB通过继承的方式进行代理，无论目标对象有没有实现接口都可以代理，但是无法处理final的情况。

Java动态代理机制的出现，使得 Java 开发人员不用手工编写代理类，只要简单地指定一组接口及委托类对象，便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行，在分派执行的过程中，开发人员还可以按需调整委托类对象及其功能，这是一套非常灵活有弹性的代理框架。

![](https://2663733811-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Ld4L6S7ar_MexV076MX%2F-Ld4L7xe9wQioYBRrDdX%2F-Ld4LD-Nw1dNyMoH0QZB%2FProxy.png?generation=1555941618576936\&alt=media)

### 从反编译$Proxy0.class源码动态生成的代理类有如下特性：

> 1、继承了Proxy类，实现了代理的接口，由于java不能多继承，这里已经继承了Proxy类了，不能再继承其他的类，所以JDK的动态代理不支持对实现类的代理，只支持接口的代理。
>
> 2、提供了一个使用InvocationHandler作为参数的构造方法。
>
> 3、生成静态代码块来初始化接口中方法的Method对象，以及Object类的equals、hashCode、toString方法。
>
> 4、重写了Object类的equals、hashCode、toString，它们都只是简单的调用了InvocationHandler的invoke方法，即可以对其进行特殊的操作，也就是说JDK的动态代理还可以代理上述三个方法。
>
> 5、代理类实现代理接口的sayHello方法中，只是简单的调用了InvocationHandler的invoke方法，我们可以在invoke方法中进行一些特殊操作，甚至不调用实现的方法，直接返回。

## 1. 动态代理是什么 <a href="#dong-tai-dai-li-shi-shi-mo" id="dong-tai-dai-li-shi-shi-mo"></a>

动态代理提供一种抽象，能够将对象中不同方法的调用重定向到一个统一的处理函数，做自定义的逻辑处理。\
但是对于调用者，对此毫无察觉，就好像调用的方法是用传统方式实现的一般。

这种语法，在java中被称为动态代理。之所以叫做动态代理，是因为它能避免传统代理模式实现中人工一个一个的将java函数转发过去，\
而是能够让代码自动做到这一点，这样代理类的代码是和业务无关的，不会因为业务类的方法增多而逐渐庞大。\
使代码更易维护更易修改，实现自动化搬砖。

实际上，被代理的类不一定位于本机类，动态代理语法提供了一种抽象方式，被代理的类也可以位于远程主机上，这也是RPC框架实现原理的一部分。

理解了动态代理的概念后不难发现，动态代理概念上有着这么几个部分：

1. 给调用者使用的代理类。在java中，我们发现动态代理提供的抽象天然契合面向接口编程，因此它也有可能是接口。
2. 一个统一的处理函数，收集不同函数转发过来的请求，可自定义处理逻辑集中处理。java中它可能会成为一个较独立的部分，因此也可能是类。
