通过上一篇文章介绍的静态代理Java反射学习总结三(静态代理)中,大家可以发现在静态代理中每一个代理类只能为一个接口服务,这样一来必然会产生过多的代理,而且对于每个实例,如果需要添加不同代理就要去添加相应的代理类。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能或者说去动态的生成这个代理类,那么此时就必须使用动态代理完成。

动态代理知识点:

Java动态代理类位于java.lang.reflect包下,主要有以下一个接口和一个类:

1.InvocationHandler接口:    该接口中仅有一个方法

public object invoke(Object obj, Method method, Object[] args)

在实际使用时,obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象的invoke方法在代理类中动态实现。

2.Proxy类:  该类即为动态代理类,这里只介绍一下newProxyInstance()这个方法

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

这个方法是最主要的方法,它会返回代理类的一个实例,返回后的代理类可以当做被代理类使用

实现动态代理需4步骤:

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法。

2.通过Proxy的静态方法newProxyInstance创建一个代理

3.创建被代理的类以及接口

4.通过代理调用方法

下面看这个例子具体说明如何通过上面的4个步骤来建立一个动态代理:

步骤1和步骤2合并写在一个类中,命名为DynamicProxy

  1. public class DynamicProxy implements InvocationHandler {
  2. // 需要被代理类的引用
  3. private Object object;
  4. // 通过构造方法传入引用
  5. public DynamicProxy(Object object) {
  6. this.object = object;
  7. }
  8. // 定义一个工厂类,去生成动态代理
  9. public Object getProxy() {
  10. // 通过Proxy类的newProxyInstance方法动态的生成一个动态代理,并返回它
  11. return Proxy.newProxyInstance(object.getClass().getClassLoader(), object
  12. .getClass().getInterfaces(), this);
  13. }
  14. // 重写的invoke方法,这里处理真正的方法调用
  15. @Override
  16. public Object invoke(Object obj, Method method, Object[] args)
  17. throws Throwable {
  18. beforeDoing();
  19. Object invoke = method.invoke(object, args);
  20. afterDoing();
  21. return invoke;
  22. }
  23. public void beforeDoing() {
  24. System.out.println("before ............");
  25. }
  26. public void afterDoing() {
  27. System.out.println("after ............."+"\n");
  28. }
  29. }

该类实现了InvocationHandler接口,并且自定义了一个getProxy()方法去调用Proxy类的newProxyInstance()去生成一个动态代理。

步骤3:创建被代理的类以及接口

  1. //真实角色对象,继承自抽象角色,重写定义的方法。
  2. public class RealSubject implements Subject1,Subject2{
  3. //Subject1接口中的方法
  4. @Override
  5. public void request() {
  6. System.out.println("this is real subject");
  7. }
  8. //Subject1接口中的方法
  9. @Override
  10. public void ask() {
  11. System.out.println("this is real ask");
  12. }
  13. //Subject2接口中的方法
  14. @Override
  15. public void request2() {
  16. System.out.println("this is real subject2");
  17. }
  18. }

这个类就是我们需要被代理的类,他继承了两个接口分别是Subject1,Subject2

  1. interface Subject1 {
  2. public  void request();
  3. public void ask();
  4. }
  1. interface Subject2 {
  2. public  void request2();
  3. }

4.通过代理调用方法

接下来在main方法中通过动态生成的代理来调用方法

  1. public static void main(String[] args) {
  2. //需要被代理的类
  3. RealSubject realSubject = new RealSubject();
  4. //用于创建动态代理的类,将被代理类的引用传递进去
  5. DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
  6. //通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
  7. Subject1 s1 = (Subject1) dynamicProxy.getProxy();
  8. s1.request();
  9. s1.ask();
  10. //通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
  11. Subject2 s2 = (Subject2) dynamicProxy.getProxy();
  12. s2.request2();
  13. }

最后打印:

before ............
this is real subject
after .............

before ............
this is real ask
after .............

before ............
this is real subject2
after .............

简单介绍动态代理内部实现原理:

例子看完了,肯定有如下疑问:

动态代理在哪里应用了反射机制?仅仅通过一个InvocationHandler接口和一个Proxy类的newProxyInstance方法是如何动态的生成代理?

下面就来简单的分析一下InvocationHandler,和Proxy的newProxyInstance方法是如何在运行时动态的生成代理的:

以下代码都是伪代码而且内容大部分参考马士兵动态代理的视频。如果感兴趣,建议找视频去学。

先看newProxyInstance是如何定义的

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

这里需要传入3个参数。先看第二个参数,传入一个接口类型的Class数组。

上面例子中传入的参数是object.getClass().getInterfaces()

object是被代理对象,这个参数就是通过反射拿到被代理对象的所有接口

在上面例子中就是我们定义的Subject1,Subject2接口了

有了接口数组,就可以通过类似下面的代码使用反射拿到接口中的所有方法

  1. for (interface infce : interfaces[]) {
  2. Method[] methods = infce.getMethods();
  3. for (Method m : method) {
  4. m.getName();
  5. }
  6. }

在正常情况下,知道了被代理的接口和接口里面的方法就可以去生成代理类了。

大概就是下面这种的一个简单的实现:一个很固定的套路,只要知道实现接口和方法就仿照写出。

  1. public class ProxySubject implements Subject{
  2. private RealSubject realSubject;
  3. @Override
  4. public void request() {
  5. realSubject.request();
  6. }
  7. }

动态代理还会在代理的方法中做一些其他的操作,如添加日志,时间,权限等操作。这时候就要靠InvocationHandler接口中的invoke方法。看看例子中如何实现的。

  1. @Override
  2. public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
  3. beforeDoing();
  4. Object invoke = method.invoke(object, args);
  5. afterDoing();
  6. return invoke;
  7. }

这些代码是我们自定义的,需要实现什么操作就写在里面。

这段代码存在于Invocationhandler对象中,这个对象会在调用Proxy的newProxyInstance的方法中传递进去。

这时候可以通过反射知道被调用方法的名字等信息,之后还是通过字符串的形式拼接处类似下面的动态代理类

  1. public class ProxySubject implements Subject{
  2. private RealSubject realSubject;
  3. @Override
  4. public void request() {
  5. Methond md = Subject.getMethod("methodName");
  6. handler.invoke(this, md);
  7. }
  8. }

这个大概就是根据传递的接口对象和InvocationHandler结合后应该生成的代理类。但现在的问题是如何去动态的生成上面这样的代理类。

答案是使用字符串拼接的方式。

从看上面的代码可以看出,除了接口和调用方法不同其他都相同。而且我们已经通过反射获得了方法和接口名字,这样就可以按着这个“套路”去用字符串拼接成这样的一类。

大概就是下面这种代码:

  1. String source = "package com.gxy.proxy;" + rt
  2. + "public class "+ClassName+"implements "+InterfaceName+ rt
  3. + "{" + rt
  4. +       "private "+ ClassName + ClassName.toLowerCase()+" ; " + rt
  5. +       "@Override"
  6. +       "public Void "+InterfaceName+ "()" + rt  + " {"
  7. +           "Method md = "+InterfaceName+".getMethod("+ methodName+");" +rt
  8. +           "hander.invoke(this, md);" + rt
  9. +       "}" + rt
  10. + "}";

用反射生成的出来类名,接口名,方法名去动态的创建这样一个类的字符串。

之后就特定的方法去将这个字符串生成成类。在用反射把这个类取出来。这样就有了这个“动态”生成的代理类了。

就简单介绍到这吧。。。

最后大家可以发现例子中的动态代理里都是通过接口来实现的,如果对于不能实现接口的类就不能用JDK的动态代理了。如果想用就需要使用cglib了,因为cglib是针对类来实现的。

关于动态代理我研究了一个多礼拜,觉得理解起来还是比较困难的,勉勉强强的知道了个大概。

以后有时间我会继续深入的学习动态代理,但暂时还是以反射为主。下一篇准备写一下反射与注解的内容,希望大家多多支持。

Java反射学习总结四(动态代理使用实例和内部原理解析)的更多相关文章

  1. java反射机制应用之动态代理

    1.静态代理类和动态代理类区别 静态代理:要求被代理类和代理类同时实现相应的一套接口:通过代理类的对象调用重写接口的方法时,实际上执行的是被代理类的同样的 方法的调用. 动态代理:在程序运行时,根据被 ...

  2. java 反射之静态and动态代理

    首先说一下我们什么情况下使用代理? (1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the c ...

  3. 深入分析Java反射(四)-动态代理

    动态代理的简介 Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分 ...

  4. Java学习笔记--JDK动态代理

    1.JDK动态代理     JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和 ...

  5. 分享非常有用的Java程序 (关键代码)(四)---动态改变数组的大小

    原文:分享非常有用的Java程序 (关键代码)(四)---动态改变数组的大小 /** * Reallocates an array with a new size, and copies the co ...

  6. Java多线程学习(四)等待/通知(wait/notify)机制

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79690279 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  7. Java反射学习系列-绪论

    Java反射学习系列-绪论 https://blog.csdn.net/hanchao5272/article/details/79358924

  8. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  9. Java反射学习总结终(使用反射和注解模拟JUnit单元测试框架)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 本文是Java反射学习总结系列的最后一篇了,这里贴出之前文章的链接,有兴趣的可以打开看看. ...

随机推荐

  1. LBP 特征

    LBP(Local Binary Pattern,局部二值模式)是一种用来描述图像局部纹理特征的算子:它具有旋转不变性和灰度不变性等显著的优点.用于纹理特征提取.而且,提取的特征是图像的局部的纹理特征 ...

  2. PHP foreach遍历数组之如何判断当前值已经是数组的最后一个

    先给出foreach的两种语法格式 1,foreach (array_expression as $value) statement 2,foreach (array_expression as $k ...

  3. Android开发之ConstraintLayout相对布局

    介绍 一个 ConstraintLayout 是一个 ViewGroup 允许您以灵活的方式定位和调整小部件的方法. 注意: ConstraintLayout 作为支持库提供,您可以在API级别9(G ...

  4. 网络流最大流dinic的板子

    void add(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nt=pre[u]; pre[u]=tot++; e[tot].v=u; e[t ...

  5. CODEVS——T1183 泥泞的道路

    时间限制: 1 s  空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解  查看运行结果 题目描述 Description CS有n个小区,并且任意小区之间都有两条单向道路(a到 ...

  6. POJ——T2271 Guardian of Decency

    http://poj.org/problem?id=2771 Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 5932   A ...

  7. secureCRT The remote system refused the connection问题解决

    问题: Ubuntu系统必须开启ssh服务后,XP或者其它的主机才干够远程登陆到Ubuntu系统. 1,安装软件包,运行sudo apt-get install openssh-server Ubun ...

  8. Django环境搭建(一)

    搭建Django环境之前先搭建python运行环境 需要了解: 解释器(编译器): 计算机不能直接理解任何除机器语言外的其他语言,所以程序员必须要把自己写的语言翻译成机器语言,而将其他语言翻译成机器语 ...

  9. ajax上传进度条

    <script type="text/javascript"> function register(){ var frm = document.getElementBy ...

  10. Testin云測与ARM 战略合作:推动全球移动应用加速进入中国市场

    Testin云測与ARM 战略合作:推动全球移动应用加速进入中国市场 2014/10/14 · Testin · 业界资讯 (中国北京–2014年10月14日 )全球最大的移动游戏.应用真机和用户云測 ...