动态代理双剑客–JDK Proxy与CGLIB

研究过设计模式的同胞们都知道代理模式可以有两种实现方案:

 

1.接口实现(或继承抽象类)

 

核心代码片段

ProxySubject–>>doOperation()

[java][/java] view plaincopy

  1. //dosomething before
  2. realSubject.doOperation()
  3. //dosomething after

 

 

2.继承父类

 

 

核心代码片段

ProxySubject–>>doOperation()

[java][/java] view plaincopy

  1. //dosomething before
  2. super.doOperation()
  3. //dosomething after

 

 

总结:

相同点

都可以通过Proxy控制对Target的访问

不同点

1.灵活性

“接口实现”的方式更加灵活,代理类可以代理所有实现了ISubject接口的类;

“继承父类”的方式代理类只能代理它的父类,因为java中只支持单继承

2.可行性

如果Target有直接接口,那么这两种方式都可以;

如果没有实现任何接口,那只能采取“继承父类”的方式了

 

 

 

 

正文

 

 

Java中动态代理对应着也有两种实现方式

 

1.“接口实现”—JDK Proxy

用到JDK提供的InvocationHandler接口和Proxy类

 

类之间的关系如下

InvocationHandler接口

用于处理方法请求

Proxy类

用于生成代理对象

 

代码演示

ISubject接口

 

[java][/java] view plaincopy

  1. public interface ISubject {
  2.     public void showName(String name);
  3. }

 

 

RealSubject类

[java][/java] view plaincopy

  1. public class RealSubject implements ISubject {
  2.     @Override
  3.     public void showName(String name) {
  4.         System.out.println(name+”闪亮登场”);
  5.     }
  6. }

 

LogHandler类

为了更明确的说明动态代理的工作原理,将代理的创建过程放到了LogHandler的外部,即main方法中

[java][/java] view plaincopy

  1. public class LogHandler implements InvocationHandler {
  2.     Object target=null;
  3.     public Object getTarget() {
  4.         return target;
  5.     }
  6.     public void setTarget(Object target) {
  7.         this.target = target;
  8.     }
  9.     @Override
  10.     public Object invoke(Object proxy, Method method, Object[] args)
  11.             throws Throwable {
  12.         Object result=null;
  13.         //调用目标对象方法前的逻辑
  14.         System.out.println(“下面有一个大人物要出现”);
  15.         //调用目标对象的方法,这句代码将代理与目标类联系了起来
  16.         method.invoke(target, args);
  17.         //调用目标对象方法后的逻辑
  18.         System.out.println(“大家鼓掌欢迎”);
  19.         return result;
  20.     }
  21. }

 

 

客户端类Client

 

[java][/java] view plaincopy

  1. public class Client {
  2.     /**
  3.      * @param args
  4.      */
  5.     public static void main(String[] args) {
  6.         LogHandler logHandler=new LogHandler();
  7.         logHandler.setTarget(new RealSubject());
  8.         //创建代理对象
  9.         ISubject proxySubject=(ISubject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), logHandler);
  10.         System.out.println(“——-JDK Proxy————-“);
  11.         proxySubject.showName(“委座”);
  12.     }
  13. }

 

 

执行结果

 

 

调用过程时序图

 

 

 

 

2.“继承父类”—CGLIB

 

用到了CBLIB提供的Enhancer类和MethodInterceptor接口

 

类之间的关系如下

需要引入第三方jar包

 

  • cglib-2.2.jar
  • asm-3.1.jar

 

 

Enhancer类

用于创建代理对象

MethodInterceptor接口

用于处理方法请求

 

代码演示

ISubject接口,RealSubject类同上

 

LogIntercept类

[java][/java] view plaincopy

  1. public class LogIntercept implements MethodInterceptor {
  2.     Object target=null;
  3.     public Object getTarget() {
  4.         return target;
  5.     }
  6.     public void setTarget(Object target) {
  7.         this.target = target;
  8.     }
  9.     @Override
  10.     public Object intercept(Object arg0, Method arg1, Object[] arg2,
  11.             MethodProxy arg3) throws Throwable {
  12.         Object result=null;
  13.         //调用目标对象方法前的逻辑
  14.         System.out.println(“下面有一个大人物要出现”);
  15.         //调用目标对象的方法,这句代码将代理与目标类联系了起来
  16.         arg3.invoke(target, arg2);
  17.         //调用目标对象方法后的逻辑
  18.         System.out.println(“大家鼓掌欢迎”);
  19.         return result;
  20.     }
  21. }

 

 

客户端类Client

[java][/java] view plaincopy

  1. public class Client {
  2.     /**
  3.      * @param args
  4.      */
  5.     public static void main(String[] args) {
  6.         LogIntercept logIntercept=new LogIntercept();
  7.         logIntercept.setTarget(new RealSubject());
  8.         Enhancer enhancer=new Enhancer();
  9.         enhancer.setSuperclass(RealSubject.class);
  10.         enhancer.setCallback(logIntercept);
  11.         ISubject proxySubject=(ISubject)enhancer.create();
  12.         System.out.println(“——-CBLIB————-“);
  13.         proxySubject.showName(“委座”);
  14.     }
  15. }

 

调用过程时序图

 

总结

大家可以看到JDK Proxy和CGLIB这两种动态代理的实现过程是非常相似的,但也有区别

相同点:

  • 都用到了一个接口一个类;
  • 接口用于处理方法调用,类用于创建代理对象

JDK Proxy

InvocationHandler接口

Proxy类

CGLIB

MethodIntercept接口

Enhancer类

不同点:

JDK Proxy

使用目标类的接口创建动态代理

CBLIB

使用目标类的子类创建动态代理

 

最后

JDK Proxy和CGLIB两种动态代理各有千秋,具体用哪个方案要看具体情况。如果目标类实现了对应接口,两种方案都可以;如果没有实现任何接口则要使用CBLIB。比如Hibernate中的实体类是POJO类,没有实现任何接口,那么要通过代理实现延迟加载就只能采用CGLIB方案了。

 

标签