我们前一篇文章已经说明了什么是代理模式,本文就谈谈Java中的三种代理模式。它们分别是静态代理,JDK动态代理,以及Cglib动态代理。
静态代理
所谓静态代理,就是经典的代理模式,一个代理者需要对应一个被代理者,如下图所示:
不过缺点也很明显,就是不够灵活,如果需要被代理的对象数量一增加,对于编写代理类的同学来说无疑是灾难。下面给出一个具体的例子:
代理接口类
1 2 3 4 5 6 7 8 9
| package com.qinjiangbo.spring.proxy;
public interface Service { void fork(String repository); }
|
被代理类
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.qinjiangbo.spring.proxy;
public class GitService implements Service {
@Override public void fork(String repository) { System.out.println("fork " + repository + " successfully!"); } }
|
代理类:
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
| package com.qinjiangbo.spring.proxy;
public class GitStaticProxy implements Service {
private GitService gitService;
public GitStaticProxy(GitService gitService) { this.gitService = gitService; }
@Override public void fork(String repository) { logBefore(); gitService.fork(repository); logAfter(); }
private void logBefore() { System.out.println(this.getClass().getName() + " @@@ " + "before fork() invoked!"); }
private void logAfter() { System.out.println(this.getClass().getName() + " @@@ " + "after fork() invoked!"); }
}
|
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.qinjiangbo.spring.proxy;
import org.junit.Test;
public class GitStaticProxyTest {
@Test public void testFork() { Service service = new GitStaticProxy(new GitService()); service.fork("tomcat"); } }
|
输出结果如下:
1 2 3
| com.qinjiangbo.spring.proxy.GitStaticProxy @@@ before fork() invoked! fork tomcat successfully! com.qinjiangbo.spring.proxy.GitStaticProxy @@@ after fork() invoked!
|
JDK动态代理
针对这一种情况,JDK也提出了对应的方式,那就是JDK动态代理,它允许你代理任何实现了某个接口的具体实现类。其实它基于的底层原理还是经典的代理模式,只不过它通过反射的原理动态地创建出了代理类。本质上还是通过实现一个共同的接口来代理对象的行为。
下面给出一个JDK动态代理模式的例子:
接口类还是不变,同上
1 2 3 4 5 6 7 8 9
| package com.qinjiangbo.spring.proxy;
public interface Service { void fork(String repository); }
|
JDK动态代理类
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
| package com.qinjiangbo.spring.proxy;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class GitJdkProxy implements InvocationHandler {
private Object target;
public GitJdkProxy(Object target) { this.target = target; }
public <T> T getProxy() { return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this ); }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { logBefore(); Object result = method.invoke(target, args); logAfter(); return result; }
private void logBefore() { System.out.println(this.getClass().getName() + " @@@ " + "before fork() invoked!"); }
private void logAfter() { System.out.println(this.getClass().getName() + " @@@ " + "after fork() invoked!"); } }
|
可以看到,它实现了一个InvocationHandler
接口,这个接口中有一个方法叫做invoke(Object proxy, Method method, Object[] args)
,它是这个代理类的具体执行方法。但是它是如何获取代理的呢?
1 2 3 4 5 6 7
| public <T> T getProxy() { return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this ); }
|
上面的这部分代码给出了答案,就是通过被代理类实现的接口反射地来创建这个代理类,本质上就是我们前面说到的经典的代理模式,基于共同的实现接口类。
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.qinjiangbo.spring.proxy;
import org.junit.Test;
public class GitJdkProxyTest {
@Test public void testFork() { Service service = new GitJdkProxy(new GitService()).getProxy(); service.fork("tomcat"); } }
|
结果输出如下:
1 2 3
| com.qinjiangbo.spring.proxy.GitJdkProxy @@@ before fork() invoked! fork tomcat successfully! com.qinjiangbo.spring.proxy.GitJdkProxy @@@ after fork() invoked!
|
Cglib动态代理
Cglib全称是Code Generator Library
,字节码生成库。大家也能猜的到它的原理是什么了吧?它是通过字节码技术动态地生成当前类的子类,从而实现对父类的代理。它的底层原理本上是继承,而不是实现。
我们来看看它是怎么实现代理的?不需要实现某个接口,但是为了方便起见,我们还是选择上面提到的GitService
来进行这个测试。
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
| package com.qinjiangbo.spring.proxy;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class GitCglibProxy implements MethodInterceptor {
public <T> T getProxy(Class<T> clazz) { return (T) Enhancer.create(clazz, this); }
@Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { logBefore(); Object result = methodProxy.invokeSuper(o, objects); logAfter(); return result; }
private void logBefore() { System.out.println(this.getClass().getName() + " @@@ " + "before fork() invoked!"); }
private void logAfter() { System.out.println(this.getClass().getName() + " @@@ " + "after fork() invoked!"); } }
|
在上面的代码中我们可以看到也是存在一个getProxy()
方法,抽取出来如下:
1 2 3
| public <T> T getProxy(Class<T> clazz) { return (T) Enhancer.create(clazz, this); }
|
可以看出它是通过Enhancer.create(clazz, this)
来创建这个clazz
的子类的,直接在内存中生成一个当前类的子类,然后在intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
方法中实现对父类行为的代理。
测试类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.qinjiangbo.spring.proxy;
import org.junit.Test;
public class GitCglibProxyTest {
@Test public void testFork() { Service service = new GitCglibProxy().getProxy(GitService.class); service.fork("tomcat"); } }
|
测试结果如下:
1 2 3
| com.qinjiangbo.spring.proxy.GitCglibProxy @@@ before fork() invoked! fork tomcat successfully! com.qinjiangbo.spring.proxy.GitCglibProxy @@@ after fork() invoked!
|
JDK动态代理 vs Cglib动态代理
关于JDK动态代理和Cglib动态代理两种方式,这里我简单总结一下:
- [相同] 二者都能够动态地产生某个对象的代理对象,使用起来非常灵活。
- [不同] JDK动态代理依据的底层原理是实现一个共同的接口,而Cglib动态代理依据的底层原理是创建某个类的子类。
- [不同] 运行性能上JDK和Cglib其实差不多,没有传说中的Cglib比JDK快10倍的差距,至少在最近的几个JDK版本(6,7,8)是这样的,版本越靠后甚至JDK更快一点。
总结
关于JDK动态代理和Cglib动态代理到底选择哪一种其实要根据自己的实际情况来确定,其实我所了解到的很多RPC框架比如HSF就使用的是JDK动态代理实现的远端服务请求和响应代理。当然Spring AOP中提供了这两种选择,大家可以在配置文件中自己指定对应的动态代理方式。默认情况下,Spring AOP会采用JDK动态代理来代理对象。当然我们也可以使用<aop:aspectj-autoproxy proxy-target-class="true"/>
来强制使用Cglib动态代理。
参考文献
:Cglib 与 JDK动态代理的运行性能比较