Java Web之动态代理

作者: YungFan | 来源:发表于2017-01-05 14:14 被阅读622次

    动态代理通俗解释:

    A接口有c方法,类B实现A接口,原本应该是执行B类中的c方法,可现在不这样做,可以先声明产生B类的代理类B',由它来冒充B类的“兄弟”并“实现”A接口, 对外界来说B'应该也有c方法,可当真正调用它的时候, 它会去执行与它关联InvocationHandler的invoke()方法, 在这个方法里面你可以做很多事情。

    Java怎样实现动态代理呢

    第一步,我们要有一个接口,还要有一个接口的实现类,而这个实现类就是我们要代理的类。

    public interface Subject
    {
        public void request();
    }
    
    
    public class RealSubject implements Subject
    {
        public void request()
        {
            System.out.println("From real subject!");
        }
    
    }
    
    

    第二步,我们要自己写一个代理类,它的特点是实现了InvocationHandler接口, 因为代理类的实例在调用实现类的方法的时候,不是去调用真正的实现类的这个方法, 而是调用代理类的invoke()方法,在这个方法中才调用真正的实现类的方法。

    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;
        }
        
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {
            System.out.println("before calling: " + method);
            
            method.invoke(sub, args);
            
            System.out.println(args == null);
            
            System.out.println("after calling: " + method);
            
            return null;
        }   
        
    }
    
    

    上述方法体中method.invoke(owner, args)的解释:执行该method.invoke方法的参数是执行这个方法的对象owner,参数数组args,可以这么理解:owner对象中带有参数args的method方法。返回值是Object,也就是该方法的返回值。

    第三步,客户端要用代理类的实例去调用实现类的方法。

    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();
    
            // newProxyInstance()动态生成一个类并加载到内存
            // 加载到内存要使用加载器,第一个参数是一个类加载器
            Subject subject = (Subject) Proxy.newProxyInstance(classType
                    .getClassLoader(), realSubject.getClass().getInterfaces(),
                    handler);
    
            subject.request();
    
            System.out.println(subject.getClass());
    
        }
    
    }
    
    

    对第三步的解释

    主要是以下代码:

    Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    

    该方法主要做了如下工作:

    • 根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces) 创建代理类$Proxy0,该代理类实现了预先定义的接口(如上面的Subject),并继承了Proxy类。
    public final class $Proxy0 extends Proxy implements Subject
    
    • 实例化$Proxy0(创建代理对象)并在构造方法中把 InvocationHandler(这里handler 是它的实例)传过去

    • $Proxy0调用父类Proxy的构造器,为InvocationHandler 赋值:

    public $Proxy0(InvocationHandler invocationhandler)
    {
      super(invocationhandler);
    }
    ========================================================
    class Proxy
    {
       protected InvocationHandler h;
       protected Proxy(InvocationHandler h) 
       {
             this.h = h;
       }
    }
    
    • 将这个$Proxy0类强制转型成接口类型,当执行接口中的方法时(如上文强转成Subject后调用request()方法),就调用了$Proxy0类中实现的接口方法,在该方法中会调用父类Proxy中的invoke()方法,即InvocationHandler.invoke(),达到做一些其他工作的效果。
    public final void request()
    {
      try
      {
          //m是通过反射得到的方法名 Method类型
          super.h.invoke(this, m, null);
          return;
      }
      catch (Error e)
      {
    
      }
      catch (Throwable throwable)
      {
        throw new UndeclaredThrowableException(throwable);
      }
    }
    

    相关文章

      网友评论

        本文标题:Java Web之动态代理

        本文链接:https://www.haomeiwen.com/subject/xykzvttx.html