代理模式-动态代理

(1)

(2)

代码实践动态代理:

第一步:被代理类的接口:

package com.JdkProxy;

public interface Moveable {

    void move();

}

第二步:被代理类:

package com.JdkProxy;

import java.util.Random;

public class Car implements Moveable {

    @Override
public void move()
{ //实现开车
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中....");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

第三步:代理类:实现接口:InvocationHandler,同时把被代理类对象接口传入构造方法,

重写的接口的invoke方法。

package com.JdkProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class TimeHandler implements InvocationHandler { private Object target; public TimeHandler(Object target)
{
this.target=target;
} /*
* 参数:
* proxy:被代理对象
* method:被代理对象方法
* arg:方法的参数
* 返回值:
* Object 方法的返回值
* */
@Override
public Object invoke(Object proxy, Method method, Object[] arg) throws Throwable { long starttime=System.currentTimeMillis();
System.out.println("汽车开始形式...."); method.invoke(target); long endtime=System.currentTimeMillis();
System.out.println("汽车结束行驶...汽车形式时间:"+(endtime-starttime)+"毫秒"); return null;
} }

测试类中实现代理:

使用Proxy类的newProxyInstance方法产生一个被代理类的实例,该实例可以当作被代理类使用接口(对应cls.getInterfaces())中声明过的方法。

package com.JdkProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; public class Test { /*JDK动态代理测试类
* */
public static void main(String[] args) { Car car=new Car(); //InvocationHandler是一个接口,接口中定义了一个方法invoke。要想实现JDK动态代理,
//代理类必须继承这个接口
InvocationHandler h=new TimeHandler(car);//
Class cls=car.getClass();//获取类对象,以便获取类加载器,以及获取类的接口 /*
* newProxyInstance返回代理类的实例,返回后的代理类可以当作被代理类使用
* (可使用被代理类的接口中声明过的方法)
* loader:类加载器
* interfaces:实现接口
* h:InvocationHandler
* */
Moveable m=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), h);

m.move(); } }

测试结果:

汽车开始形式....
汽车行驶中....
汽车结束行驶...汽车形式时间:863毫秒

代理模式-动态代理

所以动态代理是这样一种Class:

  • 他在运行时候产生了的Class
  • 该class需要实现一组interface
  • 使用动态代理类时,必须实现InvocationHandler接口

动态代理实现步骤

1,创建一个实现InvocationHandler的类,他必须实现Invoke方法

2,创建被代理的类以及接口

3,调用Proxy的静态方法,创建一个代理类:

newProxyInstance(ClassLoader,class[] interfaces,InvocationHandler  h)。

4,通过代理调用方法。

jdk动态代理只能实现了接口的类。

jdk动态代理理解:

看完这个例子,我的最大疑惑是,返回代理类实例,执行方法的时候,调用的是被代理类中含有的方法,那么我们代理类中重写的invoke方法,并没有调用,

那这个方法有什么用,但是真正执行被代理类里面的方法的时候,却好像又确实执行了invoke方法,那么到底时候用到了invoke这个方法呢

这里看了一下这篇博客的解释,稍微能理解一下这种动态代理的处理执行过程:https://juejin.im/post/5a99048a6fb9a028d5668e62。

在静态代理部分,我们在代理类中传入了被代理对象。可是,使用newProxyInstance生成动态代理对象的时候,我们居然不再需要传入被代理对象了
我们传入了的实际对象是InvocationHandler实现类的实例,这看起来有点像生成了InvocationHandler的代理对象,
在动态生成的代理类的任意方法中都会间接调用InvocationHandler->invoke(proxy, method, args)方法。 其实的确是这样。TimeProxy真正代理的对象就是InvocationHandler,不过这里设计的巧妙之处在于,InvocationHandler是一个接口,
真正的实现由用户指定。另外,在每一个方法执行的时候,invoke方法都会被调用 ,这个时候如果你需要对某个方法进行自定义逻辑处理,
可以根据method的特征信息进行判断分别处理

看完这段文字,再看这个方法的执行:

        Car car=new Car();
InvocationHandler h=new TimeHandler(car);
Class cls=car.getClass();
Moveable m=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), h);

总结步骤:

  • InvocationHandler:这个接口主要用于自定义代理逻辑处理
  • 为了完成对被代理对象的方法拦截,我们需要在InvocationHandler对象中传入被代理对象实例。
  • Proxy->newProxyInstance用于生成代理对象。
  • newProxyInstance的三个参数:
  • 前两个参数与被代理类的Class有关(被代理类的类加载器,被代理类的接口)。
  • 最后一个参数传入代理类InvocationHandler的实例。

想象一下,到此为止,如果我们还需要对其它任意对象进行代理,是否还需要改动newProxyInstance方法的源码,答案是:完全不需要!

只要你在newProxyInstance方法中指定代理需要实现的接口指定用于自定义处理的InvocationHandler对象

整个代理的逻辑处理都在你自定义的InvocationHandler实现类中进行处理。

至此,而我们终于可以从不断地写代理类用于实现自定义逻辑的重复工作中解放出来了,从此需要做什么,交给InvocationHandler。

答疑解惑

invoke方法的第一个参数proxy到底有什么作用?

1. 可以使用反射获取代理对象的信息(也就是proxy.getClass().getName(),输出可得:com.sun.proxy.$Proxy0)。
2. 可以将代理对象返回以进行连续调用,这就是proxy存在的目的。因为this并不是代理对象,指的是被代理对象实例(如例子中的car),

proxy指的是被代理对象。这个问题其实也好理解,如果你的接口中有方法需要返回自身,如果在invoke中没有传入这个参数,将导致实例无法正常返回。

在这种场景中,proxy的用途就表现出来了。简单来说,这其实就是最近非常火的链式编程的一种应用实现。

动态代理到底有什么用?

学习任何一门技术,一定要问一问自己,这到底有什么用。其实,在这篇文章的讲解过程中,我们已经说出了它的主要用途。你发现没,使用动态代理我们居然可以在不改变源码的情况下,直接在方法中插入自定义逻辑。这有点不太符合我们的一条线走到底的编程逻辑,这种编程模型有一个专业名称叫 AOP。所谓的AOP,就像刀一样,抓住时机,趁机插入。

 
 

模式的秘密-代理模式(2)-JDK动态代理的更多相关文章

  1. 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance

    浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...

  2. Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程

    spring-aop-4.3.7.RELEASE  在<Spring AOP高级——源码实现(1)动态代理技术>中介绍了两种动态代理技术,当然在Spring AOP中代理对象的生成也是运用 ...

  3. 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

  4. 从静态代理,jdk动态代理到cglib动态代理-一文搞懂代理模式

    从代理模式到动态代理 代理模式是一种理论上非常简单,但是各种地方的实现往往却非常复杂.本文将从代理模式的基本概念出发,探讨代理模式在java领域的应用与实现.读完本文你将get到以下几点: 为什么需要 ...

  5. 代理模式详解:静态代理、JDK动态代理与Cglib动态代理

    代理模式简介分类 概念 ​ 代理,是为了在不修改目标对象的基础上,增强目标方法的业务逻辑. ​ 客户类需要执行的是目标对象的目标方法,但是真正执行的是代理对象的代理方法,客户类对目标对象的访问是通过代 ...

  6. 动态代理模式——JDK动态代理

    今天,我就来讲一下动态代理的设计模式. 动态代理的意义在于生成一个代理对象,来代理真实对象,从而控制真实对象的访问.操作动态代理需要两个步骤:一.代理对象和真实对象建立代理关系.二.实现代理对象的代理 ...

  7. Cglib 与 JDK动态代理的运行性能比较

    都说 Cglib 创建的动态代理的运行性能比 JDK 动态代理能高出大概 10 倍,今日抱着怀疑精神验证了一下,发现情况有所不同,遂贴出实验结果,以供参考和讨论. 代码很简单,首先,定义一个 Test ...

  8. java学习笔记(中级篇)—JDK动态代理

    一.什么是代理模式 相信大家都知道代理商这个概念,在商业中,代理商无处不在.假设你要去买东西,你不可能去找真正的厂家去买,也不可能直接跟厂家提出需求,代理商就是这中间的一桥梁,连接买家和厂商.你要买或 ...

  9. CGlib和JDK动态代理

    一.CGlib动态代理     JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了.CGLib采用了非常底层的1:字节码技术,其原理是通过字节 ...

  10. JDK动态代理、CGLIB动态代理详解

    Spring的AOP其就是通过动态代理的机制实现的,所以理解动态代理尤其重要. 动态代理比静态代理的好处: 1.一个动态代理类可以实现多个业务接口.静态代理的一个代理类只能对一个业务接口的实现类进行包 ...

随机推荐

  1. poj 1733 并查集+hashmap

    题意:题目:有一个长度 已知的01串,给出多个条件,[l,r]这个区间中1的个数是奇数还是偶数,问前几个是正确的,没有矛盾 链接:点我 解题思路:hash离散化+并查集 首先我们不考虑离散化:s[x] ...

  2. hdu 3294 manacher 求回文串

    感谢: http://blog.csdn.net/ggggiqnypgjg/article/details/6645824/ O(n)求给定字符串的以每个位置为中心的回文串长度. 中心思想:每次计算位 ...

  3. bzoj 1051 强连通分量

    反建图,计算强连通分量,将每个分量看成一个点,缩点后的图是一个DAG,如果是一棵树,则根代表的连通分量的大小就是答案,否则答案为0. 收获: 图的东西如果不好解决,可以尝试缩点(有向图将每个强连通分量 ...

  4. Get buck-boost performance from a boost regulator

    The SEPIC (single-ended, primary-inductance-converter) topology is generally a good choice for volta ...

  5. Spring EL hello world实例

    Spring EL与OGNL和JSF EL相似,计算评估或在bean创建时执行.此外,所有的Spring表达式都可以通过XML或注解. 在本教程中,我们将学习如何使用Spring表达式语言(SpEL) ...

  6. Struts2中表单与Action传递数据三种方式

    1.       Action中的属性与表单中的属性一致就可以 JSP中的表单 <form action="login.action" method="post&q ...

  7. llvm中如何利用分支概率和基本块频率估计

    1. 背景 llvm自2.9版以后,已经集成了对分支概率和基本块频率的静态分析. 分支概率(branch probability)是指在程序的控制流图中,从控制流从一个基本块A到其任意后继基本块Si的 ...

  8. gpu和cpu区别

    GPU的功耗远远超过CPUCache, local memory: CPU > GPU Threads(线程数): GPU > CPURegisters: GPU > CPU 多寄存 ...

  9. 2 cocos2dx 3.0 源码分析-Director

    Director 导演类, 这个类在整个引擎中担当着最重要的角色, 先看看它是如何初始化的,它共管理了哪些内容呢?    1初始化- 更新处理Scheduler   Scheduler 这个类负责用户 ...

  10. Orchard运用 - 导入旧随笔导致归档的问题

    归档功能对于一个博客App这应该必须有的需求,故此Orchard中博客模块默认实现这一特性并通过一个叫"Blog Archives" widget, 你可以将其安装博客主页的侧边栏 ...