• Java 动态代理(转)


    一、代理模式

      代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后 处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的 对象的相关方法,来提供特定的服务。 按照代理的创建时期,代理类可以分为两种。 
      静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
      动态代理:在程序运行时,运用反射机制动态创建而成。

    二、静态代理模式

     1 package com.swust.dao
     2 /** 
     3 * 定义一个账户接口 
     4 *  
     5 * @author Administrator 
     6 *  
     7 */  
     8 public interface Count {  
     9 // 查看账户方法  
    10 public void queryCount();  
    11 // 修改账户方法  
    12 public void updateCount();  
    13 }  
    14 
    15 package com.swust.dao.imple
    16 public class CountImpl implements Count {  
    17 @Override  
    18 public void queryCount() {  
    19      System.out.println("查看账户方法...");  
    20 }  
    21 @Override  
    22 public void updateCount() {  
    23      System.out.println("修改账户方法...");  
    24 }  
    25 }  
    26 
    27 public class CountProxy implements Count {  
    28     private CountImpl countImpl;  
    29   
    30     /** 
    31      * 覆盖默认构造器 
    32      *  
    33      * @param countImpl 
    34      */  
    35 public CountProxy(CountImpl countImpl) {  
    36         this.countImpl = countImpl;  
    37     }  
    38   
    39 @Override  
    40 public void queryCount() {  
    41         System.out.println("事务处理之前");  
    42         // 调用委托类的方法;  
    43         countImpl.queryCount();  
    44         System.out.println("事务处理之后");  
    45 }  
    46   
    47 @Override  
    48 public void updateCount() {  
    49 System.out.println("事务处理之前");  
    50 // 调用委托类的方法;  
    51 countImpl.updateCount();  
    52 System.out.println("事务处理之后");  
    53 }  
    54 }  
    55 
    56 
    57 
    58 package net.battier.test;  
    59       
    60 import net.battier.dao.impl.CountImpl;  
    61 import net.battier.dao.impl.CountProxy;  
    62       
    63 /** 
    64  *测试Count类 
    65 *  
    66 * @author Administrator 
    67 *  
    68 */  
    69 public class TestCount {  
    70      public static void main(String[] args) {  
    71             CountImpl countImpl = new CountImpl();  
    72             CountProxy countProxy = new CountProxy(countImpl);  
    73             countProxy.updateCount();  
    74             countProxy.queryCount();  
    75       
    76     }  
    77 }  
    View Code

    三、动态代理模式

      观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 
      再来看一下动态代理: JDK动态代理中包含一个类和一个接口:InvocationHandler接口: 
      public interface InvocationHandler { 
        public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
      } 
    参数说明: 
    Object proxy:指被代理的对象。 
    Method method:要调用的方法。
    Object[] args:方法调用时所需要的参数。

    可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。 

    Proxy类: 
      Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法: 
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException 
    参数说明: 
    ClassLoader loader:类加载器 
    Class<?>[] interfaces:得到全部的接口 
    InvocationHandlerh:得到InvocationHandler接口的子类实例

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

      与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

     1 package net.battier.dao;  
     2 public interface BookFacade {  
     3         public void addBook();  
     4 }  
     5 
     6 package net.battier.dao.impl;  
     7   
     8 import net.battier.dao.BookFacade;  
     9   
    10 public class BookFacadeImpl implements BookFacade {  
    11   
    12 @Override  
    13 public void addBook() {  
    14         System.out.println("增加图书方法。。。");  
    15     }  
    16   
    17 }  
    18 
    19 package net.battier.proxy;  
    20   
    21 import java.lang.reflect.InvocationHandler;  
    22 import java.lang.reflect.Method;  
    23 import java.lang.reflect.Proxy;  
    24   
    25 /** 
    26  * JDK动态代理代理类 
    27  *  
    28  * @author student 
    29  *  
    30  */  
    31 public class BookFacadeProxy implements InvocationHandler {  
    32     private Object target;  
    33     /** 
    34      * 绑定委托对象并返回一个代理类 
    35      * @param target 
    36      * @return 
    37      */  
    38     public Object bind(Object target) {  
    39         this.target = target;  
    40         //取得代理对象  
    41         return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
    42                 target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)  
    43     }  
    44   
    45     @Override  
    46     /** 
    47      * 调用方法 
    48      */  
    49     public Object invoke(Object proxy, Method method, Object[] args)  
    50             throws Throwable {  
    51         Object result=null;  
    52         System.out.println("事务开始");  
    53         //执行方法  
    54         result=method.invoke(target, args);  
    55         System.out.println("事务结束");  
    56         return result;  
    57     }  
    58 }
    59 
    60 
    61 package net.battier.test;  
    62       
    63 import net.battier.dao.BookFacade;  
    64 import net.battier.dao.impl.BookFacadeImpl;  
    65 import net.battier.proxy.BookFacadeProxy;  
    66       
    67 public class TestProxy {  
    68       
    69 public static void main(String[] args) {  
    70 BookFacadeProxy proxy = new BookFacadeProxy();  
    71     BookFacade bookProxy = (BookFacade) proxy.bind(new  BookFacadeImpl());
    72     bookProxy.addBook();  
    73 }  
    74 }  
    75   
    View Code

    但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。 

    四、Cglib动态代理模式

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

     1 package net.battier.dao;  
     2   
     3 public interface BookFacade {  
     4     public void addBook();  
     5 }
     6 
     7 
     8 package net.battier.dao.impl;  
     9       
    10 /** 
    11 * 这个是没有实现接口的实现类 
    12 *  
    13 * @author student 
    14 *  
    15 */  
    16 public class BookFacadeImpl1 {  
    17 public void addBook() {  
    18     System.out.println("增加图书的普通方法...");  
    19     }  
    20 }  
    21 
    22 package net.battier.proxy;  
    23   
    24 import java.lang.reflect.Method;  
    25   
    26 import net.sf.cglib.proxy.Enhancer;  
    27 import net.sf.cglib.proxy.MethodInterceptor;  
    28 import net.sf.cglib.proxy.MethodProxy;  
    29   
    30 /** 
    31  * 使用cglib动态代理 
    32  *  
    33  * @author student 
    34  *  
    35  */  
    36 public class BookFacadeCglib implements MethodInterceptor {  
    37     private Object target;  
    38   
    39     /** 
    40      * 创建代理对象 
    41      *  
    42      * @param target 
    43      * @return 
    44      */  
    45 public Object getInstance(Object target) {  
    46     this.target = target;  
    47     Enhancer enhancer = new Enhancer();  
    48     enhancer.setSuperclass(this.target.getClass());  
    49     // 回调方法  
    50     enhancer.setCallback(this);  
    51     // 创建代理对象  
    52     return enhancer.create();  
    53 }  
    54   
    55     @Override  
    56     // 回调方法  
    57 public Object intercept(Object obj, Method method, Object[] args,  
    58         MethodProxy proxy) throws Throwable {  
    59         System.out.println("事物开始");  
    60         proxy.invokeSuper(obj, args);  
    61         System.out.println("事物结束");  
    62         return null;   
    63     }  
    64 }
    65 
    66 package net.battier.test;  
    67   
    68 import net.battier.dao.impl.BookFacadeImpl1;  
    69 import net.battier.proxy.BookFacadeCglib;  
    70   
    71 public class TestCglib {  
    72       
    73     public static void main(String[] args) {  
    74         BookFacadeCglib cglib=new BookFacadeCglib();  
    75         BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());  
    76         bookCglib.addBook();  
    77     }  
    78 }
    View Code

    五、总结

     一个典型的动态代理创建对象过程可分为以下四个步骤:
      1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
      2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
      3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor=clazz.getConstructor(new Class[]{InvocationHandler.class});
      4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))。

  • 相关阅读:
    LeetCode 1447. Simplified Fractions
    LeetCode 717. 1bit and 2bit Characters
    LeetCode 1725. Number Of Rectangles That Can Form The Largest Square
    LeetCode 2016. Maximum Difference Between Increasing Elements
    LeetCode 二叉树遍历算法题解 All In One
    Top Universities in China Open Source Course Materials All In One
    SQL 查询语句: 字符串正则匹配 All In One
    React memo & useMemo All In One All In One
    webpack 性能优化 All In One
    漫画教程: HTTPS 的工作原理 (中文版)All In One
  • 原文地址:https://www.cnblogs.com/sunfie/p/4369121.html
Copyright © 2020-2023  润新知