Java中动态代理代理包括JDK动态代理和CGLIB动态代理。前者通过实现目标类的接口,和反射技术实现。后者不需要实现接口,而是通过继承目标类,并通过方法拦截技术实现。Spring的AOP技术则是集成了这两种动态代理。本文对两种代理的使用方法做一个简要介绍。
代理模式
首先介绍下设计模式中的代理模式。代理模式是结构型模式的一种,要求代理对象实现被代理对象的接口,并持有被代理对象实例。外界只需要访问代理对象即可实现需要的功能。这种模式不会改变被代理对象的代码,但是又使其功能得到增强。
代理模式又分为两种,静态代理和动态代理。前者是在代码运行前就已存在代理类的字节码文件,相对扩展性较差。后者则是在运行时生成代理类。JDK中动态代理就是代理模式思想的体现。
JDK动态代理
特点
被代理对象要有实现接口,否则不能被代理。
生成的代理对象默认继承了Proxy
。
相关类
由于动态代理是基于反射,因此动态代理集成在java.lang.reflect
包下。包括两个类:InvocationHandler
和 Proxy
。前者提供了一个invoke
方法,在这个方法里调用被代理对象的方法,并实现功能增强。后者提供了一个newProxyInstance
静态方法,通过传入前者的实现,可以生成一个代理对象。
步骤
被代理类和接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public interface Star {
void play(); }
public class Singer implements Star {
public String name;
public Singer(String name) { this.name = name; }
@Override public void play() { System.out.println(name + " sing a new song!"); } }
|
重写invoke方法
实现 InvocationHandler
接口,需要引入被代理对象实例,在 invoke()
方法中调用需要代理的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class MyInvocationHandler implements InvocationHandler {
private Object object;
public MyInvocationHandler(Object object) { this.object = object; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy start"); Object ret = method.invoke(this.object, args); System.out.println("proxy end"); return ret; } }
|
创建代理对象
通过Proxy.newProxyInstance()
的方法,来创建代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class ProxyTest { public static void main(String[] args) { Singer singer = new Singer("Jay chou"); InvocationHandler singerHandler = new MyInvocationHandler(singer); Object instance = Proxy.newProxyInstance(Singer.class.getClassLoader(), new Class[]{Star.class}, singerHandler); Star star1 = (Star)instance; star1.play(); } }
|
输出结果
可以看到,在play()的执行前后,执行了代理对象的逻辑
1 2 3
| proxy start Jay chou sing a new song! proxy end
|
源码分析
newProxyInstance
方法源码
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
|
public class Proxy{ @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); }
Class<?> cl = getProxyClass0(loader, intfs);
try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); }
final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } } }
|
CGLIB动态代理
特点
CGLIB通过动态生成被代理类的子类,重写父类的所有非final方法,在子类中通过方法拦截技术拦截所有对父类方法的调用。与JDK动态代理相比,CGLIB速度更快,并且不要求被代理对象有实现接口。
相关类
在net.sf.cglib.proxy
包下,包括MethodInterceptor
接口和Enhancer
类。前者提供intercept
方法实现对父类方法的拦截,后者通过create
方法创建代理对象。
步骤
依赖导入
1 2 3 4 5
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
|
被代理类
不要求有实现接口
1 2 3 4 5 6
| public class Actress {
public void play() { System.out.println("actor a queen!"); } }
|
重写intercept方法
cglib动态代理是基于方法拦截实现的,需要实现MethodInterceptor
接口,重写intercept
方法,在这里实现代理工作。
类似于InvocationHandler
,区别在于,这里不需要引入被代理对象,由对父类的方法拦截完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("method invoke before..."); Object result = methodProxy.invokeSuper(obj, args); System.out.println("method invoke after..."); return result; } }
|
创建代理对象
创建代理对象并调用需要代理的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import net.sf.cglib.proxy.Enhancer;
public class CglibProxyTest { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Actress.class); enhancer.setCallback(new MyMethodInterceptor()); Actress actress = (Actress)enhancer.create(); actress.play(); } }
|
输出结果
1 2 3
| method invoke before... actor a queen! method invoke after...
|
参考资料
- 面试必问系列之JDK动态代理
- CGLib动态代理