引入
在之前的文章中我们介绍了常见的设计模式,今天我们来使用 Java 语言对代理模式进行具体实现举例。
原理
代理模式在不同语言的实现以及对应机制不太一样,所以在此对代理模式原理进行阐述。
实现目的:
代理模式意图提供一种代理以控制对这个对象的访问,在Spring当中的典型应用是AOP,在调用目标对象方法时,做前置、后置、异常等处理。
在Spring当中,代理模式的实现主要分为JDK动态代理和CGLIB动态代理。下面我们对他们分别来实验并比较。
被代理的接口及类
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
|
interface Student {
void study();
default void sayHi() { System.out.println("Hi"); } }
interface Child {
void play(); }
public class Tom implements Student, Child{
@Override public void study() { System.out.println("studying..."); }
@Override public void play() { System.out.println("playing..."); }
public void eat() { System.out.println("eating..."); } }
|
JDK动态代理实例
在下面的例子中,我们使用Java原生的JDK动态代理来生成代理对象,因为JDK动态代理是基于接口的,所以这里我们生成Student接口和Child接口的代理对象。
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
|
public class JDKAgent { public static void main(String[] args) { Student tom = new Tom(); InvocationHandler handler = new InvocationHandlerImpl(tom); ClassLoader loader = tom.getClass().getClassLoader(); Class[] interfaces = tom.getClass().getInterfaces();
Student agentStudent = (Student) Proxy.newProxyInstance(loader, interfaces, handler); Child agentChild = (Child) Proxy.newProxyInstance(loader, interfaces, handler);
agentStudent.study(); agentStudent.sayHi(); agentChild.play(); }
static class InvocationHandlerImpl implements InvocationHandler {
Object target;
public InvocationHandlerImpl(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (!"study".equals(method.getName())) { System.out.println("Before agent...(except study)"); }
Object obj = method.invoke(target, args); System.out.println("After agent...");
System.out.println(); return obj; } } }
|
CGLIB动态代理
在下面的例子中,我们使用CGLIB开源项目下的包来实现基于CGLIB的动态代理。因为CGLIB是基于继承实现的,所以我们在这里对Tom类来进行增强(也就是代理)。
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
|
public class CGLIBAgent {
public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Tom.class); enhancer.setCallback(new MyMethodInterceptor()); Tom agentTom = (Tom) enhancer.create();
agentTom.study(); agentTom.sayHi(); agentTom.play(); agentTom.eat(); } }
class MyMethodInterceptor implements MethodInterceptor {
@Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if (!"play".equals(method.getName())) { System.out.println("before intercept...(except play)"); }
Object obj = methodProxy.invokeSuper(o, objects);
if (!"study".equals(method.getName())) { System.out.println("after intercept...(except study)"); }
System.out.println();
return obj; } }
|
JDK动态代理和CGLIB动态代理的异同
- JDK动态代理是java原生的,基于拦截器(必须实现InvocationHandler)及反射机制生成代理指定接口的匿名类,在使用具体的方法前会被InvokeHandler拦截处理
- CGLIB是利用ASM框架将代理对象类生成的class文件加载进来,通过修改其字节码来生成子类
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的
参考
Cglib和jdk动态代理的区别