为了解决静态代理的带来的问题:

  • 代理类需要实现与目标类一样的接口,会导致代理类数量较多,不易维护
  • 一旦接口增加方法,目标类和代理类都需要维护

JDK 提供了动态代理,实现动态代理满足下列条件:

  • 代理类实现 InvocationHandler 接口
  • 使用 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法获取代理对象
  • 代理类的通过构造方法传入目标类对象,代理类持有目标类对象
  • 目标类必须实现某个接口,否则无法使用 JDK 动态代理

基于 JDK 的动态代理,实现 Chapter1 中的两个需求

1、打印每个请求从开始到结束的耗时

2、校验某些请求的当前用户是否登录

JDK 动态代理类:

  • 实现 InvocationHandler 接口
  • 重写 invoke 方法,添加业务逻辑
  • 持有目标类对象
  • 提供静态方法获取代理
package constxiong.cxproxy.chapter4.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;/*** JDK 动态代理类* @author ConstXiong* @date 2019-06-02 22:02:15*/
public class JdkDynamicProxy implements InvocationHandler {//获取用户信息的方法名private static final String METHOD_GET_USERINFO = "getUserInfo";private Object target;public JdkDynamicProxy(Object target) {this.target = target;}/*** 提供给 JVM 动态反射调用目标类的方法,返回结果*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;long start = System.currentTimeMillis();//计时开始if (METHOD_GET_USERINFO.equals(method.getName())) {//获取用户信息方法if (checkIsLogined()) {//校验是否登录result = method.invoke(target, args);//目标类的方法调用,与结果获取}} else {//非获取用户信息方法,不校验是否登录result = method.invoke(target, args);//目标类的方法调用,与结果获取}long end = System.currentTimeMillis();//计时结束System.out.println("耗时:" + (end - start) + "毫秒");//打印耗时return result;}/*** 获取代理类对象* @return*/@SuppressWarnings("unchecked")public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);}/*** 模拟  当前用户是否登录*/private boolean checkIsLogined() {Random r = new Random();int i = r.nextInt(10);if (i % 2 == 0) {System.out.println("已登录");return true;}System.out.println("未登录");return false;}}

业务代码:

package constxiong.cxproxy.chapter4.service;import java.util.HashMap;
import java.util.Map;/*** 服务接口实现* @author ConstXiong* @date 2019-05-29 11:02:15*/
public class ServiceImpl implements Service {/*** 登录*/@Overridepublic boolean login(String username, String password) {simulateDaOperation(100);System.out.println("用户名:" + username + ", 密码:" + password + "  登录成功");return true;}/*** 根据用户名获取用户信息*/@Overridepublic Map<String, Object> getUserInfo(String username) {Map<String, Object> userInfo = new HashMap<String, Object>();simulateDaOperation(150);userInfo.put("username", username);userInfo.put("sex", "男");userInfo.put("age", 18);System.out.println("用户名:" + username + ", 获取用户信息:" + userInfo);return userInfo;}/*** 模拟数据库操作,休眠* @param millis 毫秒数*/private void simulateDaOperation(long millis) {try {Thread.sleep(millis);} catch (InterruptedException e) {e.printStackTrace();}}}

测试类:

package constxiong.cxproxy.chapter4;import constxiong.cxproxy.chapter4.proxy.JdkDynamicProxy;
import constxiong.cxproxy.chapter4.service.Service;
import constxiong.cxproxy.chapter4.service.ServiceImpl;/*** 测试类* @author ConstXiong* @date 2019-06-02 22:16:30*/
public class Test {public static void main(String[] args) {JdkDynamicProxy proxy = new JdkDynamicProxy(new ServiceImpl());Service service = proxy.getProxy();service.login("ConstXiong", "123456");service.getUserInfo("ConstXiong");}}

打印的结果跟在 Chapter 2、不使用代理 中的结果一致。

优点:

  • 可以动态地代理实现了接口的目标类
  • 不用创建大量代理类。比如只需要写一个专门的打印方法调用耗时代理类,就可以代理所有的服务类,打印服务类的方法耗时
  • 可以在代理类中对功能进行拦截和扩充

缺点:

  • 目标对象必须实现了接口,否则无法使用 JDK 代理,会抛出 java.lang.ClassCastException 异常

完整源码:https://github.com/ConstXiong/xtools    cxproxy项目 chapter4


【Java面试题与答案】整理推荐

  • 基础与语法
  • 集合
  • 网络编程
  • 并发编程
  • Web
  • 安全
  • 设计模式
  • 框架
  • 算法与数据结构
  • 异常
  • 文件解析与生成
  • Linux
  • MySQL
  • Oracle
  • Redis
  • Dubbo

Chapter 4、JDK 动态代理相关推荐

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

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

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

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

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

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

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

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

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

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

  6. JDK动态代理小例子

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

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

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

  8. java jdk动态代理学习记录

    转载自: https://www.jianshu.com/p/3616c70cb37b JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过 ...

  9. 利用JDK动态代理机制实现简单拦截器

    利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer {public Object execute ...

最新文章

  1. Yii2 操作不同的数据库
  2. 【C 语言】二级指针作为输入 ( 二维数组 | 二维数组遍历 | 二维数组排序 )
  3. Mysql之外连接_OUTER JOIN
  4. Java并发编程:Lock和Synchronized 转
  5. 想写总结,却变成了胡诌。。。
  6. 博客园首页博问闪存新随笔联系订阅管理 随笔- 252 文章- 0 评论- 45 HashPasswordForStoringInConfigFile中的Md5算法并非常用的Md5算法...
  7. 关于collect2: cannot find ld的解决办法
  8. AI+游戏:高效利用样本的强化学习 | 腾讯AI Lab学术论坛演讲
  9. http的请求体body的几种数据格式
  10. java conterller_java后台controller能否直接接收list
  11. win10专业版系统没有休眠选项如何解决?
  12. C# list删除 另外list里面的元素_Java集合大全Map,Set,List
  13. window10系统下载软件教程
  14. java jbpm工作流_JBPM工作流
  15. STorM32三轴云台控制器PID参数调节(1)
  16. SEO和SEM的区别与联系有哪些?SEM和SEO是什么?
  17. c语言用while实现输出加法口诀表,「加法口诀」C语言编写一个加法口诀表 - 金橙教程网...
  18. redis 哨兵 票数_内容组织上的票数
  19. 【知识星球】视频分类模型和数据集板块汇总介绍
  20. element card样式无边框

热门文章

  1. i7 1165g7和i7 12700h差距
  2. STM32看门狗作用
  3. directx11开发中的错误解决
  4. android教育游戏设计方案,基于Android平台的手机教育游戏设计与开发
  5. vue路由跳转错误:Error: Redirected when going from “/login“ to “/home“ via a navigation guard.
  6. 对一个项目如何写一个方案?
  7. Solr(二)-Solrj操作Solr
  8. 魔术表演的核心秘密(三)——扑克手法是如何利用障眼法的?
  9. IDEA的SVN配置
  10. 广度优先算法(BFS)入門理解