Java 动态代理介绍及用法
来源:互联网 发布:淘宝开店交税吗 编辑:程序博客网 时间:2024/05/25 05:36
Java 动态代理介绍及用法
一,静态代理模式的特点
在之前的文章中 java代理模式 已经介绍里java里的(静态)代理模式
下面是上文静态代理类的例子:
public class ProxyBear implements Hitable<Dog>{ private Hitable<Dog> f = null; public ProxyBear(){ if (null == f){ f = new Fox(); } } @Override public void hit(Dog g){ if (null != f){ System.out.println("Bear hit InterDogChicken!"); f.hit(g); System.out.println("Bear bite InterDogChicken!"); } }
从代码可以看出, 上面代理类可以增强被代理的对象的某个方法。
但是被代理对象的类型Hitable是指定1个接口(或抽象类)
也就是讲上静态代理类只适用于某一种指定的接口实现类, 如果某个对象没有实现揍狗接口, 而是其他另1个接口, 那么就必须新建1个代理类的。即使新代理类也是实现同样的功能
二. 动态代理的1个需求
就用会上文的例子
我们宜家有两个接口。殴打和调戏接口..
殴打接口 Hitable
public interface Hitable<T> { public void hit(T o);}
调戏接口 Molestable
public interface Molestable<T> { public void molest(T o);}
其中狐狸类实现了殴打狗接口
public class Fox implements Hitable<Dog> { @Override public void hit(Dog g){ this.sap(g); this.uppercut(g); } //闷棍 private void sap(Dog g){ System.out.println("give " + g.getName() + " a Sap!"); } //上勾拳 private void uppercut(Dog g){ System.out.println("give " + g.getName() + " a Uppercute!"); }}
狼类实现调戏狗接口
public class Wolf implements Molestable<Dog> { @Override public void molest(Dog o) { System.out.println("wolf laugh at the dog!"); System.out.println("wolf ruffle the dog!"); }}
这个是万恶的狗类
public class Dog { private String name; public Dog(String name){ this.setName(name); } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Dog [name=" + name + "]"; }}
问题来了, 依家狗太恶, 狼和狐狸都要找头熊(代理)看着才敢去惹狗
如果用静态代理, 就如本文一开始的那个代理类, 则只能单独为狐狸or 狼代理, 这个需求需要两个代理类, 因为狗和狐狸实现的是两个不同的接口
如下图
那么有没有一种代理类, 可以同时代理多个接口的实现类呢,java 里动态代理就利用了反射实现了这个功能。
三,动态代理的例子
我们利用动态代理类重新写了1个熊类出来:
熊
public class dynamicProxyBear implements InvocationHandler { //delegate means proxy //the object which will be delegated private Object delegate; public Object bind(Object delegate){ this.delegate = delegate; /** * This method newProxyInstance return ***one of the interfaces*** of delegate object(properties object of this class) * * @param * 1.ClassLoader loader -> usually use delegate object's class loader * 2.Class<?>[] interfaces -> collection of the interface which delegate object has implemented * 3.InvocationHandler h -> this * @return */ return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this); } /** * This method will replace all the method owned by delegate object. * @param proxy -> the delegate object, never use it's method directly, otherwise will lead to Dead loop * @param method -> the method (once execute will fall into this invoke method) object of delegate object * @param args -> parameters of the mothod. * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result; if (args.length < 1){ return method.invoke(this.delegate,args); } //bear watching System.out.println("bear is watching " + args[0].toString()); result = method.invoke(this.delegate,args); System.out.println("bear leaved " + args[0].toString()); return result; }}
下面对上面的代码进行分析。
首先这个代理类要实现接口InvacationHandler, 而且不会直接提供要代理的方法(行为), 而是生成1个被代理完成(强化)的新对象。
delegate 成员对象
//delegate means proxy //the object which will be delegated private Object delegate;
这个成员变量就是为了存放被代理的对象(Fox or wolf 对象)
bind(Object delegate) 方法
public Object bind(Object delegate){ this.delegate = delegate; /** * This method newProxyInstance return ***one of the interfaces*** of delegate object(properties object of this class) * * @param * 1.ClassLoader loader -> usually use delegate object's class loader * 2.Class<?>[] interfaces -> collection of the interface which delegate object has implemented * 3.InvocationHandler h -> this * @return */ return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this); }
这个bind方法就是为了绑定要代理的对象, 并return 1个被代理后的对象啦
注意 参数Object delegate就是本文例子中的狼or狐狸对象, 因为参数类型是Object, 也就是说它可以同时适用于各种类(前提是实现了某种接口)
Proxy.newProxyInstance 这个静态方法就是返回1个被代理后的对象。
第一个参数是类加载器, 通常使用被代理对象的类加载器, 通常我们就用delegate.getClass().getClassLoader()
第二个参数是被代理对象实现的接口列表, 通常我们就用delegate.getClass().getInterface()
第三个参数是这个代理类对象本身
值得注意的是, 这个方法返回的也是1个Object类型, 在client端中, 我们可以将返回对象强制转换成被代理类的1个接口类型对象, 但是不能强制转换成被代理类本身的1个对象。
至于这个静态方法里面的细节这里就不讲了。
重写方法invoke
/** * This method will replace all the method owned by delegate object. * @param proxy -> the delegate object, never use it's method directly, otherwise will lead to Dead loop * @param method -> the method (once execute will fall into this invoke method) object of delegate object * @param args -> parameters of the mothod. * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result; if (args.length < 1){ return method.invoke(this.delegate,args); } //bear watching System.out.println("bear is watching " + args[0].toString()); result = method.invoke(this.delegate,args); System.out.println("bear leaved " + args[0].toString()); return result; }}
这个方法就是真正实现代理功能的关键,
它有三个参数
Object proxy对象代表被代理的对象
Method method代表要被代理的方法(实际上是所有方法)
args 就是代表方法的参数列表了。
返回值Object result 自然就是代表被代理方法的返回值了。
我们在里面先加入 熊看着狗的代码…
然后执行被代理的方法本身
然后加入熊离开的代码。
最后要返回被代理方法的返回值。
而且, 千万不要直接调用proxy的方法, 因为proxy直接调用方法会再次进入这个代理对象invoke方法, 导致死循环。
Client端代码
public static void g(){ Dog g = new Dog("InterDogChicken"); dynamicProxyBear bear = new dynamicProxyBear(); Hitable<Dog> newFox = (Hitable<Dog>) bear.bind(new Fox()); newFox.hit(g); Molestable<Dog> newWolf = (Molestable<Dog>) bear.bind(new Wolf()); newWolf.molest(g); }
上面代码中 首先要定义1个动态代理(熊)类, 然后熊返回1个新的狐狸对象(必须强制转换为Fox or Wolf类的1个接口, 而不能是本身)
然后再执行这个新的狐狸对象的方法。
输出结果
bear is watching Dog [name=InterDogChicken]give InterDogChicken a Sap!give InterDogChicken a Uppercute!bear leaved Dog [name=InterDogChicken]bear is watching Dog [name=InterDogChicken]wolf laugh at the dog!wolf ruffle the dog!bear leaved Dog [name=InterDogChicken]
这样的话无论狐狸or狼都被代理了
四,动态代理的关键点和小结
值得注意是, 上面重写的invoke的方法, 我并没有提到Fox or 狼类的方法名字。
实际上, 动态代理会利用java反射机制拦截被代理对象的所有方法。 接管对方法里管理。
所以动态代理是非常适合增加日志监控功能的。
例如在每个方法执行前和执行后都加日志的话, 动态代理模式避免了去修改以前的业务类。
被代理对象必须至少实现1个接口才能被动态代理对象代理, 因为动态代理bind方法返回的对象必须强制转换成1个接口引用。
而且,动态代理只对被代理对象的第一层入口方法有效果。
例如下面这个对象有3个方法
void a(){}void b(){}void c(){ a(); b();}
那么无论动态代理对象执行 a b c那个方法, 都是被拦截一次, 而不是当执行c时, c a b分别被拦截3次
五, 另1个例子, 利用动态方法实现监控功能
动态代理有时也会被利用于接管被代理对象的方法, 实现监控功能
例如下面要实现1个学生类, 它一次执行study/exam/graduate 三个方法,
学习接口:
public interface Studyable { public void study(); public int exam(); public void graduate();}
学生类
package proxyPattern.dynamicProxy;public class Student implements Studyable { private String name; private int examResult; public Student(String name, int examResult) { super(); this.name = name; this.examResult = examResult; } @Override public String toString() { return "Student [name=" + name + ", examResult=" + examResult + "]"; } @Override public void study() { System.out.println(this + " is studying"); } @Override public int exam() { System.out.println(this + " is examming"); return this.examResult; } @Override public void graduate() { System.out.println(this + " graduated"); }}
但是我们加入一个要求, 就是当某个学生对象exam分数少于60时, graduate执行失败
有人说直接修改学生类最快, 但是项目中很多情况下你没有修改上层类的权限, 这个时候我们可以利用动态代理来实现:
动态代理类 StudentMonitor
package proxyPattern.dynamicProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class StudentMonitor implements InvocationHandler{ private Object delegate; private boolean flag = false; public Object bind(Object delegate){ this.delegate = delegate; return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start to execute " + method.getName()); Object result; if(method.getName().equals("exam")){ result = method.invoke(this.delegate, args); if ((Integer)result >= 60){ this.flag = true; }else{ this.flag = false; } return result; } if(method.getName().equals("graduate")){ if(this.flag == true){ return method.invoke(this.delegate, args); } System.out.println(this.delegate + " exam result < 60,cannot graduate"); return null; } return method.invoke(this.delegate, args); }}
在重写的invoke方法中, 我们可以加入方法名字判断实现我们的需求
client代码
public static void h(){ StudentMonitor sm = new StudentMonitor(); //must be translated to a interface object of the class, cannot be translate to a class object Studyable st = (Studyable)sm.bind(new Student("Mike", 59)); st.study(); st.exam(); st.graduate(); Studyable st2 = (Studyable)sm.bind(new Student("bill", 61)); st2.study(); st2.exam(); st2.graduate(); }
输出
start to execute studyStudent [name=Mike, examResult=59] is studyingstart to execute examStudent [name=Mike, examResult=59] is exammingstart to execute graduateStudent [name=Mike, examResult=59] exam result < 60,cannot graduatestart to execute studyStudent [name=bill, examResult=61] is studyingstart to execute examStudent [name=bill, examResult=61] is exammingstart to execute graduateStudent [name=bill, examResult=61] graduated
- Java 动态代理介绍及用法
- java动态代理用法
- java代理及动态代理
- CGLib动态代理的介绍及用法(单回调、多回调、不处理、固定值、懒加载)
- java代理模式及动态代理类
- 代理模式及Java动态代理
- Java代理模式及动态代理
- Java代理模式及动态代理
- 代理模式及Java实现动态代理
- Java 中的代理模式及动态代理
- Java 中的代理模式及动态代理
- 代理模式及Java实现动态代理
- Java动态代理及原理
- java反射及动态代理
- Java注解及动态代理
- Java代理机制及动态代理和CGLIB代理详解
- java动态代理使用方法简单介绍
- Java动态代理用法(Proxy + InvocationHandler)
- 解析注解实现注解注入
- hibernate query.list() 返回的数据类型
- 用指针对十个整数大小排序
- Java学习笔记之重写(Overriding)与重载(Overloading)
- 练习打字第十五天!
- Java 动态代理介绍及用法
- c语言-常量变量笔记
- arcgis 卸载和注册表相关总结
- 修改maven本地仓库位置
- 正则表达式的基础和语法
- JavaScript公有、私有以及受保护的属性和方法
- Leetcode no. 43
- 视图的优点与缺点总结
- Java学习笔记之深入理解关键字null