
本文共 6213 字,大约阅读时间需要 20 分钟。
代理模式是指给某个对象提供一个代理对象,用户不直接访问原对象而是通过代理对象间接访问。
我们可以使用代理模式实现面向切面编程(AOP), 由动态代理将切面功能织入目标方法而不侵入调用方的业务代码。
或者使用代理模式实现远程过程调用(RPC), 调用方像调用本地方法一样调用代理方法,而不必关心代理调用远程方法细节。
JDK提供了基于反射机制的动态代理实现,而被广泛使用的第三方库CGLIB则基于字节码操作框架ASM实现动态代理。
本文将简单介绍两种动态代理使用方法,做抛砖引玉之用。
content:
代理模式
实现静态代理模式是非常简单的, 首先我们定义一个接口:
public interface MyInterface { void foo();}
编写被代理的对象:
public class MyService implements MyInterface { @Override public void foo() { System.out.println("foo"); }}
在代理模式中被代理的类通常被称作委托类。
编写静态代理:
public class MyProxy implements MyInterface { private MyInterface subject; MyProxy(MyInterface subject) { this.subject = subject; } @Override public void foo() { long start = System.System.currentTimeMillis(); subject.foo(); long elapseTime = System.currentTimeMillis() - start; System.out.println("elapse time: " + elapseTime); }}
静态代理是指代理类在编译时生成,与之相对动态代理则是运行时生成代理类的字节码并加载到JVM中。
静态代理的问题在于编写代理类时必须了解接口细节, 即编写MyProxy
时必须了解MyInterface
的定义。
以AOP框架为例,框架无法预先了解接口信息只能在运行时根据Class对象创建代理对象,因此编写此类框架必须要有动态代理机制的支持。
java动态代理
Java的反射机制可以在运行时创建类,Java标准库中提供了基于反射的代理机制。
代理类应该实现java.lang.reflect.InvocationHandler
接口,该接口只有一个方法: invoke
。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
当我们通过代理访问委托类方法时,会调用代理类的invoke方法。我们可以通过invoke方法的参数获得调用信息,并用反射机制调用为委托类的方法。
invoke方法的三个参数为:
Object porxy
: 被调用方法的动态代理实例Method method
: 被调用的方法Object[] args
: 调用时传入的参数
java.lang.reflect.Proxy.newProxyInstance
方法用于创建代理对象,它使用反射机制在运行时创建了代理类并加载到JVM中。该方法有三个参数:
ClassLoader loader
: 加载代理类的加载器Class<?>[] interfaces
: 代理类要实现的方法InvocationHandler h
: 在调用代理类方法时,嵌入调用过程的InvocationHandler实例
直接描述较难理解,我们来看代码:
public class MyProxy implements InvocationHandler { private Object subject; public MyProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1 return method.invoke(subject, args); } @SuppressWarnings("unchecked") public staticT createProxy(T subject) { return (T)Proxy.newProxyInstance(subject.getClass().getClassLoader(), // 2 subject.getClass().getInterfaces(), new MyProxy(subject)); } public static void main(String[] args) { MyInterface obj = createProxy(new MyService()); // 3 obj.foo(); }}
MyInterface
和MyService
和上文中完全相同:
public interface MyInterface { void foo();}public class MyService implements MyInterface { @Override public void foo() { System.out.println("foo"); }}
MyProxy
类实现了InvocationHandler
接口,并提供了createProxy
静态方法。
在main
方法中我们为MyService
对象创建了一个代理,并通过代理调用了foo
方法。
在代码中有几处细节值得了解:
在
main
方法中通过obj.getClass().getName()
获得代理类的类名为:com.sun.proxy.$Proxy0
。这个类名表示这个类是动态生成的Proxy类,0表示它是当前JVM中第一个动态代理类。在注释2处我们通过
newProxyInstance
创建了代理类即上文中的$Proxy0
类:loader
为被代理类的加载器,也可以使用MyInterface.class.getClassLoader()
,它们都是AppClassLoader实例。interfaces
参数为subject.getClass().getInterfaces()
表示动态代理类$Proxy0
实现了subject的所有接口,但$Proxy0
不是Subject类的子类。因此在main
方法中使用MyInterface obj = createProxy(new MyService());
而不能使用MyService obj = createProxy(new MyService());
。- 我们使用
MyProxy
实例作为InvocationHandler拦截动态代理类所有方法调用。
在注释1处我们实现了
invoke
方法拦截动态代理对象所有方法调用:- 传入的实参
Object proxy
是动态代理类的实例,即main
方法中的obj
, 它是$Proxy0
类的实例。注意InvocationHandler实例不是动态代理实例,handler是编译生成的静态类。 - 传入的实参
Method method
是接口类的Method对象,即MyInterface.class.getMethod("foo")
而非委托类MyService
的Method对象 method.invoke(subject, args)
用反射的方式调用了被代理实例的方法,我们可以在invoke
方法中添加其它代码以增强委托类的功能
- 传入的实参
cglib动态代理
CGLIB是一个强大的动态代理库,SpringAOP和dynaop框架使用CGLIB进行方法拦截和增强,Hibernate使用CGLIB进行代理关联,此外JMock也使用CGLIB提供Mock对象。
在实现上,CGLIB使用高性能轻量级字节码操作框架ASM来动态生成代理类。值得一提的是,CGLIB比JDK动态代理还要快。
CGLIB将动态代理类实现为委托类的子类,因此可以代理没有实现接口或对接口进行了扩展的类。因为子类无法覆盖final方法,因此只能代理非final方法。
首先使用maven导入cglib依赖:
cglib cglib 3.2.6
编写CGLIB动态代理:
public class CglibProxy { @SuppressWarnings("unchecked") public staticT createProxy(T subject) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(subject.getClass()); enhancer.setCallback(new MyInterceptor()); return (T) enhancer.create(); } private static class MyInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } } public static void main(String[] args) { MyService obj = createProxy(new MyService()); obj.foo(); }}
Enhancer
用于对委托类进行增强,它可以拦截方法调用以实现代理机制。
MethodInterceptor.intercept
方法用于拦截方法调用,它的几个参数为:
Object obj
: 动态代理对象Method method
: 被拦截的方法,本例main
方法中被拦截的方法是:MyService.class.getMethod("foo")
Object[] args
: 调用实参MethodProxy proxy
: 用来调用委托类方法的快捷代理,不是动态代理类。
示例中定义的动态代理没有添加任何额外功能,这种特殊的Callback可以使用net.sf.cglib.proxy.NoOp
代替:
public staticT createProxy(T subject) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(subject.getClass()); enhancer.setCallback(NoOp.INSTANCE); return (T) enhancer.create(); }
Enhancer可以使用CallbackFilter为每个方法调用配置过滤器:
class MyService { void foo0() { System.out.println("0"); } void foo1() { System.out.println("1"); }}public class CglibProxy { @SuppressWarnings("unchecked") public staticT createProxy(T subject) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(subject.getClass()); Callback callback0 = NoOp.INSTANCE; Callback callback1 = NoOp.INSTANCE; enhancer.setCallbacks(new Callback[]{callback0, callback1}); enhancer.setCallbackFilter(new MyCallbackFilter()); return (T) enhancer.create(); } private static class MyCallbackFilter implements CallbackFilter { @Override public int accept(Method method) { if ("foo0".equals(method.getName())) { return 0; } else { return 1; } } } public static void main(String[] args) { MyService obj = createProxy(new MyService()); obj.foo0(); obj.foo1(); }}
当Enhancer设置了CallbackFilter之后,在拦截方法前会先调用CallbackFilter.accept()
方法判断使用哪个Callback实例进行拦截。
CallbackFilter.accept()
返回值即为拦截器的下标,即若accept方法返回0,则调用enhancer.callbacks[0]
进行拦截。
发表评论
最新留言
关于作者
