1. 静态代理模式

因为需要对一些函数进行二次处理,或是某些函数不让外界知道时,可以使用代理模式,通过访问第三方,间接访问原函数的方式,达到以上目的,来看一下代理模式的类图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
interface Hosee{
     String sayhi();
}
class Hoseeimpl implements Hosee{
     @Override
     public String sayhi()
     {
         return "Welcome oschina hosee's blog" ;
     }
}
class HoseeProxy implements Hosee{
     Hosee h;
     public HoseeProxy(Hosee h)
     {
         this .h = h;
     }
     @Override
     public String sayhi()
     {
         System.out.println( "I'm proxy!" );
         return h.sayhi();
     }
}
public class StaticProxy
{
     public static void main(String[] args)
     {
         Hoseeimpl h = new Hoseeimpl();
         HoseeProxy hp = new HoseeProxy(h);
         System.out.println(hp.sayhi());
     }
}

1.1 静态代理的弊端

如果要想为多个类进行代理,则需要建立多个代理类,维护难度加大。

仔细想想,为什么静态代理会有这些问题,是因为代理在编译期就已经决定,如果代理哪个发生在运行期,这些问题解决起来就比较简单,所以动态代理的存在就很有必要了。

2. 动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface HoseeDynamic
{
     String sayhi();
}
class HoseeDynamicimpl implements HoseeDynamic
{
     @Override
     public String sayhi()
     {
         return "Welcome oschina hosee's blog" ;
     }
}
class MyProxy implements InvocationHandler
{
     Object obj;
     public Object bind(Object obj)
     {
         this .obj = obj;
         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                 .getClass().getInterfaces(), this );
     }
     @Override
     public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable
     {
         System.out.println( "I'm proxy!" );
         Object res = method.invoke(obj, args);
         return res;
     }
}
public class DynamicProxy
{
     public static void main(String[] args)
     {
         MyProxy myproxy = new MyProxy();
         HoseeDynamicimpl dynamicimpl = new HoseeDynamicimpl();
         HoseeDynamic proxy = (HoseeDynamic)myproxy.bind(dynamicimpl);
         System.out.println(proxy.sayhi());
     }
}

类比静态代理,可以发现,代理类不需要实现原接口了,而是实现InvocationHandler。通过

1
2
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                 .getClass().getInterfaces(), this );

来动态生成一个代理类,该类的类加载器与被代理类相同,实现的接口与被代理类相同。

通过上述方法生成的代理类相当于静态代理中的代理类。

这样就实现了在运行期才决定代理对象是怎么样的,解决了静态代理的弊端。

当动态生成的代理类调用方法时,会触发invoke方法,在invoke方法中可以对被代理类的方法进行增强。

通过动态代理可以很明显的看到它的好处,在使用静态代理时,如果不同接口的某些类想使用代理模式来实现相同的功能,将要实现多个代理类,但在动态代理中,只需要一个代理类就好了。

除了省去了编写代理类的工作量,动态代理实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景中。

2.1 动态代理的弊端

代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,就不能使用该机制。

而CGLIB则可以实现对类的动态代理

2.2 回调函数原理

上文说了,当动态生成的代理类调用方法时,会触发invoke方法。

很显然invoke方法并不是显示调用的,它是一个回调函数,那么回调函数是怎么被调用的呢?

上述动态代理的代码中,唯一不清晰的地方只有

1
2
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                 .getClass().getInterfaces(), this );

跟踪这个方法的源码,可以看到程序进行了验证、优化、缓存、同步、生成字节码、显示类加载等操作,前面的步骤并不是我们关注的重点,而最后它调用了

1
2
byte [] proxyClassFile = ProxyGenerator.generateProxyClass(
                 proxyName, interfaces);

该方法用来完成生成字节码的动作,这个方法可以在运行时产生一个描述代理类的字节码byte[]数组。

在main函数中加入

1
System.getProperties().put( "sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" );

加入这句代码后再次运行程序,磁盘中将会产生一个名为”$Proxy().class”的代理类Class文件,反编译(反编译工具我使用的是 JD-GUI )后可以看见如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
   implements HoseeDynamic
{
   private static Method m1;
   private static Method m3;
   private static Method m0;
   private static Method m2;
   public $Proxy0(InvocationHandler paramInvocationHandler)
     throws
   {
     super (paramInvocationHandler);
   }
   public final boolean equals(Object paramObject)
     throws
   {
     try
     {
       return ((Boolean) this .h.invoke( this , m1, new Object[] { paramObject })).booleanValue();
     }
     catch (Error|RuntimeException localError)
     {
       throw localError;
     }
     catch (Throwable localThrowable)
     {
       throw new UndeclaredThrowableException(localThrowable);
     }
   }
   public final String sayhi()
     throws
   {
     try
     {
       return (String) this .h.invoke( this , m3, null );
     }
     catch (Error|RuntimeException localError)
     {
       throw localError;
     }
     catch (Throwable localThrowable)
     {
       throw new UndeclaredThrowableException(localThrowable);
     }
   }
   public final int hashCode()
     throws
   {
     try
     {
       return ((Integer) this .h.invoke( this , m0, null )).intValue();
     }
     catch (Error|RuntimeException localError)
     {
       throw localError;
     }
     catch (Throwable localThrowable)
     {
       throw new UndeclaredThrowableException(localThrowable);
     }
   }
   public final String toString()
     throws
   {
     try
     {
       return (String) this .h.invoke( this , m2, null );
     }
     catch (Error|RuntimeException localError)
     {
       throw localError;
     }
     catch (Throwable localThrowable)
     {
       throw new UndeclaredThrowableException(localThrowable);
     }
   }
   static
   {
     try
     {
       m1 = Class.forName( "java.lang.Object" ).getMethod( "equals" , new Class[] { Class.forName( "java.lang.Object" ) });
       m3 = Class.forName( "HoseeDynamic" ).getMethod( "sayhi" , new Class[ 0 ]);
       m0 = Class.forName( "java.lang.Object" ).getMethod( "hashCode" , new Class[ 0 ]);
       m2 = Class.forName( "java.lang.Object" ).getMethod( "toString" , new Class[ 0 ]);
       return ;
     }
     catch (NoSuchMethodException localNoSuchMethodException)
     {
       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
     }
     catch (ClassNotFoundException localClassNotFoundException)
     {
       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
     }
   }
}

动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。

可以在上述代码中看到,无论调用哪个方法,都会调用到InvocationHandler的invoke方法,只是参数不同。

2.3 动态代理与静态代理的区别

  1. Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;
  2. 可以实现AOP编程,这是静态代理无法实现的;
  3. 解耦,如果用在web业务下,可以实现数据层和业务层的分离。
  4. 动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

3. CGLIB

cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class CGlibHosee
{
     public String sayhi()
     {
         return "Welcome oschina hosee's blog" ;
     }
}
class CGlibHoseeProxy
{
     Object obj;
     public Object bind( final Object target)
     {
         this .obj = target;
         Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(obj.getClass());
         enhancer.setCallback( new MethodInterceptor()
         {
             @Override
             public Object intercept(Object obj, Method method, Object[] args,
                     MethodProxy proxy) throws Throwable
             {
                 System.out.println( "I'm proxy!" );
                 Object res = method.invoke(target, args);
                 return res;
             }
         });
         return enhancer.create();
     }
}
public class CGlibProxy
{
     public static void main(String[] args)
     {
         CGlibHosee cGlibHosee = new CGlibHosee();
         CGlibHoseeProxy cGlibHoseeProxy = new CGlibHoseeProxy();
         CGlibHosee proxy = (CGlibHosee) cGlibHoseeProxy.bind(cGlibHosee);
         System.out.println(proxy.sayhi());
     }
}

cglib需要指定父类和回调方法。当然cglib也可以与Java动态代理一样面向接口,因为本质是继承。

Java动态代理与CGLIB相关推荐

  1. Java动态代理之cglib

    转载自 Java动态代理之cglib cglib是对jdk动态代理的补充,弥补了因没有接口的类生成代理类的缺失.  下面通过简单的例子学习一下cglib的使用,当然,要是用需要第三方的jar包,jar ...

  2. Java动态代理与Cglib代理

    为什么80%的码农都做不了架构师?>>>    最近又继续回来死磕Spring源码,以前看的也忘得差不多了,这次先把Spring使用的动态代理cglib看了一下,打好基础知识. cg ...

  3. Java 静态代理、Java动态代理、CGLIB动态代理

    为什么80%的码农都做不了架构师?>>>    Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理.为啥要这样呢, 是因为使用代理有 ...

  4. 设计模式之代理模式(静态代理、Java动态代理、Cglib动态代理)

    代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. 提醒:动态代理中涉及到以前的一些知识 ...

  5. java动态代理二cglib

    2019独角兽企业重金招聘Python工程师标准>>> java动态代理 转载于:https://my.oschina.net/u/1430510/blog/290215

  6. Java动态代理、CGLIB动态代理

    文章目录 代理模式 静态代理 动态代理 CGLIB动态代理 JDK动态代理源码分析 代理模式 代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务. ...

  7. Java动态代理与Cglib库应用

    转载:http://blog.csdn.net/zhoudaxia/article/details/30591941 JDK动态代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的 ...

  8. java动态代理上是否能再进行一层代理

    CGLIB动态代理类 import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.p ...

  9. 支撑Java框架的基础技术:泛型,反射,动态代理,cglib

    以Spring为例要想看明白他的源码需要彻底理解Java的一些基础技术泛型,反射同时对于一些高级技术例如动态代理,cglib和字节码技术也需要掌握,下面就按章节来一一说清楚这些技术的核心部分,最后手写 ...

最新文章

  1. Wijmo 更优美的jQuery UI部件集:运行时处理Wijmo GridView数据操作
  2. python函数使用易错点_大部分人都会忽略的Python易错点总结
  3. IOS开发基础之模拟科技头条项目案例32
  4. 20145313张雪纯《信息安全系统设计基础》第11周学习总结
  5. 双向@OneToOne主键关联
  6. 一位前BAT面试官详谈进入BAT面试经验
  7. PHP rewinddir()函数与示例
  8. vs2012 怎样解决 未能正确加载“Microsoft.VisualStudio.Editor.Implementation.EditorPackage”包的问题
  9. Android下载安装Apk
  10. 线性系统实验:化学方程式配平 与 天体轨道参数估计
  11. 检测到目标站点存在javascript框架库漏洞
  12. 苹果电脑怎么打开计算机管理,mac开机启动管理怎么设置_mac如何设置开机启动管理-win7之家...
  13. Learun FrameWork,基于.NET的智能化开发工具
  14. 数值型数据的表示(1.0)
  15. 计算机网络实验八——聊天程序
  16. 当型循环和直到型循环
  17. swapidc对接虚拟服务器,利用SWAPIDC实现自助开通虚拟主机
  18. 【2021.11综合方案】Ubuntu + 高通QCA6174无线网卡连接不上WIFI问题解决
  19. MIT6.S081 Lab3: page tables
  20. 通过Lora无线网关实现数据采集远程监控究竟有多香?

热门文章

  1. 8-10 了不起的魔术师
  2. iOS-小Demo(Swift基础练习)--双TableView关联
  3. 人人宝:应该依靠健康保险治疗精神疾病吗?
  4. 使用docker安装Prometheus
  5. Java的jmap命令使用详解
  6. 回味小时候拿着诺基亚玩的推箱子
  7. 图表目录每章空行问题(毕业论文和大型文档专用)
  8. 移动端app开发 - 01 - 开篇
  9. java毕业生设计医患辅助系统计算机源码+系统+mysql+调试部署+lw
  10. 使用c语言打印倒等腰三角形