淺談 Spring的AOP的實現 -- 動態代理

提及Spring的AOP(Aspect-Oriented Programming)面向切面編程你們都很熟悉(Spring不是此次博文的重點),可是我先提出幾個問題,看看同窗們是否瞭解,若是瞭解的話能夠不用繼續往下讀:html

  1. Spring的AOP的實現方式有哪些?java

  2. 爲何使用動態代理?編程

  3. 它們是怎麼實現的?數組

  4. 它們的區別是什麼?ide

  下面進入正題,Spring採用動態代理的方式實現AOP,具體採用了JDK的動態代理和CGLib的動態代理。使用動態代理的目的是在現有類的基礎上增長一些功能。簡單地將就是有一個Proxy類,實現了原始類的方法,而且在原始類的基礎上增長了新的功能。那麼這麼作能夠實現不少功能:this

  1. 在方法先後進行日誌處理。spa

  2. 進行額外的校驗,好比參數的驗證功能等。代理

  3. 實現一些懶加載,也就是實例化的時候若是不去調用真正的方法的時候,這個類的屬性就不會存在(Hibernate有這樣相似的功能)。日誌

  下面我們用簡單的代碼實現它是如何進行代理的,首先採用的是JDK的動態代理實現:code

  定義一個接口:

按 Ctrl+C 複製代碼
按 Ctrl+C 複製代碼

  定義一個實現類:

複製代碼
package com.hqs.proxy;

/**
 * Mac 的實現
 * @author hqs
 *
 */
public class Mac implements OpSystem {

    public void work() {
        System.out.println("Mac is running");    
    }


}
複製代碼

  關鍵位置來了,咱們經過實現JDK自帶的反射機制的包的InvocationHandler來進行反射處理,實現它以後須要實現裏邊的invoke方法,這個invoke方法裏邊的參數分別爲:代理類實例,用於調用method的;method參數是實際執行的方法;args所傳輸的參數數組。

複製代碼
package com.hqs.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class OpHandler implements InvocationHandler {
    
    private final OpSystem ops; 
    
    public OpHandler(OpSystem ops) {
        this.ops = ops;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before system running");
        method.invoke(ops, args);
        System.out.println("After system running");
        return null;
    }
    
    public static void main(String[] args) {
        Mac mac = new Mac();
        OpHandler oph = new OpHandler(mac);
        OpSystem os = (OpSystem)Proxy.newProxyInstance(oph.getClass().getClassLoader(),
                mac.getClass().getInterfaces(), oph);
        
        os.work();
        System.out.println(os.getClass());
    }
    

}


輸出:
Before system running
Mac is running
After system running
class com.sun.proxy.$Proxy0
複製代碼

   而後看到裏邊的main方法中,代理類實例化對象的方法Proxy.newProxyInstance,這個是JDK的反射方法去實例化代理類,其中有三個分別是,去實例化代理類的class loader;所代理的類的全部接口Class數組;hander處理類,用於作攔截使用的類。最後我輸出了一下os.getClass(),你們能夠看到的是代理類的實例,而不是真正代理類的實例,這麼作的好處就是很方便的複用這個代理類,好比你能夠重複調用它而不用去從新實例化新類,再一點就是你能夠針對不一樣的方法進行攔截,好比你能夠method.getName()去判斷調用的方法名字是什麼從而更細粒度的攔截方法。我們繼續看用CGLib的實現:  

複製代碼
package com.hqs.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 Interceptor用於方法攔截
 * @author hqs
 *
 */
public class CGLibInterceptor implements MethodInterceptor {
    
    private final Mac mac;
    
    public CGLibInterceptor(Mac mac) {
        this.mac = mac;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, 
            MethodProxy methodProxy) throws Throwable {
        System.out.println("Before system running");
        method.invoke(mac, args);
        System.out.println("After system running");
        return null;
    }
    
    public static void main(String[] args) {
        Mac mac = new Mac(); //實例而非接口
        MethodInterceptor handler = new CGLibInterceptor(mac);
        Mac m = (Mac)Enhancer.create(mac.getClass(), handler);
        m.work();
        System.out.println(m.getClass());
        
    }

}


輸出:
Before system running
Mac is running
After system running
class com.hqs.proxy.Mac$$EnhancerByCGLIB$$1f2c9d4a
複製代碼

  首先須要引入cglib包,而後才能使用他的MethodInterptor,它也採用method.invoke實現對代理類的調用。它的代理類建立採用Enhancer的create方法,其中傳入了須要建立的類的class,以及Callback對象,由於MethodInterceptor繼承了Callback對象。用於指向方法先後進行調用的類。  

public interface MethodInterceptor
extends Callback

  這是這兩個類的基本實現,那麼它們的區別是什麼呢?

  1. JDK的動態代理只能針對接口和其實現類,若是沒有實現類只有接口也是能夠代理的,這裏就不在舉例了。爲何JDK的動態代理只針對接口代理,由於這個是JDK的定義。
  2. 若是不針對接口實現動態代理那就用到了CGLib了,也就是能夠針對具體的類進行代理,你們能夠參考個人代碼。

  這些是它們的根本區別,可是Spring推薦使用JDK的動態代理,面向接口去編程。使用CGLib去作動態代理的時候須要注意,它生產的代理類存放在JVM的Perm space裏邊,那麼是否是生成的代理對象就不進行回收了?其實不是的,不常常回收可是仍是回收的,當類被加載,加載類的classLoader何時變得對垃圾回收可用的時候才進行回收。也就是你本身建立全部類移除classLoader以後,那麼這個classLoader就會被回收,通常很是精通CGLib的話能夠進行這塊內容深刻開發,由於它能夠作出amzing的事情若是你熟悉的話。

 

轉載: 黃山清水岸邊石   http://www.cnblogs.com/huangqingshi/p/7651376.html

相關文章
相關標籤/搜索