动态代理的概念

首先来理解动态代理是什么?它能干什么?它的好处或者说优势是什么?

动态代理顾名思义就是动态地代理目标对象执行相应的操作,并且在之间进行功能增强,也就是在执行目标对象的方法同时加上其他需要的业务处理。作用是能代替目标对象执行目标对象的方法,达到调用者的目的同时,还可以进行功能增强的操作,加入其他的业务处理,如日志、验证等处理。

主要就是动态、代理两个词的理解。

动态 :指的是可以动态地代理多个目标对象,而不只是一个,是可以灵活的,所以是动态的。

代理 :指的是可以代替目标对象去执行目标对象要执行的方法,并且可以在不修改目标对象方法的同时加入自己需要的业务处理,也就是功能增强。

举个生活中的例子:比如你要租房找房子,那你就是那个目标对象,你要做的事情就是找房子,但是你可能不是自己亲自去找房子,而是去找中介帮你找房子,那中介就是代理对象,代理对象可以代替你去完成你需要做的事情(找到房子),而且在完成你的目标(找到房子)后,中介会向你收取费用(功能增强),这个本来不在你的目标之内,所以这个是代理对象额外加入的其他业务处理。这样代理对象就不仅完成了目标对象的事情,并可以在这期间中加入其他业务处理。

图解:

说完概念性的知识,现在来看下具体的例子。

需求:模拟计算器执行加减乘除运算

一个数学运算的接口:

package com.zhbit.proxy;/*** @author XieGuanPei* @Description 数字计算接口* @date 2020/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 2020/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 2020/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 2020/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 org.apache.commons.logging.Log;
import org.apache.commons.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 2020/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 2020/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));}
}

运行结果达到需求和目的。完毕!希望能对大家有些帮助!

JDK动态代理(通俗易懂,小白首选)相关推荐

  1. JDK动态代理模式小白式详解

    动态代理模式到底是干啥的? 答:当我们想去修改一个类的实现,而又不能去直接修改.干预该类的代码时,我们就需要一个该类的代理类来辅助我们. 比如,我们想在每次调用某个类的save方法时,都在日志文件里写 ...

  2. 接住喽????,送你个装逼的技能: JDK动态代理

    今天讲一个比较深层的知识点:JDK动态代理,这是个可以让小白在大咖面前装逼的神器,顺便送你一个代理模式的温习机会. 代理模式场景 为了引出动态代理的用法,我们先看看代理设计模式,这能让你了解JDK动态 ...

  3. (转)面试必备技能:JDK动态代理给Spring事务埋下的坑!

    一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...

  4. 【spring】初识aop(面向切面编程) 使用jdk动态代理

    BankServiceIImple.java 代码实现: package com.zzxtit.aop;import java.math.BigDecimal;public interface Ban ...

  5. 【原创】分布式之缓存击穿 【原创】自己动手实现静态资源服务器 【原创】自己动手实现JDK动态代理...

    [原创]分布式之缓存击穿 什么是缓存击穿 在谈论缓存击穿之前,我们先来回忆下从缓存中加载数据的逻辑,如下图所示 因此,如果黑客每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询, ...

  6. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

  7. aop的四种增强以及JDK动态代理、Cglib动态代理

    动态代理 AOP底层实现:有接口自动应用的就是JDK动态代理 (1).JDK 在运行时运行时注入 本质:在内存中构建出接口的实现类 特点:被代理对象,必须有接口 实例: import java.lan ...

  8. JDK动态代理小例子

    一个小汽车,有一个跑run()的方法,我们想使用jdk动态代理使小汽车执行run之前 加点油,run之后洗车. 有四个类,接口Car(小汽车)Kayan(具体实现类(卡宴)) CarProxy(汽车的 ...

  9. jdk动态代理实例和cglib动态代理实例_CGLib 动态代理 原理解析

    JDK 动态代理实现与原理 首先来看一段CGLib代理的测试代码(MethodInterceptor的测试, 其他类型这里不做展开了). Util类的代码在后面给出的码云片段中 public 下面的输 ...

最新文章

  1. solrcloud集群搭建
  2. 用线段拟合曲线,纯Java实现缠论笔段
  3. linux简单快速启用web
  4. Interfaces and Inheritance 接口与继承
  5. 从壹开始【学代码】|| 我开发中的用到的几个框架
  6. 09_ServletContext介绍
  7. MyBatis启动流程分析
  8. python对excel读写操作
  9. 即时通信聊天工具的原理与设计
  10. BBS论坛经典Gif表情包合集
  11. 多目标跟踪算法方案总结
  12. 微信转发软件后缀_简单修改后缀让微信发送25M以上微信大视频
  13. acme 生成通配符 SSL 证书
  14. 写论文,这些工具让你少踩坑!
  15. maven jar坐标和doc引入中央创库没有的jar
  16. win10 内存清理
  17. win10如何删除注册表残留文件
  18. 指向函数的指针 ------ 函数指针(function pointer)
  19. 宝塔linux docker,docker-宝塔
  20. (4.6.26)Android特殊系统的校验方式

热门文章

  1. 服务器被ddos攻击的处置策略
  2. JavaScript之动画特效
  3. 旅行商问题(Travelling salesman problem, TSP)
  4. Raid、裸容量、可用容量的概念
  5. 如何增加自己的文章在csdn的曝光量-参考CSDN博主排名更新公告
  6. JS逆向案例文章推荐
  7. react手机号码344格式分割
  8. 豆豆趣事[2014年05月]
  9. 1362: [蓝桥杯2018初赛]第几个幸运数(简单题)
  10. 川崎机器人here指令_川崎机器人定点修正坐标设置指导书.pdf