Java复习拾遗之动态代理、SpringAOP动态代理

动态代理的实现

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

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

动态代理对象是用Proxy.newProxyInstance()方法创建的,参数列表为:

第一个参数是希望代理的接口的类加载器(或者该接口的实现类的类加载器通过getClass().getClassLoader()可以获取);

第二个参数是希望该代理实现的接口列表(Class对象数组);

第三个参数是InvocationHandler接口的一个实现(Proxy.newProxyInstance()返回值可以强制转换成接口,不能强制转换成实现类,因此Java动态代理,只能代理接口,不能代理类【只能代理接口的意思是,创建代理后,只能通过接口的方式来调用代理的实现。】)。

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

dynamic proxy

其中,

代理类实例proxy的类名是:$Proxy0

proxy中的属性有:m1, m0, m3, m4, m2,

proxy中的方法有:equals, toString, hashCode, doSomething, somethingElse,

proxy的父类是:class java.lang.reflect.Proxy

proxy实现的接口是:opensource.Test$Interface,

动态代理的作用:

动态代理主要用来做方法的增强,就像静态代理一样,我们传入一个要代理的对象,然后所有的处理逻辑都写在代理方法里面。但是动态代理是动态的创建代理,使得我们可以在不修改源码的情况下,增强一些方法,比如在方法执行前后加上我们额外编写的一些逻辑处理,甚至还可以不处理任何事情。

在实现动态代理时,我们在InvocationHandler的invoke方法中,我们可以获取到要被代理的那个方法的Method对象,然后我们就可以添加日志处理、事务等等。另外,我们还可以利用动态代理来实现远程调用,例如现有Java接口的实现部署在远程服务器上,然后就可以引入接口,动态创建要调用的远程服务的代理,通过Proxy.newProxyInstance()方法创建该接口对应的InvocationHandler对象代理,然后,我们在InvocationHandler的invoke方法内部封装通讯细节和远程调用等内容,从而使得调用远程服务就像调用本地接口一样简单。其实很多的远程调用框架实现比如RMI、hessian、dubbo、各种webservice框架等等都是使用类似方法实现的。

动态代理示例

我们来看一下动态代理的示例,首先,我们创建一个要被代理的接口:

interface

其次,创建该接口的一个实现:

interface实现

然后,我们实现InvocationHandler接口来创建我们的动态代理:

invocationhandler

这样,我们就完成了动态代理的创建,最后,我们测试一下动态代理:

dynamicproxy

输出:

登录前处理

欢迎 张三 登录系统

登录后处理

Spring AOP动态代理

Spring AOP通过代理模式实现,目前支持两种代理方式:JDK动态代理、CGLIB代理来创建AOP代理,Spring建议优先使用JDK动态代理。当然,在使用Spring的时候,也可以引入AspectJ来实现代理。

JDK动态代理如上面所述:使用Proxy.newProxyInstance()方法来动态的创建被代理类的代理实现,即提取目标对象的接口,然后对接口创建代理,该创建的代理对象的类关系与被代理接口的实现类平级。

CGLIB代理:CGLIB代理不仅能进行接口代理,也能进行类代理,CGLIB代理需要注意以下问题:

不能代理final方法,因为final方法不能被覆盖(CGLIB通过生成子类来创建代理)。

会产生两次构造器调用,第一次是目标类的构造器调用,第二次是CGLIB生成的代理类的构造器调用。如果需要CGLIB代理方法,请确保两次构造器调用不影响应用。

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

对于Schema风格配置切面使用如下方式来指定使用CGLIB代理:<aop:config proxy-target-class="true"></aop:config>

而如果使用@AspectJ风格使用如下方式来指定使用CGLIB代理:<aop:aspectj-autoproxy proxy-target-class="true"/>

Spring AOP代理示例

首先,我们创建接口和实现,如:

interface and implement

其次,我们使用AspectJ风格来创建切面、切点等代理,如:

pointcut

然后,我们运行:

使用Spring获取bean,会自动的创建代理

最后,查看结果:

aop 结果

更多文章

Java复习拾遗之sql、util等日期完全详解和高级用法

Spring源码之JdbcTemplate中的坑,你中招了吗

Java中最重要的并发编程抽象工具类

爬虫进阶:CrawlSpider爬取169ee全站美女图片

Python网络爬虫requests、bs4爬取空姐网图片

Java序列化进阶:Java内置序列化的三种方式

Java高并发多线程之读写锁完全解析