Spring事務Transactional和動態代理(一)-JDK代理實現

系列文章索引:

  1. Spring事務Transactional和動態代理(一)-JDK代理實現
  2. Spring事務Transactional和動態代理(二)-cglib動態代理
  3. Spring事務Transactional和動態代理(三)-事務失效的場景

什麼是代理

理設計模式提供了對目標對象的間接訪問方式,能力模式能夠解耦合併且便於擴展目標的功能。

在現實生活這,我們消費者如果要去購買一杯牛奶的時候,並不是直接去找牛奶廠商購買,而是在便利店或者超市購買(零售商);超市進貨的時候也通常不是直接找牛奶廠商,而是找市級代理(渠道經銷商),市級代理再找省級代理(代理商),省級代理從牛奶生產商(廠商)提貨。
如下圖,雖然通過層層代理,一杯牛奶的價格會有增加,但是用戶省卻了時間(用戶不能跑到內蒙去買牛奶)。

靜態代理

靜態代理的實現比較簡單,代理類通過實現與目標對象相同的介面,並在類中維護一個代理對象。通過構造器塞入目標對象,賦值給代理對象,進而執行代理對象實現的介面方法

public interface Person {      void eat();  }    public class Child implements Person {      @Override      public void eat() {          System.out.println("A Child eats something");      }  }      public class StaticProxyDemo {        public static void main(String[] args) {          Person person = new Child();          person.eat();      }  }

代理類如下:

public class PersonProxy {     private Person person;       public PersonProxy(Person person){         this.person = person;     }       private void beforeExecute(){         System.out.println("before");     }       public void execute(){         beforeExecute();         person.eat();         afterExecute();     }        private void afterExecute(){          System.out.println("after");      }          public static void main(String[] args) {          PersonProxy personProxy = new PersonProxy(new Child());          personProxy.execute();      }  }  

靜態代理的優點:

這樣可以通過PersonProxy來操作目標對象Person,且在不修改Person對象的條件下能夠對目標對象進行beforeExecute()和afterExecute()的攔截操作,這樣就達到了解耦合和擴展目標對象的功能。

靜態代理的缺點:

因為代理對象,需要實現與目標對象一樣的介面,會導致代理類十分繁多,不易維護,同時一旦介面增加方法,則目標對象和代理類都需要維護。

動態代理的實現

JDK的動態代理是基於java.lang.reflect.Proxy實現的InvocationHandler介面
增加Proxy對象,需要實現InvocationHandler,可以看到DynamicPersonProxy類並沒有實現Person介面或者繼承Person介面的子類,DynamicPersonProxy類是完全與Person松耦合

import java.lang.reflect.InvocationHandler;  import java.lang.reflect.Method;  import java.lang.reflect.Proxy;    public class DynamicPersonProxy implements InvocationHandler {        private Object target;        public DynamicPersonProxy(Object target){          this.target = target;      }        public <T> T getProxy(){          return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);      }        private void beforeInvoke(){          System.out.println("before");      }        private void afterInvoke(){          System.out.println("after");      }        @Override      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {          beforeInvoke();          Object result = method.invoke(target,args);          afterInvoke();          return result;      }  }

測試類如下:

public class DynamicProxyTest{        public static void main(String[] args) {  //        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");          Person person = new Child();          DynamicPersonProxy dynamicPersonProxy = new DynamicPersonProxy(person);          Person proxyPerson = (Person) dynamicPersonProxy.getProxy();          proxyPerson.eat();  }    }    //輸出如下:  before  A Child eats something  after

動態代理的優點:

代理對象無需實現介面,介面增加方法也就無需再修改代理對象

動態代理的缺點:

目標對象一定要實現介面,否則無法使用JDK動態代理

動態代理的原理

動態代理的核心流程是:

  1. 為介面創建代理類的位元組碼文件
  2. 使用ClassLoader將位元組碼文件載入到JVM
  3. 創建代理類實例對象
  4. 執行對象的目標方法

JDK Proxy源碼分析

下面從源碼的角度來看一下動態代理的實現原理
核心方法Proxy.newProxyInstance源碼如下:

    @CallerSensitive      public static Object newProxyInstance(ClassLoader loader,                                            Class<?>[] interfaces,                                            InvocationHandler h)          throws IllegalArgumentException      {          //InvocationHandler不能為空          Objects.requireNonNull(h);          //clone介面          final Class<?>[] intfs = interfaces.clone();          final SecurityManager sm = System.getSecurityManager();          if (sm != null) {              checkProxyAccess(Reflection.getCallerClass(), loader, intfs);          }            //首先從快取查找是否有代理類,沒有就生成一個          Class<?> cl = getProxyClass0(loader, intfs);            /*           * 通過InvocationHandler調用目標類的構造函數           */          try {              if (sm != null) {                  checkNewProxyPermission(Reflection.getCallerClass(), cl);              }              //constructorParams是指指定代理類的構造函數類型              final Constructor<?> cons = cl.getConstructor(constructorParams);              final InvocationHandler ih = h;              //如果構造函數不是public修飾,修改              if (!Modifier.isPublic(cl.getModifiers())) {                  AccessController.doPrivileged(new PrivilegedAction<Void>() {                      public Void run() {                          cons.setAccessible(true);                          return null;                      }                  });              }              return cons.newInstance(new Object[]{h});          } catch (IllegalAccessException|InstantiationException e) {              throw new InternalError(e.toString(), e);          } catch (InvocationTargetException e) {              Throwable t = e.getCause();              if (t instanceof RuntimeException) {                  throw (RuntimeException) t;              } else {                  throw new InternalError(t.toString(), t);              }          } catch (NoSuchMethodException e) {              throw new InternalError(e.toString(), e);          }      }

其中查找Proxy類的源碼如下:

    private static Class<?> getProxyClass0(ClassLoader loader,                                             Class<?>... interfaces) {          //長度檢查          if (interfaces.length > 65535) {              throw new IllegalArgumentException("interface limit exceeded");          }            //調用了下面的WeakCache<K, P, V>.get(K key, P parameter)方法,loader作為key,interfaces作為parameter參數          //定義如下:proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory())          return proxyClassCache.get(loader, interfaces);      }         //首先當前key(也就是上面的ClassLoader)已經載入存在,就直接從快取中返回     //如果不存在,就會通過ProxyClassFactory來創建代理對象     public V get(K key, P parameter) {          Objects.requireNonNull(parameter);            expungeStaleEntries();           //根據key的hash值和一個ReferenceQueue來構造          Object cacheKey = CacheKey.valueOf(key, refQueue);            // 從map中取出cacheKey的值          ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);          if (valuesMap == null) {              ConcurrentMap<Object, Supplier<V>> oldValuesMap                  = map.putIfAbsent(cacheKey,                                    valuesMap = new ConcurrentHashMap<>());              if (oldValuesMap != null) {                  valuesMap = oldValuesMap;              }          }            Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));          Supplier<V> supplier = valuesMap.get(subKey);          Factory factory = null;            while (true) {              if (supplier != null) {                  // supplier可能是Factory或者CacheValue<V>                  V value = supplier.get();                  if (value != null) {                      return value;                  }              }              // 快取中沒有supplier,同時supplier中沒有              // 懶載入的方式創建一個Factory              if (factory == null) {                  factory = new Factory(key, parameter, subKey, valuesMap);              }                if (supplier == null) {                  supplier = valuesMap.putIfAbsent(subKey, factory);                  if (supplier == null) {                      // 安裝 Factory                      supplier = factory;                  }              } else {                  if (valuesMap.replace(subKey, supplier, factory)) {                      supplier = factory;                  } else {                      supplier = valuesMap.get(subKey);                  }              }          }      }  

再看一下上面提到的ProxyClassFactory是一個 工廠方法,是一個靜態final類,實現了BiFunction介面,其中只有一個apply方法

//類定義   private static final class ProxyClassFactory          implements BiFunction<ClassLoader, Class<?>[], Class<?>>{            @Override          public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);              for (Class<?> intf : interfaces) {                  /*                   * 校驗當前類載入器ClassLoader解析到的名稱和定義的名稱是否相同                   */                  Class<?> interfaceClass = null;                  try {                      interfaceClass = Class.forName(intf.getName(), false, loader);                  } catch (ClassNotFoundException e) {                  }                  if (interfaceClass != intf) {                      throw new IllegalArgumentException(                          intf + " is not visible from class loader");                  }                  /*                   * 校驗是否是介面類型,這也就是為什麼JDK動態代理只能基於介面                   */                  if (!interfaceClass.isInterface()) {                      throw new IllegalArgumentException(                          interfaceClass.getName() + " is not an interface");                  }                  /*                   * 防重                   */                  if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {                      throw new IllegalArgumentException(                          "repeated interface: " + interfaceClass.getName());                  }              }               // 代理對象的目錄              String proxyPkg = null;              int accessFlags = Modifier.PUBLIC | Modifier.FINAL;                .....                /*               * 生成指定Proxy代理對象的位元組碼               */              byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                  proxyName, interfaces, accessFlags);              try {                  //調用的native方法                  return defineClass0(loader, proxyName,                                      proxyClassFile, 0, proxyClassFile.length);              } catch (ClassFormatError e) {                  /*                   * 生成的代理類有bug                   */                  throw new IllegalArgumentException(e.toString());              }          }    }  

Proxy代理位元組碼生成分析

去掉DynamicProxyTest類中的注釋

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

這樣就可以看到JDK生成的class文件。所生成的$Proxy0特性如下:

  1. 繼承了Proxy,實現了目標介面Person。因為Java不允許多重繼承,這就限制了:使用JDK代理不能是普通類或者抽象類,只能是介面類型
  2. 只有一個InvocationHandler參數的構造函數,所以代理類必須繼承InvocationHandler介面
  3. 生成了同名的eat()方法,且調用了InvocationHandler的invoke方法
  4. 使用靜態程式碼塊初始化Object類的equals,toString和hashCode方法,還有Person介面的eat方法

如上示例反編譯所生成的class文件內容如下:

package com.sun.proxy;    import com.randy.dynamicproxy.jdk.interfaces.Person;  import java.lang.reflect.InvocationHandler;  import java.lang.reflect.Method;  import java.lang.reflect.Proxy;  import java.lang.reflect.UndeclaredThrowableException;    public final class $Proxy0 extends Proxy implements Person {      private static Method m1;      private static Method m3;      private static Method m2;      private static Method m0;        public $Proxy0(InvocationHandler var1) throws  {          super(var1);      }        public final boolean equals(Object var1) throws  {          try {              return (Boolean)super.h.invoke(this, m1, new Object[]{var1});          } catch (RuntimeException | Error var3) {              throw var3;          } catch (Throwable var4) {              throw new UndeclaredThrowableException(var4);          }      }        public final void eat() throws  {          try {              super.h.invoke(this, m3, (Object[])null);          } catch (RuntimeException | Error var2) {              throw var2;          } catch (Throwable var3) {              throw new UndeclaredThrowableException(var3);          }      }        public final String toString() throws  {          try {              return (String)super.h.invoke(this, m2, (Object[])null);          } catch (RuntimeException | Error var2) {              throw var2;          } catch (Throwable var3) {              throw new UndeclaredThrowableException(var3);          }      }        public final int hashCode() throws  {          try {              return (Integer)super.h.invoke(this, m0, (Object[])null);          } catch (RuntimeException | Error var2) {              throw var2;          } catch (Throwable var3) {              throw new UndeclaredThrowableException(var3);          }      }        static {          try {              m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));              m3 = Class.forName("com.randy.dynamicproxy.jdk.interfaces.Person").getMethod("eat");              m2 = Class.forName("java.lang.Object").getMethod("toString");              m0 = Class.forName("java.lang.Object").getMethod("hashCode");          } catch (NoSuchMethodException var2) {              throw new NoSuchMethodError(var2.getMessage());          } catch (ClassNotFoundException var3) {              throw new NoClassDefFoundError(var3.getMessage());          }      }  }