Java动态代理的原生实现和Spring AOP的实现

Java中的动态代理

动态代理是动态地创建代理并动态地处理对所代理方法的调用。

实现动态代理需要实现InvocationHandler接口,实现其invoke(object, method, args[])函数,传递的是一个代理实例(Proxy类库的$Proxy0)、方法和参数。

Java动态代理的创建

动态代理对象是用静态方法Proxy.newProxyInstance()方法创建的:

  1. 第一个参数是希望代理的接口的类加载器,
  2. 第二个参数是希望该代理实现的接口列表(Class对象数组),
  3. 第三个参数是InvocationHandler接口的一个实现(Proxy.newProxyInstance()返回值可以强制转换成接口,不能强制转换成实现类,因此Java动态代理,只能代理接口,不能代理类【只能代理接口的意思是,创建代理后,只能通过接口的方式来调用代理的实现。】)。

动态代理代理的方法都会经过第三个参数的对象所实现的invoke方法来进行代理调用,因此通常会向调用处理器传递一个“实际对象”的引用(如下面的readObject对象),从而使得调用处理器可以将请求转发。如下图:

上图中:

  1. 代理类实例proxy的类名是:$Proxy0
  2. proxy中的属性有:m1, m0, m3, m4, m2
  3. proxy中的方法有:equals, toString, hashCode, doSomething, somethingElse
  4. proxy的父类是:class java.lang.reflect.Proxy
  5. proxy实现的接口是:opensource.Test$Interface

动态代理的作用

动态代理主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。还有一个有趣的作用是可以用作远程调用,比如现在有Java接口,这个接口的实现部署在其它服务器上,在编写客户端代码的时候,没办法直接调用接口方法,因为接口是不能直接生成对象的,这个时候就可以考虑代理模式(动态代理)了,通过Proxy.newProxyInstance代理一个该接口对应的InvocationHandler对象,然后在InvocationHandler的invoke方法内封装通讯细节就可以了。具体的应用,最经典的当然是Java标准库的RMI,其它比如hessian,各种webservice框架中的远程调用,大致都是这么实现的。见下面的例子:

Spring AOP动态代理

Spring AOP通过代理模式实现,目前支持两种代理:

  1. JDK动态代理。
  2. CGLIB代理来创建AOP代理。

Spring建议优先使用JDK动态代理。

JDK动态代理

使用java.lang.reflect.Proxy动态代理实现,即提取目标对象的接口,然后对接口创建AOP代理。

CGLIB代理

CGLIB代理不仅能进行接口代理,也能进行类代理。

CGLIB代理需要注意以下问题:不能代理final方法,因为final方法不能被覆盖(CGLIB通过生成子类来创建代理);会产生两次构造器调用,第一次是目标类的构造器调用,第二次是CGLIB生成的代理类的构造器调用(CGLIB要求被代理的类要有默认的构造函数,因为子类实例化时会隐式调用super());如果需要CGLIB代理方法,请确保两次构造器调用不影响应用。

Spring AOP默认首先使用JDK动态代理来代理目标对象,如果目标对象没有实现任何接口将使用CGLIB代理,如果需要强制使用CGLIB代理,请使用如下方式指定:

  1. 对于Schema风格配置切面使用如下方式来指定使用CGLIB代理:<aop:config proxy-target-class="true"></aop:config>
  2. 而如果使用@AspectJ风格使用如下方式来指定使用CGLIB代理:<aop:aspectj-autoproxy proxy-target-class="true"/>

更快更便捷的获取Java技术知识,欢迎关注我的微信订阅号!

发布了14 篇原创文章 · 获赞 37 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/c315838651/article/details/104025698