1. 程式人生 > >Spring溫故而知新 – AOP代理

Spring溫故而知新 – AOP代理

for 過程 orm machine create 輸出 scac finally mac

AOP的概念

AOP:Aspect-Oriented Programming(面向切面編程),維基百科的解釋如下:Aspect是一種新的模塊化機制,用來描述分散在對象、類或者函數中的橫切關註點,從關註點中分離出橫切關註點是面向切面的程序設計的核心概念。分離關註點使解決特定領域問題的代碼從業務邏輯中獨立出來,業務邏輯的代碼中不在含有針對特定領域問題的代碼的調用,業務邏輯同特定領域問題的關系通過切面來封裝、維護,這樣原本分散在整個應用程序中的變動就可以很好地管理起來。從AOP的角度,應用可以分為橫切關註點和業務邏輯代碼,實際開發中,這些橫切關註點往往會直接嵌入到業務邏輯代碼中,面向切面編程就是要解決把橫切關註點與業務邏輯相分離

實現方式:

Spring默認使用 JDK 動態代理作為AOP的代理,缺陷是目標類的類必須實現接口,否則不能使用JDK動態代理。如果需要代理的是類而不是接口,那麽Spring會默認使用CGLIB代理,關於兩者的區別:jdk動態代理是通過java的反射機制來實現的,目標類必須要實現接口,cglib是針對類來實現代理的,他的原理是動態的為指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。

JDK動態代理

Jdk動態代理是在程序運行過程中,根據目標類實現的接口來動態生成代理類的class文件,使用主要涉及兩個類:

InvocationHandler接口: 它提供了一個invoke(Object obj,Method method, Object[] args)方法供實現者提供相應的代理邏輯的實現。可以對實際的實現進行一些特殊的處理其中參數

            Object obj :被代理的目標類

            Method method: 需要執行的目標類的方法

            Object[] args :目標方法的參數

Proxy類:提供一個方法newProxyInstance (ClassLoader loader, Class[] interfaces, InvocationHandler h)來獲得動態代理類

示例代碼:

public interface OrderService {

    public void createOrder(); 
}
public class OrderServiceImpl implements
OrderService { @Override public void createOrder() { System.out.println("creating order"); } }
public class OrderLogger {
    
    public void beforeCreateOrder(){
        System.out.println("before create order");
    }

    public void afterCreateOrder(){
        System.out.println("after create order");
    }
}
package com.sl.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ServiceProxy implements InvocationHandler {
    private Object targetClass;
    private OrderLogger orderLogger;

    public ServiceProxy(Object targetClass,OrderLogger orderLogger) {
        this.targetClass = targetClass;
        this.orderLogger = orderLogger;
    }
    
    //獲取代理
    public Object GetDynamicProxy()
    {
        return Proxy.newProxyInstance(targetClass.getClass().getClassLoader(), //通過這個ClassLoader生成代理對象
                targetClass.getClass().getInterfaces(),//代理類已實現的接口
                this);  //動態代理調用方法是關聯的InvocationHandler,最終通過此InvocationHandler的invoke方法執行真正的方法
    }
    
    //實現相應的代理邏輯
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.orderLogger.beforeCreateOrder();
        Object result= method.invoke(targetClass, args);
        this.orderLogger.afterCreateOrder();
        
        return result;
    }

}

測試類:

package com.sl.aop;
import org.junit.Test;
public class AopTest {
    @Test
    public void Testdynamicproxy() {

        OrderServiceImpl serviceImpl = new OrderServiceImpl();
        OrderLogger logger = new OrderLogger();
        OrderService service = (OrderService) new ServiceProxy(serviceImpl, logger).GetDynamicProxy();
        service.createOrder();
    }
}

運行結果:

技術分享圖片

到這個其實還是有點困惑,Proxy.newProxyInstance()這個返回的是什麽? Invoke方法在哪裏調用的?我們看一下JDK源碼:看看DK動態代理的過程是什麽樣的:

根據源碼內部的函數調用Proxy.newProxyInstance()->Proxy.getProxyClass0()->WeakCache.get() ,先定位到

WeakCache.class:

public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular 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;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn‘t successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

可以看到函數return value; 而 V value = supplier.get(); 繼續往下讀可以發現 supper=factory,實際上是一個Factory對象,那麽繼續查看Factory.get()方法

public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;

            // wrap value with CacheValue (WeakReference)
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // try replacing us with CacheValue (this should always succeed)
            if (valuesMap.replace(subKey, this, cacheValue)) {
                // put also in reverseMap
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }

            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            return value;
        }

Return value;那麽直接查看賦值語句:value = Objects.requireNonNull(valueFactory.apply(key, parameter)); valueFactory又什麽鬼?

public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

可以知道valueFactory是ProxyClassFactory類型對象,直接查看ProxyClassFactory. Apply()方法

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                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");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf(‘.‘);
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
}

直接畫重點:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);

調用ProxyGenerator.generateProxyClass最終動態生成一個代理類,但是似乎並未找到何處調用了invoke方法;參考CSDN: https://blog.csdn.net/jiankunking/article/details/52143504這邊文章,嘗試將這個動態生成的二進制字節碼輸出到本地,並反編譯出來一看究竟,測試代碼如下:

public class AopTest {
    @Test
    public void Testdynamicproxy() {

        OrderServiceImpl serviceImpl = new OrderServiceImpl();
        OrderLogger logger = new OrderLogger();
        OrderService service = (OrderService) new ServiceProxy(serviceImpl, logger).GetDynamicProxy();
        service.createOrder();
        //輸出動態代理類字節碼
        createProxyClassFile();
    }

    private static void createProxyClassFile(){  
        String name = "ProxyObject";  
        byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{OrderService.class});  
        FileOutputStream out =null;  
        try {  
            out = new FileOutputStream(name+".class");  
            System.out.println((new File("hello")).getAbsolutePath());  
            out.write(data);  
        } catch (FileNotFoundException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }finally {  
            if(null!=out) try {  
                out.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    } 
}

使用java Decompiler工具將這個二進制class文件反編譯查看:

技術分享圖片

具體動態代理類ProxyObject.java:

import com.sl.aop.OrderService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class ProxyObject
  extends Proxy
  implements OrderService
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  public ProxyObject(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void createOrder()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("com.sl.aop.OrderService").getMethod("createOrder", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }

終於看到關於invoke的部分了:

public final void createOrder()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

實際上動態代理類繼承自Proxy,並且實現了目標類繼承的接口,在createOrder方法中調用了invoke方法,實現了切面邏輯的植入,這裏也回答了一個問題,為什麽JDK動態代理的目標類必須是實現接口的,因為代理類其實是針對接口代理,而不是針對類來代理的,動態代理類自己繼承自Proxy,Java也不允許多重繼承。動態代理類和目標類其實是各自實現了接口,代理類通過InvocationHandler.invoke實現對目標類方法的調用。

CGLIB動態代理

CGLIB代理是通過使用一個字節碼處理框架ASM,來轉換字節碼並生成新的類,並在子類中采用方法攔截的技術攔截所有父類方法的調用,實現織如如橫切邏輯 ,效率上比使用反射技術的JDK動態代理要高,但是由於CGLIB的原理是動態為目標類生成子類代理類,所以不能為聲明為final的方法進行代理。其使用主要涉及兩個類:

MethodInterceptor接口:該接口提供一個方法intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3)主要用於攔截目標類方法的調用

        Object arg0, :被代理的目標類

        Method arg1, 委托方法

        Object[] arg2, 方法參數

        MethodProxy arg3 :代理方法的MethodProxy對象

Enhancer類:用於創建代理類

示例:

實現MethodInterceptor接口

package com.sl.aop;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibServiceProxy implements MethodInterceptor {

    private Object targetClass;
    private OrderLogger orderLogger;
    
    
    public CglibServiceProxy(Object targetClass,OrderLogger orderLogger) {
        this.targetClass = targetClass;
        this.orderLogger = orderLogger;
    }
    /** 
     * 創建代理對象 
     *
     */  
    public Object getInstance() { 
        Enhancer enhancer = new Enhancer();  
        
        //設置目標類(需要被代理的類)  
        enhancer.setSuperclass(this.targetClass.getClass());
        
        // 回調方法  
        enhancer.setCallback(this);
        
        // 創建代理對象  
        return enhancer.create();  
    }  
  
    /** 
     * 攔截所有目標類方法的調用 
     *
     */
    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
        orderLogger.beforeCreateOrder();  
        
        Object o1 = arg3.invokeSuper(arg0, arg2);
        
        orderLogger.afterCreateOrder();  
        return o1;  
    }
}

測試方法:

public void Testdynamicproxy() {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
        OrderServiceImpl serviceImpl = new OrderServiceImpl();
        OrderLogger logger = new OrderLogger();
        CglibServiceProxy proxy = new CglibServiceProxy(serviceImpl,logger);  
          //通過生成子類的方式創建代理類  
        OrderServiceImpl proxyImp = (OrderServiceImpl)proxy.getInstance();
        proxyImp.createOrder();
        
    }

結果:

技術分享圖片

最後兩種代理對比一下:

JDK動態代理:代理類與委托類實現同一接口,主要是通過代理類實現InvocationHandler並重寫invoke方法來進行動態代理的,在invoke方法中將對方法進行增強處理 優點:不需要硬編碼接口,代碼復用率高,缺點:只能夠代理實現了接口的委托類

CGLIB動態代理:

代理類將委托類作為自己的父類並為其中的非final委托方法創建兩個方法,一個是與委托方法簽名相同的方法,它在方法中會通過super調用委托方法;另一個是代理類獨有的方法。在代理方法中,它會判斷是否存在實現了MethodInterceptor接口的對象,若存在則將調用intercept方法對委托方法進行代理 優點:可以在運行時對類或者是接口進行增強操作,且委托類無需實現接口,缺點:不能對final類以及final方法進行代理

Spring溫故而知新 – AOP代理