zoukankan      html  css  js  c++  java
  • 从代理模式到Spring AOP

    什么是代理模式

               假如我喜欢上隔壁班的翠花,但是我没胆量向她送花,这时候我需要一个铁杆哥们帮我做这件事, 很明显这哥们是个代理,是去执行任务的,但是花实际上是我“送”的,代理和我一样会送花这个动作,直接上代码。

      1 public interface IProcess {
      2     void SendFlower();
      3 }
      1 public class Studnet1 implements IProcess {
      2 
      3     @Override
      4     public void SendFlower() {
      5         System.out.println("my name is Studnet1 , the flower is for you ");
      6     }
      7 }
      8 
      9 
     10 
     11 
     12 
     13 public class ProxyStudent implements IProcess {
     14 
     15     private IProcess targetObj;
     16 
     17     public ProxyStudent(IProcess targetObj) {
     18         this.targetObj = targetObj;
     19     }
     20 
     21     @Override
     22     public void SendFlower() {
     23         System.out.println("check it before send");
     24         targetObj.SendFlower();
     25         System.out.println("check it after send");
     26     }
     27 }
     28 
      1 public class ProcessFactory {
      2     public static IProcess getProcess(){
      3         return  new Studnet1();
      4     }
      5 }
      6 
      1 public class Main {
      2 
      3     public static void main(String[] args) {
      4         IProcess ProxyObj = ProcessFactory.getProcess();
      5         ProxyObj.SendFlower();
      6     }
      7 }

            运行结果:

      1 check it before send
      2 my name is Studnet1 , the flower is for you
      3 check it after send

            很开心,终于把花送出去了,可以见到调用代理者的SendFlower方法,实际上是我的SendFlower 方法,打到我需要送花的目的,同时这铁哥们人缘非常好,其他的同学也需要他来帮忙 , Student2, Student3 , Student4 也需要这铁哥们,

    并且他们的要求不只是送花,还可能邀请看电影….等等,那么ProcessFatory 也要改写,另外假如我不止想送花这个动作,需要添加方法到接口上,那么其他类相应的也要改动。

      1 public class ProcessFactory {
      2     public static IProcess getProcess(){
      3         return  new Studnet1();
      4     }
      5 
      6     public static IHold getHold(){
      7         return  new Studnet2();
      8     }
      9 
     10     public static IMovie getMovier(){
     11         return  new Studnet3();
     12     }
     13 
     14     .....
     15 
     16 }

            显然这样的每次一个新的一个需求都需要创建一个对象,很不合理,于是就出现了动态代理。

     

    动态代理

            先上代码,新建一个类,

      1 public class StuInvocationHandler<T> implements InvocationHandler {
      2     T target;
      3 
      4     public StuInvocationHandler(T target) {
      5         this.target = target;
      6     }
      7 
      8 
      9     @Override
     10     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     11         System.out.println("this is a proxy method");
     12         System.out.println("check it before sending");
     13         Object result = method.invoke(target, args);
     14         System.out.println("check it after sending ");
     15         return result;
     16     }
     17 }

          调用,输出结果:

      1 public class Main {
      2 
      3     public static void main(String[] args) {
      4         IProcess student1 = ProcessFactory.getProcess();
      5         InvocationHandler handler = new StuInvocationHandler<IProcess>(student1);
      6         IProcess ProxyStudent = (IProcess) Proxy.newProxyInstance(IProcess.class.getClassLoader(), new Class<?>[]{IProcess.class}, handler);
      7         ProxyStudent.SendFlower();
      8 
      9     }
     10 }
      1 this is a proxy method
      2 check it before sending
      3 my name is Studnet1 , the flower is for you
      4 check it after sending

            我们似乎看不到了代理类,实际上StuInvocationHandler就是我们的代理类,这时无论代理谁都可以进行操作,动态代理运用了java一个重要的特性—“反射” 。  我们需要知道两点:

    • 代理类调用方法使用了反射
    • 代理类继承了Proxy , 实现了被代理的接口,对应例子中的 IProcess , 由于java是单继承,所以也就决定了java动态代理只能对接口进行代理。

           在main方法中添加以下代码,

      1  byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Studnet1.class.getInterfaces());
      2         String path = "E:StuProxy.class";
      3         try(FileOutputStream fos = new FileOutputStream(path)) {
      4             fos.write(classFile);
      5             fos.flush();
      6             System.out.println("代理类class文件写入成功");
      7         } catch (Exception e) {
      8             System.out.println("写文件错误");
      9         }

             在E盘中就会有一个StuProxy.class 文件,这个就是代理类, 我们用反编译工具java decompiler查看源代码,

      1 import com.benjious.IProcess;
      2 import java.lang.reflect.InvocationHandler;
      3 import java.lang.reflect.Method;
      4 import java.lang.reflect.Proxy;
      5 import java.lang.reflect.UndeclaredThrowableException;
      6 
      7 public final class $Proxy0
      8   extends Proxy
      9   implements IProcess
     10 {
     11   private static Method m1;
     12   private static Method m3;
     13   private static Method m2;
     14   private static Method m0;
     15 
     16   public $Proxy0(InvocationHandler paramInvocationHandler)
     17     throws
     18   {
     19     super(paramInvocationHandler);
     20   }
     21 
     22   public final boolean equals(Object paramObject)
     23     throws
     24   {
     25     try
     26     {
     27       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
     28     }
     29     catch (Error|RuntimeException localError)
     30     {
     31       throw localError;
     32     }
     33     catch (Throwable localThrowable)
     34     {
     35       throw new UndeclaredThrowableException(localThrowable);
     36     }
     37   }
     38 
     39   public final void SendFlower()
     40     throws
     41   {
     42     try
     43     {
     44       this.h.invoke(this, m3, null);
     45       return;
     46     }
     47     catch (Error|RuntimeException localError)
     48     {
     49       throw localError;
     50     }
     51     catch (Throwable localThrowable)
     52     {
     53       throw new UndeclaredThrowableException(localThrowable);
     54     }
     55   }
     56 
     57   public final String toString()
     58     throws
     59   {
     60     try
     61     {
     62       return (String)this.h.invoke(this, m2, null);
     63     }
     64     catch (Error|RuntimeException localError)
     65     {
     66       throw localError;
     67     }
     68     catch (Throwable localThrowable)
     69     {
     70       throw new UndeclaredThrowableException(localThrowable);
     71     }
     72   }
     73 
     74   public final int hashCode()
     75     throws
     76   {
     77     try
     78     {
     79       return ((Integer)this.h.invoke(this, m0, null)).intValue();
     80     }
     81     catch (Error|RuntimeException localError)
     82     {
     83       throw localError;
     84     }
     85     catch (Throwable localThrowable)
     86     {
     87       throw new UndeclaredThrowableException(localThrowable);
     88     }
     89   }
     90 
     91   static
     92   {
     93     try
     94     {
     95       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
     96       m3 = Class.forName("com.benjious.IProcess").getMethod("SendFlower", new Class[0]);
     97       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
     98       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
     99       return;
    100     }
    101     catch (NoSuchMethodException localNoSuchMethodException)
    102     {
    103       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    104     }
    105     catch (ClassNotFoundException localClassNotFoundException)
    106     {
    107       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    108     }
    109   }
    110 }
    111 

    动态代理的弊端

             java动态代理只能对接口进行代理。这个在源代码中也可以看到,CGLib可以解决这个问题,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

             再回想一下,调用invoke方法前后我们都是可以进行其他操作的,实际上这就是Spring里的AOP,Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。

    参考文章:

    1.https://www.cnblogs.com/gonjan-blog/p/6685611.html

    2.http://www.importnew.com/22015.html

  • 相关阅读:
    jQuery对表单的操作
    js-工厂模式
    js中call、apply、bind的区别
    js实现重载和重写
    js封装/继承/多态
    变量的解构赋值
    var & let & const 的区别
    jQuery之animate中的queue
    jQuery之动画
    .trigger
  • 原文地址:https://www.cnblogs.com/Benjious/p/9302016.html
Copyright © 2011-2022 走看看