Java反射学习总结四(动态代理使用实例和内部原理解析)
通过上一篇文章介绍的静态代理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
- public class DynamicProxy implements InvocationHandler {
- // 需要被代理类的引用
- private Object object;
- // 通过构造方法传入引用
- public DynamicProxy(Object object) {
- this.object = object;
- }
- // 定义一个工厂类,去生成动态代理
- public Object getProxy() {
- // 通过Proxy类的newProxyInstance方法动态的生成一个动态代理,并返回它
- return Proxy.newProxyInstance(object.getClass().getClassLoader(), object
- .getClass().getInterfaces(), this);
- }
- // 重写的invoke方法,这里处理真正的方法调用
- @Override
- public Object invoke(Object obj, Method method, Object[] args)
- throws Throwable {
- beforeDoing();
- Object invoke = method.invoke(object, args);
- afterDoing();
- return invoke;
- }
- public void beforeDoing() {
- System.out.println("before ............");
- }
- public void afterDoing() {
- System.out.println("after ............."+"\n");
- }
- }
该类实现了InvocationHandler接口,并且自定义了一个getProxy()方法去调用Proxy类的newProxyInstance()去生成一个动态代理。
步骤3:创建被代理的类以及接口
- //真实角色对象,继承自抽象角色,重写定义的方法。
- public class RealSubject implements Subject1,Subject2{
- //Subject1接口中的方法
- @Override
- public void request() {
- System.out.println("this is real subject");
- }
- //Subject1接口中的方法
- @Override
- public void ask() {
- System.out.println("this is real ask");
- }
- //Subject2接口中的方法
- @Override
- public void request2() {
- System.out.println("this is real subject2");
- }
- }
这个类就是我们需要被代理的类,他继承了两个接口分别是Subject1,Subject2
- interface Subject1 {
- public void request();
- public void ask();
- }
- interface Subject2 {
- public void request2();
- }
4.通过代理调用方法
接下来在main方法中通过动态生成的代理来调用方法
- public static void main(String[] args) {
- //需要被代理的类
- RealSubject realSubject = new RealSubject();
- //用于创建动态代理的类,将被代理类的引用传递进去
- DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
- //通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
- Subject1 s1 = (Subject1) dynamicProxy.getProxy();
- s1.request();
- s1.ask();
- //通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
- Subject2 s2 = (Subject2) dynamicProxy.getProxy();
- s2.request2();
- }
最后打印:
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接口了
有了接口数组,就可以通过类似下面的代码使用反射拿到接口中的所有方法
- for (interface infce : interfaces[]) {
- Method[] methods = infce.getMethods();
- for (Method m : method) {
- m.getName();
- }
- }
在正常情况下,知道了被代理的接口和接口里面的方法就可以去生成代理类了。
大概就是下面这种的一个简单的实现:一个很固定的套路,只要知道实现接口和方法就仿照写出。
- public class ProxySubject implements Subject{
- private RealSubject realSubject;
- @Override
- public void request() {
- realSubject.request();
- }
- }
动态代理还会在代理的方法中做一些其他的操作,如添加日志,时间,权限等操作。这时候就要靠InvocationHandler接口中的invoke方法。看看例子中如何实现的。
- @Override
- public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
- beforeDoing();
- Object invoke = method.invoke(object, args);
- afterDoing();
- return invoke;
- }
这些代码是我们自定义的,需要实现什么操作就写在里面。
这段代码存在于Invocationhandler对象中,这个对象会在调用Proxy的newProxyInstance的方法中传递进去。
这时候可以通过反射知道被调用方法的名字等信息,之后还是通过字符串的形式拼接处类似下面的动态代理类
- public class ProxySubject implements Subject{
- private RealSubject realSubject;
- @Override
- public void request() {
- Methond md = Subject.getMethod("methodName");
- handler.invoke(this, md);
- }
- }
这个大概就是根据传递的接口对象和InvocationHandler结合后应该生成的代理类。但现在的问题是如何去动态的生成上面这样的代理类。
答案是使用字符串拼接的方式。
从看上面的代码可以看出,除了接口和调用方法不同其他都相同。而且我们已经通过反射获得了方法和接口名字,这样就可以按着这个“套路”去用字符串拼接成这样的一类。
大概就是下面这种代码:
- String source = "package com.gxy.proxy;" + rt
- + "public class "+ClassName+"implements "+InterfaceName+ rt
- + "{" + rt
- + "private "+ ClassName + ClassName.toLowerCase()+" ; " + rt
- + "@Override"
- + "public Void "+InterfaceName+ "()" + rt + " {"
- + "Method md = "+InterfaceName+".getMethod("+ methodName+");" +rt
- + "hander.invoke(this, md);" + rt
- + "}" + rt
- + "}";
用反射生成的出来类名,接口名,方法名去动态的创建这样一个类的字符串。
之后就特定的方法去将这个字符串生成成类。在用反射把这个类取出来。这样就有了这个“动态”生成的代理类了。
就简单介绍到这吧。。。
最后大家可以发现例子中的动态代理里都是通过接口来实现的,如果对于不能实现接口的类就不能用JDK的动态代理了。如果想用就需要使用cglib了,因为cglib是针对类来实现的。
关于动态代理我研究了一个多礼拜,觉得理解起来还是比较困难的,勉勉强强的知道了个大概。
以后有时间我会继续深入的学习动态代理,但暂时还是以反射为主。下一篇准备写一下反射与注解的内容,希望大家多多支持。
Java反射学习总结四(动态代理使用实例和内部原理解析)的更多相关文章
- java反射机制应用之动态代理
1.静态代理类和动态代理类区别 静态代理:要求被代理类和代理类同时实现相应的一套接口:通过代理类的对象调用重写接口的方法时,实际上执行的是被代理类的同样的 方法的调用. 动态代理:在程序运行时,根据被 ...
- java 反射之静态and动态代理
首先说一下我们什么情况下使用代理? (1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the c ...
- 深入分析Java反射(四)-动态代理
动态代理的简介 Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分 ...
- Java学习笔记--JDK动态代理
1.JDK动态代理 JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和 ...
- 分享非常有用的Java程序 (关键代码)(四)---动态改变数组的大小
原文:分享非常有用的Java程序 (关键代码)(四)---动态改变数组的大小 /** * Reallocates an array with a new size, and copies the co ...
- Java多线程学习(四)等待/通知(wait/notify)机制
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79690279 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- Java反射学习系列-绪论
Java反射学习系列-绪论 https://blog.csdn.net/hanchao5272/article/details/79358924
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
- Java反射学习总结终(使用反射和注解模拟JUnit单元测试框架)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 本文是Java反射学习总结系列的最后一篇了,这里贴出之前文章的链接,有兴趣的可以打开看看. ...
随机推荐
- #学习笔记#——JavaScript 数组部分编程(六)
14. 题目描述 实现一个打点计时器,要求 1.从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1 2.返回的对象中需 ...
- 【记录】无法读取配置节“AppSettings”,因为它缺少节声明
Web.config对大小写敏感, 把AppSettings改为appSettings即可.
- Flask框架简介
Flask框架诞生于2010年,是Armin ronacher 用python语言基于Werkzeug工具箱编写的轻量级Web开发框架! Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展. ...
- 【Codeforces Round #456 (Div. 2) C】Perun, Ult!
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] set1 < pair < int,int > > set1;记录关键点->某个人怪物永远打不死了,第 ...
- 经验之谈—让你看明确block
事实上我感觉不常常使用block的朋友.对block应该是比較陌生的,那么如今我们来扒开block的真面目,看看block的本质 普通的局部变量.block内部仅仅会引用它初始的值(block定义那一 ...
- FZU 2205 据说题目很水
2205 据说题目很水 Accept: 199 Submit: 458Time Limit: 1000 mSec Memory Limit : 32768 KB Problem Descr ...
- vue踩坑记-在项目中安装依赖模块npm install报错
在维护别人的项目的时候,在项目文件夹中安装npm install模块的时候,报错如下: npm ERR! path D:\ShopApp\node_modules\fsevents\node_modu ...
- iOS界面生命周期过程具体解释
开发过Android的人都知道,每个Android界面就是一个Activity,而每个Activity都会有自己的生命周期, 有一系列方法会控制Activity的生命周期.如:onCreate(),o ...
- Android平台中的三种翻页效果机器实现原理
本文给开发者集中展现了Android平台中的三种翻页效果机器实现原理,希望能够对开发者有实际的帮助价值! 第一种翻页效果如下: 实现原理: 当前手指触摸点为a,则 a点坐标为(ax,ay), ...
- shell脚本中的反引号,单引号,双引号与反斜杠
转自:http://blog.sina.com.cn/s/blog_6561ca8c0102we2i.html 反引号位 (`)经常被忽略,而且容易与单引号弄混.它位于键盘的Tab键的上方.1键的左方 ...