下面对上面的代码进行分析。
首先这个代理类要实现接口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 study
Student [name=Mike, examResult=59] is studying
start to execute exam
Student [name=Mike, examResult=59] is examming
start to execute graduate
Student [name=Mike, examResult=59] exam result < 60,cannot graduate
start to execute study
Student [name=bill, examResult=61] is studying
start to execute exam
Student [name=bill, examResult=61] is examming
start to execute graduate
Student [name=bill, examResult=61] graduated
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-12/138192.htm