动态代理proxy与CGLib的区别

  1. 什么是代理?
  2. 静态代理与动态代理
  3. 静态代理实例
  4. JDK动态代理实例
  5. CGLib 简介
  6. CGLib 与JDK动态代理的区别

 

    代理模式是Java中常见的一种模式,英文名字叫走Proxy或者Surrogate,代理的本意是一个人代表另一个人,或者一个机构代表另一个机构,采取行动,因而,代理和现实生活中的中介有很大的类似,你买房子、卖房子,可以自己去操作,但是需要了解和买卖房产无关的细节,如契税等,而找一个中介,则不用关心这些与买卖房产无直接关系的中间细节,只关心业务本身。

     因而,我们可以把代理模式的适用,总结为:代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等

   代理模式的用途,可以分为如下几种:

    1)远程代理(Remote ):为远程对象的invoke提供代理,典型的例子如RMI或者EJB,都会在本地生成一个远程对象的stub。

    2)虚拟代理(Virtual):如果需要创建一个资源消耗大的对象,则可以首先使用一个代理,使这个对象在需要时才真正创建。

    3)Copy-On-Write 代理:把复制延迟到客户端需要时才采取行动

    4)保护(Protect)

    5)Cache代理

   2.静态代理与动态代理

代理分为静态代理和动态代理

   按照代理创建的时期,可以分为动态代理和静态代理:

 静态代理:由程序员或者自动生成工具生成代理类,然后进行代理类的编译和运行。在代理类、委托类运行之前,代理类已经以.class的格式存在。

 静态代理:在程序运行时,由反射机制动态创建而成。

 

 3.静态代理实例

    静态代理实例:

     首先需要一个接口

  1. package net.battier.dao;  
  2.   
  3. /** 
  4.  * 定义一个账户接口 
  5.  *  
  6.  * @author Administrator 
  7.  *  
  8.  */  
  9. public interface Count {  
  10.     // 查看账户方法  
  11.     public void queryCount();  
  12.   
  13.     // 修改账户方法  
  14.     public void updateCount();  
  15.   
  16. }  

  然后是委托类,也就是接口的真正实现类,内涵主要的业务逻辑:

  1. package net.battier.dao.impl;  
  2.   
  3. import net.battier.dao.Count;  
  4.   
  5. /** 
  6.  * 委托类(包含业务逻辑) 
  7.  *  
  8.  * @author Administrator 
  9.  *  
  10.  */  
  11. public class CountImpl implements Count {  
  12.   
  13.     @Override  
  14.     public void queryCount() {  
  15.         System.out.println("查看账户方法...");  
  16.   
  17.     }  
  18.   
  19.     @Override  
  20.     public void updateCount() {  
  21.         System.out.println("修改账户方法...");  
  22.   
  23.     }  
  24.   
  25. }  

   最后是代理类:

  1. CountProxy.java  
  2. package net.battier.dao.impl;  
  3.   
  4. import net.battier.dao.Count;  
  5.   
  6. /** 
  7.  * 这是一个代理类(增强CountImpl实现类) 
  8.  *  
  9.  * @author Administrator 
  10.  *  
  11.  */  
  12. public class CountProxy implements Count {  
  13.     private CountImpl countImpl;  
  14.   
  15.     /** 
  16.      * 覆盖默认构造器 
  17.      *  
  18.      * @param countImpl 
  19.      */  
  20.     public CountProxy(CountImpl countImpl) {  
  21.         this.countImpl = countImpl;  
  22.     }  
  23.   
  24.     @Override  
  25.     public void queryCount() {  
  26.         System.out.println("事务处理之前");  
  27.         // 调用委托类的方法;  
  28.         countImpl.queryCount();  
  29.         System.out.println("事务处理之后");  
  30.     }  
  31.   
  32.     @Override  
  33.     public void updateCount() {  
  34.         System.out.println("事务处理之前");  
  35.         // 调用委托类的方法;  
  36.         countImpl.updateCount();  
  37.         System.out.println("事务处理之后");  
  38.   
  39.     }  
  40.   
  41. }  

运行:

  1. package com.mahoutchina.pattern.proxy;  
  2.   
  3. public class CountTest {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         CountImpl countImpl = new CountImpl();    
  10.         CountProxy countProxy = new CountProxy(countImpl);    
  11.         countProxy.updateCount();    
  12.         countProxy.queryCount();   
  13.   
  14.     }  
  15.   
  16. }  

  从静态代理中可以看出:

 

    1.接口:代理类需要实现一个接口,这个接口和委托类的接口是一样的,这样proxy才能和委托类行为表现一致

    2. 方法(Method):由于接口限制,proxy类中也要有interface中的各个方法,这就造成了代码重复

   

4.JDK动态代理实例
    动态代理类克服了proxy需要继承专一的interface接口,并且要实现相应的method的缺陷。从JDK 1.3以来,Java 语言通过java.lang.reflex库提供的三个类直接支持代理:

     java.lang.reflect.Proxy,java.lang.reflect.InvocationHandler 和Method.

    Proxy类在运行时动态创建代理对象,这也是dynamic proxy的由来,下面是类图,其中最重要的是newProxyInstance,这个方法中,指明了将要代理的类的加载器,业务类接口,以及代理类要执行动作的调用处理器(InvokeHandler)

 

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 
                               throws IllegalArgumentException 
参数说明: 
ClassLoader loader:类加载器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子类实例 

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器; 
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的; 
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类; 
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。 

   当系统有了一个代理对象之后,对原方法的调用会首先被分派到一个调用处理器(Invocation Handler).InvocationHandler 接口如下图所示:

    

  代码:

    接口:

 
  1. package com.mahoutchina.pattern.proxy.dynamicproxy;  
  2.   
  3. public interface BookFacade {  
  4.     public void addBook();  
  5.     public void deleteBook();  
  6. }  

  实际业务类:

 

 

  1. package com.mahoutchina.pattern.proxy.dynamicproxy;  
  2.   
  3. public class BookFacadeImpl implements BookFacade {  
  4.   
  5.     @Override  
  6.     public void addBook() {  
  7.         System.out.println("add book logic is running。。。");   
  8.     }  
  9.   
  10.     @Override  
  11.     public void deleteBook() {  
  12.         System.out.println("delete book logic is running。。。");  
  13.           
  14.     }  
  15.       
  16.   
  17. }  

  动态代理类:

 

 
  1. package com.mahoutchina.pattern.proxy.dynamicproxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.lang.reflect.Proxy;  
  6.   
  7. public class BookFacadeProxy implements InvocationHandler {  
  8.     private Object target;  
  9.   
  10.     /** 
  11.      *  
  12.      * @param target 
  13.      * @return 
  14.      */  
  15.     public Object bind(Object target) {  
  16.         this.target = target;  
  17.         // 取得代理对象  
  18.         return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
  19.                 target.getClass().getInterfaces(), this);  
  20.     }  
  21.   
  22.     @Override  
  23.     public Object invoke(Object proxy, Method method, Object[] args)  
  24.             throws Throwable {  
  25.         Object result=null;  
  26.         System.out.println("Proxy start...");  
  27.         System.out.println("method name:"+method.getName());  
  28.         result=method.invoke(target, args);  
  29.         System.out.println("Proxy end...");  
  30.         return result;  
  31.     }  
  32.   
  33. }  

  测试类:

  

 
  1. package com.mahoutchina.pattern.proxy.dynamicproxy;  
  2.   
  3. ublic class TestProxy {  
  4.   
  5. /** 
  6.  * @param args 
  7.  */  
  8. public static void main(String[] args) {  
  9.     BookFacadeProxy proxy = new BookFacadeProxy();  
  10.     BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());  
  11.     bookProxy.addBook();  
  12.     bookProxy.deleteBook();  
  13. }  

   对于JDK 的Proxy,有以下几点:

 

    1)Interface:对于JDK proxy,业务类是需要一个Interface的,这也是一个缺陷

    2)Proxy,Proxy 类是动态产生的,这个类在调用Proxy.newProxyInstance(targetCls.getClassLoader, targetCls.getInterface,InvocationHander)之后,会产生一个Proxy类的实例。实际上这个Proxy类也是存在的,不仅仅是类的实例。这个Proxy类可以保存到硬盘上。

    3) Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来

    4) InvocationHandler: 这个类在业务委托类执行时,会先调用invoke方法。invoke方法再执行相应的代理操作,可以实现对业务方法的再包装

    5 CGLib 简介

          JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 
示例 

   业务类:

  1. package net.battier.dao;  
  2.   
  3. public interface BookFacade {  
  4.     public void addBook();  
  5. }  
 
  1. package net.battier.dao.impl;  
  2.   
  3. /** 
  4.  * 这个是没有实现接口的实现类 
  5.  *  
  6.  * @author student 
  7.  *  
  8.  */  
  9. public class BookFacadeImpl1 {  
  10.     public void addBook() {  
  11.         System.out.println("增加图书的普通方法...");  
  12.     }  
  13. }  

  代理:

 
  1. package net.battier.proxy;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import net.sf.cglib.proxy.Enhancer;  
  6. import net.sf.cglib.proxy.MethodInterceptor;  
  7. import net.sf.cglib.proxy.MethodProxy;  
  8.   
  9. /** 
  10.  * 使用cglib动态代理 
  11.  *  
  12.  * @author student 
  13.  *  
  14.  */  
  15. public class BookFacadeCglib implements MethodInterceptor {  
  16.     private Object target;  
  17.   
  18.     /** 
  19.      * 创建代理对象 
  20.      *  
  21.      * @param target 
  22.      * @return 
  23.      */  
  24.     public Object getInstance(Object target) {  
  25.         this.target = target;  
  26.         Enhancer enhancer = new Enhancer();  
  27.         enhancer.setSuperclass(this.target.getClass());  
  28.         // 回调方法  
  29.         enhancer.setCallback(this);  
  30.         // 创建代理对象  
  31.         return enhancer.create();  
  32.     }  
  33.   
  34.     @Override  
  35.     // 回调方法  
  36.     public Object intercept(Object obj, Method method, Object[] args,  
  37.             MethodProxy proxy) throws Throwable {  
  38.         System.out.println("事物开始");  
  39.         proxy.invokeSuper(obj, args);  
  40.         System.out.println("事物结束");  
  41.         return null;  
  42.   
  43.   
  44.     }  
  45.   
  46. }  

  测试;

 

   

[java] view plain copy
 
    1. package net.battier.test;  
    2.   
    3. import net.battier.dao.impl.BookFacadeImpl1;  
    4. import net.battier.proxy.BookFacadeCglib;  
    5.   
    6. public class TestCglib {  
    7.       
    8.     public static void main(String[] args) {  
    9.         BookFacadeCglib cglib=new BookFacadeCglib();  
    10.         BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());  
    11.         bookCglib.addBook();  
    12.     }  
    13. }  
posted @ 2016-11-26 16:42  starskyhu  阅读(938)  评论(0编辑  收藏  举报