动态代理的概念
首先来理解动态代理是什么?它能干什么?它的好处或者说优势是什么?
动态代理顾名思义就是动态地代理目标对象执行相应的操作,并且在之间进行功能增强,也就是在执行目标对象的方法同时加上其他需要的业务处理。作用是能代替目标对象执行目标对象的方法,达到调用者的目的同时,还可以进行功能增强的操作,加入其他的业务处理,如日志、验证等处理。
主要就是动态、代理两个词的理解。
动态:指的是可以动态地代理多个目标对象,而不只是一个,是可以灵活的,所以是动态的。
代理:指的是可以代替目标对象去执行目标对象要执行的方法,并且可以在不修改目标对象方法的同时加入自己需要的业务处理,也就是功能增强。
举个生活中的例子:比如你要租房找房子,那你就是那个目标对象,你要做的事情就是找房子,但是你可能不是自己亲自去找房子,而是去找中介帮你找房子,那中介就是代理对象,代理对象可以代替你去完成你需要做的事情(找到房子),而且在完成你的目标(找到房子)后,中介会向你收取费用(功能增强),这个本来不在你的目标之内,所以这个是代理对象额外加入的其他业务处理。这样代理对象就不仅完成了目标对象的事情,并可以在这期间中加入其他业务处理。
图解:
说完概念性的知识,现在来看下具体的例子。
需求:模拟计算器执行加减乘除运算
一个数学运算的接口:
package com.zhbit.proxy;/*** @author XieGuanPei* @Description 数字计算接口* @date /9/2 - 6:26*/public interface Calculate {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);}
实现类(目标类):
package com.zhbit.proxy;/*** @author XieGuanPei* @Description 目标类* @date /9/2 - 6:24*/public class Calculator implements Calculate{@Overridepublic int add(int i,int j) {return i + j;}@Overridepublic int sub(int i,int j) {return i - j;}@Overridepublic int mul(int i,int j) {return i * j;}@Overridepublic int div(int i,int j) {return i / j;}}
现在假如说我需要在这个运算功能的基础上加入日志和验证功能(只能是正数进行运算),
那我们应该怎么做呢?
我们可以这么做(原始做法):就是在每个方法的逻辑基础上加上日志功能和验证功能。如下:
package com.zhbit.proxy;/*** @author XieGuanPei* @Description 目标类* @date /9/2 - 6:24*/public class Calculator implements Calculate{@Overridepublic int add(int i,int j) {Validate.validate(i,j);System.out.println("log: add method begin,args[:" + i + "+" + j + "]");int ret = i + j;System.out.println("log: add method end,result[" + (i+j) + "]");return ret;}@Overridepublic int sub(int i,int j) {Validate.validate(i,j);System.out.println("log: add method begin,args[:" + i + "-" + j + "]");int ret = i - j;System.out.println("log: add method end,result[" + (i-j) + "]");return ret;}@Overridepublic int mul(int i,int j) {Validate.validate(i,j);System.out.println("log: add method begin,args[:" + i + "*" + j + "]");int ret = i * j;System.out.println("log: add method end,result[" + (i*j) + "]");return ret;}@Overridepublic int div(int i,int j) {Validate.validate(i,j);System.out.println("log: add method begin,args[:" + i + "/" + j + "]");int ret = i / j;System.out.println("log: add method end,result[" + (i/j) + "]");return ret;}}
封装的验证模块:
package com.zhbit.proxy;/*** @author XieGuanPei* @Description 验证模块* @date /9/2 - 6:18*/public class Validate {public static void validate(int i, int j) {if (i<0 || j<0) {throw new IllegalArgumentException(" It is not positive number !!!");}}}
由上可以看出:因为要增加日志和验证功能,我们不得不在原有的业务方法内加入这些非业务需求的代码,使得代码量剧增,而且写了很多重复的代码,如果日志需要发生变化,那就得修改所有运算模块,牵一发而动全身!!! 因此这种做法非常不好,这就需要用动态代理模式来解决这个问题了。
接下来我们用动态代理的方式改造代码,看看效果如何:
首先明确思路,将非业务需求代码抽取出来作为代理对象的功能增强部分,而代理对象去执行目标对象的运算方法的同时加入这些功能增强部分,这样就不用影响到我们核心业务代码部分了,而且抽取出来的日志和验证功能同样可以适用其他需要这些处理的模块,减少代码冗余,符合“开放封闭原则”,对扩展开放,对修改封闭。
编写工具类创建代理对象:
package com.zhbit.proxy;import mons.logging.Log;import mons.logging.LogFactory;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;/*** @author XieGuanPei* @Description JDK动态代理* @date /9/2 - 6:21*///方式一:public class ProxyUtils {private Log log = LogFactory.getLog(this.getClass());private Object target;public ProxyUtils(Object target) {this.target = target;}public Object getProxyInstance() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//遍历每个参数进行验证,需要先判断是否有参数,否则可能报空指针异常if (args !=null && args.length > 0) {for (Object arg : args) {validate((Integer) arg);}}//加入日志和验证功能log.info( method.getName() + "method begin,args" + Arrays.toString(args));//执行目标对象方法Object ret = method.invoke(target, args);log.info( method.getName() + "method end,ret=" + ret);return ret;}});}private static void validate(Integer num) {if (num < 0) {throw new IllegalArgumentException(" It is not positive number !!!");}}}// 方式二://public class ProxyUtils implements InvocationHandler{// private Object target;//// public ProxyUtils(Object target) {// this.target = target;// }//// public Object getProxyInstance() {// return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);// }//// @Override// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// System.out.println("代理对象功能增强的部分,执行其他业务处理1");// Object ret = method.invoke(target,args);// System.out.println("代理对象功能增强的部分,执行其他业务处理2");// return ret;// }//}
接下来解读一下代码中重要的方法和参数
1.Proxy类 : 通 过 JDK 的 java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 会 使 用 其 静 态 方 法
newProxyInstance(),依据目标对象、业务接口及调用处理器三者,自动生成一个动态代理对象
public static newProxyInstance ( ClassLoader loader, Class<?>[] interfaces,
InvocationHandler handler)
loader:目标类的类加载器,通过目标对象的反射可获取
interfaces:目标类实现的接口数组,通过目标对象的反射可获取
handler: 调用处理器。
参数解读:
①loader: 因为要生成一个代理对象,对象必须依赖于类才能生成,所以这里需要先加载代理类,那就需要类加载器去加载到内存中,这里可以使用目标类的加载器。
②interfaces: 代理对象要去调用目标对象的方法,那得先知道目标对象中都有哪些方法,那就得需要代理类去实现跟目标类相同的接口们,这样就能知道目标对象中存在哪些方法了。
③handler : 调用处理器接口实现类的实例对象。上面已经知道了目标对象的方法了,接下来就是执行了,就在调用处理器接口实现类中的invoke()方法中调用目标对象的方法,这样就能代替目标对象执行方法了,而且还可以在invoke()方法中添加额外的业务处理(功能增强)。
2.InvocationHandler 接口
InvocationHandler 接口叫做调用处理器,负责完调用目标方法,并增强功能。
当我们通过代理对象调用目标接口中的方法时,自动调用此接口实现类中的invoke()方法,该方法中写的就是需要调用的目标方法和进行功能增强部分的业务处理。
测试代码:
package com.zhbit.test;import com.zhbit.proxy.Calculate;import com.zhbit.proxy.Calculator;import com.zhbit.proxy.ProxyUtils;import org.junit.Test;/*** @author XieGuanPei* @Description* @date /9/2 - 6:32*/public class TestProxyUtils {@Testpublic void test() {//创建工具类对象ProxyUtils proxyUtils = new ProxyUtils(new Calculator());//创建代理对象Calculate proxyInstance = (Calculate) proxyUtils.getProxyInstance();//System.out.println(proxyInstance);//代理对象执行目标对象方法System.out.println(proxyInstance.add(1,5));System.out.println(proxyInstance.div(6,2));System.out.println(proxyInstance.sub(5,6));System.out.println(proxyInstance.mul(3,2));}}
运行结果达到需求和目的。完毕!希望能对大家有些帮助!