Java动态代理
Java动态代理的实现方式主要有两种:
1、基于JDK的动态代理:动态代理类和被代理类必须继承相同的接口(没有实现接口的类,无法通过这种方式实现动态代理。所以也称基于接口的动态代理)
2、基于CGLIB的动态代理:没有上面的限制
在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者后续操作,同时又不干预原先的业务实现。 把一些基本业务和主要的业务逻辑分离,Spring的AOP就是基于动态代理实现的
关于动态代理或者说AOP的好处:在业务处理代码中,通常都有日志记录、性能统计、安全控制、事务处理、异常处理等操作,尽管使用OOP(面向对象),可以通过封装或继承达到代码的重用,但仍然存在同样的代码分散到各个方法中。为了解决此类问题,AOP(面向切面)诞生了,它采取横向抽取机制,将分散在各个方法中的重复代码抽取出来,然后再程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制,是OOP无法做到的,因为OOP实现的是父子关系的纵向继承,而AOP则对OOP的缺陷做了补充,两者相辅相成。
下面进入正题
1、基于JDK的动态代理
基于JDK的动态代理是通过两个类 1、InvocationHandler(调用处理 接口) 2、Proxy(代理 类) 来实现的
实现基于JDK的动态代理 首先要创建具有一定功能的接口,然后创建接口的实现类(也就是被代理类)
譬如,我们假设周杰伦要开演唱会了 为了实现这一逻辑,我需要创建一个歌手接口Singer,接口里有演唱的方法sing。之后我们为这个接口创建一个实现类(也就是我们的被代理类)ZhouJielun
代码如下:
interface Singer{ void sing(); } //接口实现类(也就是被代理类) class ZhouJielun implements Singer{ @Override public void sing() { System.out.println("周杰伦在演唱会进行演唱"); } }
在开演唱会之前,造型师势必会对他的造型进行修理,这个过程是必须的,而这个过程它不由歌手本人负责,因为每个人的精力都是有限的。这个逻辑换成工作开发就是什么呢?每个人都负责一定的功能模块,如果一个人负责过多的任务,那么就很有可能出现各种各样的问题,同时也会造成各种功能代码耦合在一起,不便于维护。 希望这个解释帮助大家更清晰的认识到AOP的好处。
接着说如何实现动态代理,现在,接口创建好了,被代理的类也创建好了 ,之后我们创建一个代理工厂。
这就需要用到上面说的 InvocationHandler接口 和 Proxy 类了(先贴代码,再说流程)
首先,我们创建一个MyInvocationHandler类并实现InvocationHandler接口,代码如下:
class MyInvocationHandler implements InvocationHandler { //这是 被代理类的对象 private Object obj; //捆绑方法:将被代理的对象捆绑到 MyInvocationHandler(调用处理器) 类 //当然,该操作也可以在构造函数中完成 public void bind(Object obj){ this.obj = obj; } //当我们通过”代理对象“调用方法时,就会自动调用如下方法:invoke() @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method:即为要调用的被代理类的方法 //args:即为要调用方法的参数 doBefore(); //调用被代理类中的方法 Object returnValue = method.invoke(obj,args); doAfter(); return returnValue; } //需要插入的前置代码 private void doBefore(){ System.out.println("造型师帮助化妆"); } //需要插入的后置代码 private void doAfter(){ System.out.println("造型师帮助卸妆"); } }
接着创建一个代理工厂类,代码如下:
class ProxyFactory{ public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } }
在main方法中测试一下:
public class ProxyTest { public static void main(String[] args) { ZhouJielun zhouJielun = new ZhouJielun(); Singer proxyInstance = (Singer) ProxyFactory.getProxyInstance(zhouJielun); proxyInstance.sing(); } }
简单说一下整个过程:在Proxy类中有一个静态方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h),它需要传入一个类加载器,一个代表对象实现接口的Class数组,一个调用处理器,然后返回一个实现了所有传入接口代理对象。 在上面的代码中,我们选择创建了一个代理工厂,它的静态方法可以根据接受的对象,返回需要的代理类。
InvocationHandler顾名思义,就是调用处理器。 就是它完成了额外功能的插入:它里面内置了一个被代理类的对象 和 一个invoke()方法,当我们通过代理类调用任意方法时,都会调用invoke()方法。 它的代码逻辑很简单:通过反射调用被代理对象的同名方法,而我们在原方法执行前后,可以插入一些其他代码。 比如在执行这段方法前,进行日志记录。 这样一来,成功将不同的功能分离,使得代码更加易于阅读。
2、基于CGLIB的动态代理
还没学