zoukankan      html  css  js  c++  java
  • Java动态代理学习

    动态代理类

      Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

      1.Interface InvocationHandler

      该接口中仅定义了一个方法:

      Object invoke(Object proxy, Method method, Object[] args) 

      在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组(无参时设置为null)。

      这个抽象方法在代理类中动态实现。

      2.Proxy

      该类即为动态代理类,作用类似于上文例子中的ProxySubject,其中主要包含如下内容:

      protected  Proxy(InvocationHandler h): 构造函数,用于给内部的invocation handler赋值。

      static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) : loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

      static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在Subject接口中声明过的方法)。

     

    动态代理类说明

      所谓Dynamic Proxy是这样一种class:

      它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。

      你当然可以把该class的实例当作这些interface中的任何一个来用。

      当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

      在使用动态代理类时,我们必须实现InvocationHandler接口。每一个动态代理类都会有一个与之关联的invocation handler。

      真正的调用是在invocation handler的invoke()方法里完成的。

    动态代理步骤

      1.创建一个实现接口InvocationHandler的类,它必须实现invoke()方法。

      2.创建被代理的类以及接口。

      3.通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建一个代理。

      4.通过代理调用方法。

    动态代理例子1:

      首先定义抽象角色和真实角色类:

    public interface Subject
    {
        public void request();
    }
    复制代码
    public class RealSubject implements Subject
    {
        @Override
        public void request()
        {
            System.out.println("From real subject!");
        }
    }
    复制代码

      之后定义一个DynamicSubject类:

    复制代码
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象。
     * 该类实现了invoke()方法,该方法中的method.invoke()其实就是调用被代理对象的将要执行的方法,
     * 方法参数sub表示该方法从属于sub。
     * 通过动态代理类,我们可以在执行真实对象的方法前后加入自己的一些额外方法
     *
     */
    public class DynamicSubject implements InvocationHandler
    {
    
        //对真实对象的引用
        private Object sub;
        
        public DynamicSubject(Object obj)
        {
            this.sub = obj;
            
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {
            System.out.println("Before calling: " + method);
            
            //通过反射来调用方法
            method.invoke(sub, args);
            
            System.out.println("After calling: " + method);
            return null;
        }
    
    }
    复制代码

      使用的时候:

    复制代码
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class Client
    {
        public static void main(String[] args)
        {
            RealSubject realSubject = new RealSubject();
    
            InvocationHandler handler = new DynamicSubject(realSubject);
    
            Class<?> classType = handler.getClass();
    
            // 生成代理
            // 动态生成一个类(实现了指定的接口),生成类的对象,转换成接口类型
            Subject subject = (Subject) Proxy.newProxyInstance(classType
                    .getClassLoader(), realSubject.getClass().getInterfaces(),
                    handler);
    
            subject.request();
            // 调用方法时,转移给handler接管,由其中的invoke()方法实际完成方法执行
    
            System.out.println(subject.getClass());// 打印出:class $Proxy0
            // $Proxy0是在运行期间动态生成的一个类
    
        }
    
    }
    复制代码

      通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

     

    动态代理实现例子2:

      这个例子中定义了一个接口: 

    public interface Foo
    {
        public void doAction();
    }

      这个接口有两个实现类:

    复制代码
    public class FooImpl1 implements Foo
    {
        @Override
        public void doAction()
        {
            System.out.println("From Implement 1 !");
        }
    }
    
    public class FooImpl2 implements Foo
    {
        @Override
        public void doAction()
        {
            System.out.println("From Implement 2 !");
        }
    }
    复制代码

      定义invocation handler,其中的set方法使得实际对象是可更换的:

    复制代码
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class CommonInvocationHandler implements InvocationHandler
    {
        private Object target;
    
        public CommonInvocationHandler()
        {
    
        }
    
        public CommonInvocationHandler(Object obj)
        {
            this.target = obj;
        }
    
        public void setTarget(Object target)
        {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {        
            return method.invoke(target, args);
        }
    
    }
    复制代码

      使用:

    复制代码
    import java.lang.reflect.Proxy;
    
    public class Demo
    {
        public static void main(String[] args)
        {
            CommonInvocationHandler handler = new CommonInvocationHandler();
    
            Foo f = null;
    
            handler.setTarget(new FooImpl1());
    
            f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                    new Class[] { Foo.class }, handler);
            
            f.doAction();
            System.out.println("----------------------------");
            handler.setTarget(new FooImpl2());    
            f.doAction();
    
        }
    
    }
    复制代码

      程序运行后输出:

    From Implement 1 !
    ----------------------------
    From Implement 2 !

    动态代理实现例子3:

    1、BookFacade.java  

     public interface BookFacade {
    
     public void addBook();  
    
    }  

    2、BookFacadeImpl.java   

    public class BookFacadeImpl implements BookFacade {  
    
    @Override  
    
    public void addBook() {  
    
            System.out.println("增加图书方法。。。");  
    
        }  
    }  

    3、BookFacadeProxy.java  

    • package net.battier.proxy;  
      import java.lang.reflect.InvocationHandler;  
      import java.lang.reflect.Method;  
      import java.lang.reflect.Proxy;  
      /** 
       * JDK动态代理代理类 
       *  
       * @author student 
       *  
       */  
      public class BookFacadeProxy implements InvocationHandler {  
      private Object target;  
      /** 
           * 绑定委托对象并返回一个代理类 
           * @param target 
           * @return 
           */  
      public Object bind(Object target) {  
      this.target = target;  
      //取得代理对象  
      return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                      target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)  
          }  
      @Override  
      /** 
           * 调用方法 
           */  
      public Object invoke(Object proxy, Method method, Object[] args)  
      throws Throwable {  
              Object result=null;  
              System.out.println("事物开始");  
      //执行方法  
              result=method.invoke(target, args);  
              System.out.println("事物结束");  
      return result;  
          }  
      }  

      4、TestProxy.java 

    • package net.battier.test;  
      import net.battier.dao.BookFacade;  
      import net.battier.dao.impl.BookFacadeImpl;  
      import net.battier.proxy.BookFacadeProxy;  
      public class TestProxy {  
      public static void main(String[] args) {  
              BookFacadeProxy proxy = new BookFacadeProxy();  
              BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());  
              bookProxy.addBook();  
          }  
      }  

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

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

    • package net.battier.dao;  
      public interface BookFacade {  
      public void addBook();  
      }  

    2、BookCadeImpl1.java 

    • package net.battier.dao.impl;  
      /** 
       * 这个是没有实现接口的实现类 
       *  
       * @author student 
       *  
       */  
      public class BookFacadeImpl1 {  
      public void addBook() {  
              System.out.println("增加图书的普通方法...");  
          }  
      }  

    3、BookFacadeProxy.java 

    • package net.battier.proxy;  
      import java.lang.reflect.Method;  
      import net.sf.cglib.proxy.Enhancer;  
      import net.sf.cglib.proxy.MethodInterceptor;  
      import net.sf.cglib.proxy.MethodProxy;  
      /** 
       * 使用cglib动态代理 
       *  
       * @author student 
       *  
       */  
      public class BookFacadeCglib implements MethodInterceptor {  
      private Object target;  
      /** 
           * 创建代理对象 
           *  
           * @param target 
           * @return 
           */  
      public Object getInstance(Object target) {  
      this.target = target;  
              Enhancer enhancer = new Enhancer();  
              enhancer.setSuperclass(this.target.getClass());  
      // 回调方法  
              enhancer.setCallback(this);  
      // 创建代理对象  
      return enhancer.create();  
          }  
      @Override  
      // 回调方法  
      public Object intercept(Object obj, Method method, Object[] args,  
                  MethodProxy proxy) throws Throwable {  
              System.out.println("事物开始");  
              proxy.invokeSuper(obj, args);  
              System.out.println("事物结束");  
      return null;  
          }  
      }  

    4、TestCglib.java 

    package net.battier.test;  
    import net.battier.dao.impl.BookFacadeImpl1;  
    import net.battier.proxy.BookFacadeCglib;  
    public class TestCglib {  
    public static void main(String[] args) {  
            BookFacadeCglib cglib=new BookFacadeCglib();  
            BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());  
            bookCglib.addBook();  
        }  
    }  



      

  • 相关阅读:
    二叉树中序遍历 2021.4.14
    简单题 好数对题目 2021.4.13
    2021.4.3 在排序数组中查找元素的第一个和最后一个位置
    力扣刷题记录2021.4.13 最富有客户的资产总量
    iOS开源项目阅读整理
    Missing iOS Distribution signing identity解决方案
    微信票选项目遇到的坑
    [转]从此爱上iOS Autolayout
    [转]iOS应用性能调优的25个建议和技巧
    iOS中用json接收图片的二进制流
  • 原文地址:https://www.cnblogs.com/upcwanghaibo/p/5656113.html
Copyright © 2011-2022 走看看