动态代理

动态代理源于设计模式中的代理模式,代理模式的主要作用就是使代理对象完成用户的请求,屏蔽用户对真实对象的访问。通过代理对象去访问目标对象来控制原对象的访问。

代理模式的最典型的应用就是 Spring AOP。

静态代理

代理模式的实现有两种,静态代理和动态代理,静态代理的代理类是需要程序员去写的,而动态代理的代理类是自动生成的。

静态代理需要持有被代理对象的引用,通过这个引用去调用被代理对象的方法。

我们来看一个静态代理的实例:

首先定义一个接口,代理对象和被代理对象都需要实现这个接口。

public interface IService{
void sayHello();
}

被代理的对象:

public class RealClass implements IService{
@Override
public void sayHello(){
System.out.println("hello world.....");
} public void doService(){
System.out.println("doing service.....");
}
}

真正的代理类需要做的事情:

public class ProxyClass implements IService{
//被代理对象的引用
private RealClass realClass; public ProxyClass(RealClass realClass){
this.realClass = realClass;
} @Override
public void sayHello(){
System.out.println("i am proxy, prepare for saying hello...");
realClass.sayHello();
System.out.println("i am proxy, saying hello ending...");
} public void doService(){
System.out.println("i am proxy, prepare for saying hello...");
realClass.doService();
System.out.println("i am proxy, saying hello ending...");
}
}

一般来说,代理类会选择直接继承被代理类所有的接口和父类以便于实现所有被代理类的方法。

到这里其实静态代理就讲完了,也没有什么难点。但是动态代理不同于静态代理的点在于,代理类不用我们自己写。

JDK 动态代理

动态代理区别于静态代理的一点是,动态代理的代理类由虚拟机在运行时动态创建并于虚拟机卸载时清除。

我们还用上面的 RealClass 类,看看 JDK 的动态代理是如何实现的。

首先定义一个 Handler 类,它继承自 InvocationHandler。

public class MyHandler implements InvocationHandler{
//同样需要传入被代理类的引用,这里我们直接使用 Object 类型
private Object realObject; public MyHandler(Object realObj){
this.realObject = realObj;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
System.out.println("proxy begainning ... ");
// 通过反射的方式,调用被代理对象的方法
Object result = method.invoke(realObj,args);
System.out.println("proxy ending....");
return result;
}
}

代理类的生成以及调用过程:

public static void main(String[] args){
RealClass rc = new RealClass();
MyHandler handler = new MyHandler(rc);
Object obj = Proxy.newProxyInstance(rc.getClass().getClassLoader(),new Class[]{IService.class},handler);
}
  1. 首先我们定义处理类,它继承了 InvocationHandler 并实现了 invoke 方法。这里面还是需要传入被代理对象的引用。
  2. InvocationHandler 的实现类里面通过反射来调用被代理兑现的方法,还可以加上一些新的功能。
  3. 通过 JDK 提供的 Proxy 类的 newProxyInstance 方法来构建代理类,里面需要传入三个参数,类加载器,被代理类的所有接口,第三个是我们自己定义的 InvocationHandler 的实现类。

我们通过反编译来看看这个代理类的 Class 到底有什么不同:

  1. 生成的代理类的名字是很随意的,一个程序中如果有多个代理类药生成,【$PRroxy + 数字】就是它们的类名。
  2. 这个代理类继承了 Proxy 类并且实现了 IService 接口(之前如果指定多个,这里就会继承多个)。
  3. 代理类的构造器需要传入 InvocationHandler 类型的参数。并且将这个参数传递给了父类。这也是为什么所有代理类都必须使用 Proxy 作为父类的一个原因。

我们继续看代理类下面的内容:

  1. 包裹在 static 里面的静态代码块很重要,通过反射获取了四个 Method。其中有三个是 Object 类的常用方法,也就是说代理类还会代理被代理对象从 Object 继承来的方法,还有一个是被代理类的接口的方法。

  1. 最后一部分我们看到的是,虚拟机根据静态初始化代码块所反射出来的所有代理方法,为他们生成代理的方法。
  2. 调用时需要从父类 Proxy 中取出构造实例化时存储的处理器类,并调用它的 invoke 方法。
  3. 第一个参数是当前代理类的实例(事实证明这个参数并没有什么用),第二个参数是 Method 方法实例,第三个参数是方法的形式参数集合,如果没有就是 null。

总结

  1. 一个处理器类的定义是必不可少的,它的内部必须关联一个真实对象,即被代理类实例。
  2. 我们从外部调用代理类的任意一个方法,从反编译的源码我们知道,代理类方法会转而去调用处理器的 invoke 方法并传入方法签名和参数。

缺陷

我们需要注意到,虚拟机生成的代理类为了共用 Proxy 类中的 InvocationHandler 字段来存储自己的处理器类实例而继承了 Proxy 类。

这里有个问题,说明代理类不能在继承其他类了。那么被代理类父类的方法自然就无法获取了,即代理类无法代理被代理类中父类的任何方法,只能代理接口中的方法,这也是我们常说的 JDK 动态代理必须实现接口的原因。

RealClass 自己的方法 doService 也没有被代理,只有接口中的方法被代理了。所以说,JDK 的动态代理机制是单一的,它只能代理被代理类的接口集合中的方法。。

类中的非接口方法和父类中的方法是无法被代理的。

不友好的返回值。

public static void main(String[] args){
RealClass rc = new RealClass();
MyHandler handler = new MyHandler(rc);
Object obj = Proxy.newProxyInstance(rc.getClass().getClassLoader(),new Class[]{IService.class},handler);
}

newProxyInstance 返回的是代理类 【Proxy0】的一个实例,但是它是以 Object 类型进行返回的,而你又不能把它强转成 【Proxy0】类型。因为编译期是不存在这个【Proxy0】类型的,所以一般只会强转为该代理类实现的接口之一。

IService obj = (IService)Proxy.newProxyInstance(
rc.getClass().getClassLoader(),
new Class[]{IService.class},
hanlder);
obj.sayHello();

现在问题又来了,加入我们被代理类实现了多个接口,那么你该强转为哪个接口类型哪?可能需要你多次进行转换,这样的设计相当不友好。

下一篇我们将介绍一个广为各类框架使用的 CGLIB 动态代理库,它的底层基于字节码操作框架 ASM,不再依赖继承来实现,完美的解决了 JDK 的单一代理的不足。

动态代理之 JDK 动态代理的更多相关文章

  1. 静态代理与JDK动态代理

    demo地址: https://github.com/ZbLeaning/leaning 代理: 为其他对象提供一种代理以控制对这个对象的访问.分为静态代理和动态代理.代理模式的目的就是为真实业务对象 ...

  2. Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)

    一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法. ...

  3. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

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

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

  5. Java代理(静态代理、JDK动态代理、CGLIB动态代理)

    Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...

  6. 代理模式之静态代理,JDK动态代理和cglib动态代理

    代理模式,顾名思义,就是通过代理去完成某些功能.比如,你需要购买火车票,不想跑那么远到火车站售票窗口买,可以去附近的火车票代售点买,或者到携程等第三方网站买.这个时候,我们就把火车站叫做目标对象或者委 ...

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

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

  8. 静态代理和利用反射形成的动态代理(JDK动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 静态代理 1.新建 ...

  9. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  10. 动态代理:JDK动态代理和CGLIB代理的区别

    代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...

随机推荐

  1. 本地运行angularjs应用,提示出现跨域问题

    应用介绍: 使用angularjs创建一个简单的公司员工管理页面 使用Chrome打开后,提示存在跨域问题. 和别人交流后,得知使用像Angular,React,Vue框架,在Chrome地址栏中输入 ...

  2. laravel 增加不存在数据库的字段

    有时候您可能想要增加不存在数据库字段的属性数据.这时候只要定义一个获取器即可: public function getIsAdminAttribute() { return $this->att ...

  3. xml path 列转行实例

    SQL Server2005提供了一个新查询语法——For XML PATH(''),这个语法有什么用呢?想象一下这样一个查询需求:有两个表,班级表A.学生表B,要查询一个班级里有哪些学生?针对这个需 ...

  4. JavaScript 按位与和逻辑与

    逻辑与操作符有两个和好(&&)表示,有两个操作数,如下面的例子所示: var result = true && false;   第一个操作数 第二个操作数 结果 tr ...

  5. Java Integer类的缓存

    首先看一段代码(使用JDK 5),如下: public class Hello { public static void main(String[] args) { int a = 1000, b = ...

  6. python的for循环、下标和切片

    for循环的格式   for 临时变量 in 列表或者字符串:     循环满足条件时执行的代码 else:     循环不满足条件时执行的代码   例: name = "abcdef&qu ...

  7. Mac下SVN基本操作和常见错误

    一.基本操作 1  从服务器上下载代码 svn checkout http://xxx.xxx.xxx/xxx 2  获取最新的代码 svn update 3  提交代码 svn commit -m ...

  8. 清除SVN未版控文件

    用Git时,git clean -df 可以清除所有没有add的文件,得到一个干净的工作空间. 用SVN没有这样的命令,当然可以 svn export 得到一个干净的工作空间,但会花很长时间,而且没有 ...

  9. C# json 转 xml 字符串

    本文告诉大家如何将 json 转 xml 或将 xml 转 json 字符串 首先需要安装 Newtonsoft.Json 库,打开 VisualStudio 2019 新建一个 dotnet cor ...

  10. linux单 open 设备

    提供存取控制的强力方式是只允许一个设备一次被一个进程打开(单次打开). 这个技术最 好是避免因为它限制了用户的灵活性. 一个用户可能想运行不同的进程在一个设备上, 一 个读状态信息而另一个写数据. 在 ...