标签:
转载:http://www.shangxueba.com/jingyan/1853835.html
一、动态代理与静态代理的区别。(1)Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;(2)可以实现AOP编程,这是静态代理无法实现的;(3)解耦,如果用在web业务下,可以实现数据层和业务层的分离。(4)动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题
二、动态代理
Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。
首先,invoke方法的完整形式如下:
Java代码
首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下:
Java代码
由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。
为了方便说明,这里写一个简单的例子来实现动态代理。
Java代码
Java代码
Java代码
10. public DynamicSubject(Object obj)
11. {
12. this.obj = obj;
13. }
15. //这个方法不是我们显示的去调用
16. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
17. {
18. System.out.println("before calling " + method);
20. method.invoke(obj, args);
22. System.out.println("after calling " + method);
24. return null;
25. }
27. }
Java代码
11. //以下是一次性生成代理
13. Subject subject=(Subject) Proxy.newProxyInstance(
14. cls.getClassLoader(),cls.getInterfaces(), ds);
16. //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口
17. System.out.println(subject instanceof Proxy);
19. //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口
20. System.out.println("subject的Class类是:"+subject.getClass().toString());
22. System.out.print("subject中的属性有:");
24. Field[] field=subject.getClass().getDeclaredFields();
25. for(Field f:field){
26. System.out.print(f.getName()+", ");
27. }
29. System.out.print("\n"+"subject中的方法有:");
31. Method[] method=subject.getClass().getDeclaredMethods();
33. for(Method m:method){
34. System.out.print(m.getName()+", ");
35. }
37. System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass());
39. System.out.print("\n"+"subject实现的接口是:");
41. Class[] interfaces=subject.getClass().getInterfaces();
43. for(Classi:interfaces){
44. System.out.print(i.getName()+", ");
45. }
47. System.out.println("\n\n"+"运行结果为:");
48. subject.request();
49. }
50. }
Xml代码
10. before calling public abstract void ***.Subject.request()
11. From real subject.
12. after calling public abstract void ***.Subject.request()
PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class $Proxy0就能解决很多的疑问,再加上下面将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。
从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下:
从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:
Java代码
10. /*
11. * Look up or generate the designated proxy class.
12. */
13. Class cl = getProxyClass(loader, interfaces);
15. /*
16. * Invoke its constructor with the designated invocation handler.
17. */
18. try {
19. /*
20. * Proxy源码开始有这样的定义:
21. * private final static Class[] constructorParams = { InvocationHandler.class };
22. * cons即是形参为InvocationHandler类型的构造方法
23. */
24. Constructor cons = cl.getConstructor(constructorParams);
25. return (Object) cons.newInstance(new Object[] { h });
26. } catch (NoSuchMethodException e) {
27. throw new InternalError(e.toString());
28. } catch (IllegalAccessException e) {
29. throw new InternalError(e.toString());
30. } catch (InstantiationException e) {
31. throw new InternalError(e.toString());
32. } catch (InvocationTargetException e) {
33. throw new InternalError(e.toString());
34. }
35. }
Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)做了以下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
Java代码
来看一下这个继承了Proxy的$Proxy0的源代码:
Java代码
10. new Class[] { Class.forName("java.lang.Object") });
12. m0 = Class.forName("java.lang.Object").getMethod("hashCode",
13. new Class[0]);
15. m3 = Class.forName("***.RealSubject").getMethod("request",
16. new Class[0]);
18. m2 = Class.forName("java.lang.Object").getMethod("toString",
19. new Class[0]);
21. } catch (NoSuchMethodException nosuchmethodexception) {
22. throw new NoSuchMethodError(nosuchmethodexception.getMessage());
23. } catch (ClassNotFoundException classnotfoundexception) {
24. throw new NoClassDefFoundError(classnotfoundexception.getMessage());
25. }
26. } //static
28. public $Proxy0(InvocationHandler invocationhandler) {
29. super(invocationhandler);
30. }
32. @Override
33. public final boolean equals(Object obj) {
34. try {
35. return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
36. } catch (Throwable throwable) {
37. throw new UndeclaredThrowableException(throwable);
38. }
39. }
41. @Override
42. public final int hashCode() {
43. try {
44. return ((Integer) super.h.invoke(this, m0, null)).intValue();
45. } catch (Throwable throwable) {
46. throw new UndeclaredThrowableException(throwable);
47. }
48. }
50. public final void request() {
51. try {
52. super.h.invoke(this, m3, null);
53. return;
54. } catch (Error e) {
55. } catch (Throwable throwable) {
56. throw new UndeclaredThrowableException(throwable);
57. }
58. }
60. @Override
61. public final String toString() {
62. try {
63. return (String) super.h.invoke(this, m2, null);
64. } catch (Throwable throwable) {
65. throw new UndeclaredThrowableException(throwable);
66. }
67. }
68. }
接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了$Proxy0类中的request()方法,进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。
PS:1、需要说明的一点是,Proxy类中getProxyClass方法返回的是Proxy的Class类。之所以说明,是因为我一开始犯了个低级错误,以为返回的是“被代理类的Class类”- -!推荐看一下getProxyClass的源码,很长=。=
2、从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。
Q:到现在为止,还有一个疑问,invoke方法中的第一个参数是Proxy的实例(准确说,最终用到的是$Proxy0的实例),但是有什么用呢?或者说,程序内是怎样显示出作用的?
A:就本人目前的水平看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧。
标签:
原文地址:http://www.cnblogs.com/YL450606975/p/5515670.html