您的位置:首页 > 其它

设计模式之代理模式二(动态代理)

2015-12-01 13:55 260 查看
1、实例

虽然使用动态代理的时候有几个类平时没怎么见过,但是对于使用并没有什么影响,所以先学会动态代理的使用然后再深究其中的细节。

1.1 实例
动态代理的实现需要用到  InvocationHandler 接口,它有一个invoke函数,动态代理的实现就是靠这个函数来实现。

首先先实现这个接口,定义一个HomeWorkProxy3实现了InvocationHandler。在实例化 HomeWorkProxy3的时候要将被代理对象当做参数传入。例子如下:


public class HomeWorkProxy3 implements InvocationHandler{

private Class cls = null ;
private Object obj = null ;

//这里的obj就是实例化的时候传入的被代理对象,本例中的student
public HomeWorkProxy3(Object obj) {
this.obj = obj ;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

Object result = method.invoke(this.obj, args);

return result;
}

}


被代理对象仍然使用Student(见前一篇文章 代理模式一),使用方式如下:


Student student2 = new Student("动态代理");
ClassLoader cl = student2.getClass().getClassLoader();
HomeWorkProxy3 handler = new HomeWorkProxy3(student2);
Class[] cs = new Class[]{DoHomeWork.class} ;
DoHomeWork proxy2 = (DoHomeWork) Proxy.newProxyInstance(cl, cs, handler);
proxy2.doHomeWork();


输出结果如下:

动态代理is doing homework!

1.2详细讲解
个人猜测:
看Proxy.newProxyInstance(cl, new Class[]{DoHomeWork.class}, handler) 这个函数,其中有三个参数,
第一个ClassLoader类型cl,
第二个Class[]类型 cs,
第三个实现了InvocationHandler接口的HomeWorkProxy3类型的handler。
例子中的newProxyInstance就可以理解为handler控制了student类中实现的并且在cs中出现的DoHomeWork接口。(我是这么理解的,这样比较容易记忆)。


文档注解:

java文档中行对这个函数的注解为:返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。文档对三个参数的注解是:定义代理类的类加载器、代理类要实现的接口列表、指派方法调用的调用处理程序。文档的解释和自己的理解也挺相似的。函数会返回一个指定接口的代理类实例X,这个代理类类型由参数中的类加载器控制,指定接口由参数中的接口列表控制,当程序调用代理类实例X中的方法时,X就会自动去调用handler中的invoke函数。

2、动态代理的好处

好处一:可以为一个接口函数进行切面编程(就是控制当程序调用某个函数之前执行什么,调用之后执行什么)
在上面的例子中,并没有感觉到动态代理的强大,只是普通代理模式多了几行代码嘛!
在上面的介绍中我们知道代理类实例在希望调接口函数时要利用handler中的invoke函数去调用,而invoke函数我们是可以自己修改的,那么在真正调用接口函数之前,我们可以在invoke中进行各种处理。如下:


public class HomeWorkProxy3 implements InvocationHandler{
private Class cls = null ;
private Object obj = null ;
public HomeWorkProxy3(Object obj) {
this.obj = obj ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//Object result = method.invoke(this.obj, args);
Object result = null ;
if(method.getName().equals("doHomeWork")){
System.out.println("动态代理开始");
result = method.invoke(this.obj, args);
System.out.println("动态代理结束");
}else {
System.out.println("others");
result = method.invoke(this.obj, args);
}
return result;
}
}


执行下面的代码:


Student student2 = new Student("动态代理");
ClassLoader cl = student2.getClass().getClassLoader();
HomeWorkProxy3 handler = new HomeWorkProxy3(student2);
DoHomeWork proxy2 = (DoHomeWork) Proxy.newProxyInstance(cl, new Class[]{DoHomeWork.class}, handler);
proxy2.doHomeWork();
proxy2.hashCode();


执行结果如下:

动态代理开始
动态代理is doing homework!
动态代理结束
others

很明显,当我们调用DoHomeWork函数的时候执行了


System.out.println("动态代理开始");
result = method.invoke(this.obj, args);
System.out.println("动态代理结束");


这几行代码,而调用hashCode的时候执行了


System.out.println("others");
result = method.invoke(this.obj, args);


这几行代码,这不就是AOP切面编程吗?
( 我们在DoHomeWork接口中没有hashCode函数啊,这个函数从哪里来的?我们的所有接口和类都是继承自Object的hashCode是继承来的函数。)

好处二:给代理类做切面编程,可以控制在取得代理类之前执行什么,取得之后执行什么。实现这种模式,我们必须自己写一个动态代理类,实际上就是将Proxy.newProxyInstance函数再封装一下,如下:


public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader cl,Class<?>[] cs,InvocationHandler handler) {

System.out.println("before 创建动态代理");
T t =  (T) Proxy.newProxyInstance(cl, cs, handler);
System.out.println("after 创建动态代理");
return t ;
}
}


测试代码如下:


Student student3 = new Student("动态代理2");
ClassLoader cl2 = student3.getClass().getClassLoader();
HomeWorkProxy3 handler2 = new HomeWorkProxy3(student3);
DoHomeWork proxy3 = DynamicProxy.newProxyInstance(cl2, new Class[]{DoHomeWork.class}, handler2);
proxy3.doHomeWork();


结果如下:
before 创建动态代理
after 创建动态代理
动态代理开始
动态代理2is doing homework!
动态代理结束


3、总结

代理模式差不多就学习结束了,代理模式可以将真正重要的不常变的业务逻辑和一些易变的判断条件等等分离开来,

在写项目的时候可以先完成主要业务逻辑的编写,然后再利用代理控制权限等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: