如果有错,希望指出
动态代理在Java中有着广泛的应用,比如 Spring Aop、Hibernate 数据查询、RPC、Java 注解对象的获取。静态代理的代理关系在编译时确定,而动态代理的代理关系是在编译期确定的。主要了解:JDK Proxy 和 cglib 动态代理。
动态代理可以提供另一个对象的访问,同时可以隐藏实际对象的具体实例。
静态代理
再说动态代理之前,先了解下静态代理。
静态代理是代理模式实现方式之一,在程序运行前,由程序员创建或者特定工具自动生成源代码并对其编译生成 .class 文件。静态代理的实现需要三步:
- 定义业务接口
- 实现业务接口
- 定义代理类并实现业务接口
最后就可以直接通过客户顿进行调用静态代理了。
静态代理总结:
- 优点:可以做到不对目标对象进行修改的前提下,对目标对象进行功能的扩展和拦截。
- 缺点:因为代理对象,需要实现和目标对象一样的接口,会导致代理对象十分繁多,不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。
JDK Proxy
先实现一个接口。
1 | public interface Hello { |
下面使用java.lang.reflect.Proxy
实现动态代理
1 | public class MyInvocationHandler implements InvocationHandler { |
newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。理解上述代码需要对Java反射机制有一定了解。动态代理神奇的地方就是:
- 代理对象是在程序运行时产生的,而不是编译期;
- 对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;之后我们通过某种方式执行真正的方法体,示例中通过反射调用了Hello对象的相应方法,还可以通过RPC调用远程方法。
cglib
GCLIB(Code Generation Library)是一个基于 ASM 的字节码生成库,它允许在运行时对字节码进行修改和动态生成。
1 | //实现MethodInterceptor方法 |
总结
JDK Proxy
- JDK动态代理只能代理有接口的类,并且是只能代理接口方法,不能代理一般的类中的方法
- 提供了一个使用 InvocationHandler 作为参数的构造方法,在代理类中做一层包装,业务逻辑在 invoke 方法中实现;
- 重写了 Object 类的 equals、hashCode、toString,它们都只是简单的调用了InvocationHandler 的 invoke 方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法;
- 在 invoke 方法中我们甚至可以不用
Method.invoke
方法调用实现类就返回。这种方式常常用在 RPC 框架中,在 invoke 方法中发起通信调用远端的接口等。
CGlib
- CGlib 可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类;
- 由于是继承方式,如果是 static方法、private方法、final方法等描述的方法是不能被代理的;
- 做了方法访问优化,使用建立方法索引的方式避免了传统 Method 的方法反射调用;
- 提供 callback 和 filter 设计,可以灵活地给不同的方法绑定不同的 callback。编码更方便灵活。
- CGLIB 会默认代理 Object 中 finalize、equals、toString、hashCode、clone等方法。比JDK代理多了finalize和clone。