java動態代理實現

jdk代理html

參考博文:jdk動態代理實現原理 以前雖然會用jdk代理,但一直不知道是怎麼回事,好比說:InvocationHandler的invoke方法是由誰來調用的,代理對象是怎麼生成的。 先來看看jdk的動態代理是怎麼用的吧java

實現本身的InvocationHandler codeide

package com.geccocrawler.gecco.spider.render.html;

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

/**
 * 實現本身的InvocationHandler
 *
 * @author zyb
 * @since 2012-8-9
 */
public class MyInvocationHandler implements InvocationHandler {

    // 目標對象   
    private Object target;

    /**
     * 構造方法
     *
     * @param target 目標對象
     */
    public MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }


    /**
     * 執行目標對象的方法
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 在目標對象的方法執行以前簡單的打印一下  
        System.out.println("------------------before------------------");

        // 執行目標對象的方法  
        Object result = method.invoke(target, args);

        // 在目標對象的方法執行以後簡單的打印一下  
        System.out.println("-------------------after------------------");

        return result;
    }

    /**
     * 獲取目標對象的代理對象
     *
     * @return 代理對象
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}

目標對象實現的接口UserService code性能

package com.geccocrawler.gecco.spider.render.html;

import java.io.IOException;

/**
 * 目標對象實現的接口,用JDK來生成代理對象必定要實現一個接口
 *
 * @author zyb
 * @since 2012-8-9
 */
public interface UserService {

    /**
     * 目標方法
     */
    public abstract void add();

}

目標對象UserServiceImpl code測試

package com.geccocrawler.gecco.spider.render.html;

import java.io.IOException;

/**
 * 目標對象
 *
 * @author zyb
 * @since 2012-8-9
 */
public class UserServiceImpl implements UserService {
  
    /* (non-Javadoc) 
     * @see dynamic.proxy.UserService#add() 
     */

    public void add() {
        System.out.println("--------------------add---------------");
    }
}

動態代理測試類ProxyTest codethis

package com.geccocrawler.gecco.spider.render.html;

/**
 * 動態代理測試類
 *
 * @author zyb
 * @since 2012-8-9
 */
public class ProxyTest {

    public static void main(String[] args) throws Throwable {
        // 實例化目標對象  
        UserService userService = new UserServiceImpl();

        // 實例化InvocationHandler
        MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);

        // 根據目標對象生成代理對象  
        UserService proxy = (UserService) invocationHandler.getProxy();

        // 調用代理對象的方法  
        proxy.add();

    }
}

執行結果以下: ------------------before------------------ --------------------add--------------- -------------------after------------------代理

用起來是很簡單吧,其實這裏基本上就是AOP的一個簡單實現了,在目標對象的方法執行以前和執行以後進行了加強。Spring的AOP實現其實也是用了Proxy和InvocationHandler這兩個東西的。code

用起來是比較簡單,可是若是能知道它背後作了些什麼手腳,那就更好不過了。首先來看一下JDK是怎樣生成代理對象的。既然生成代理對象是用的Proxy類的靜態方newProxyInstance,那麼咱們就去它的源碼裏看一下它到底都作了些什麼? 在內存中生成一個字節碼文件,而後使用classload加載,反射實例化,調用MyInvocationHandler中的invoke方法實現代理htm

cglib代理對象

參考博文:cglib動態代理實現 JDK實現動態代理須要實現類經過接口定義業務方法,對於沒有接口的類,如何實現動態代理呢,這就須要CGLib了。CGLib採用了很是底層的字節碼技術,其原理是經過字節碼技術爲一個類建立子類,並在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢織入橫切邏輯。JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎 簡單的實現舉例: 這是一個須要被代理的類,也就是父類,經過字節碼技術建立這個類的子類,實現動態代理。

public class SayHello {
    public void say() {
        System.out.println("hello everyone");
    }
}

該類實現了建立子類的方法與代理的方法。getProxy(SuperClass.class)方法經過入參即父類的字節碼,經過擴展父類的class來建立代理對象。intercept()方法攔截全部目標類方法的調用,obj表示目標類的實例,method爲目標類方法的反射對象,args爲方法的動態入參,proxy爲代理類實例。proxy.invokeSuper(obj, args)經過代理類調用父類中的方法。

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {
        //設置須要建立子類的類
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        //經過字節碼技術動態建立子類實例
        return enhancer.create();
    }

    //實現MethodInterceptor接口方法
    public Object intercept(Object obj, Method method, Object[] args,
                            MethodProxy proxy) throws Throwable {
        System.out.println("前置代理");
        //經過代理類調用父類中的方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("後置代理");
        return result;
    }
}

具體實現類:

public class DoCGLib {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        //經過生成子類的方式建立代理類
        SayHello proxyImp = (SayHello) proxy.getProxy(SayHello.class);
        proxyImp.say();
    }
}

輸出結果: 前置代理 hello everyone 後置代理

CGLib建立的動態代理對象性能比JDK建立的動態代理對象的性能高很多,可是CGLib在建立代理對象時所花費的時間卻比JDK多得多,因此對於單例的對象,由於無需頻繁建立對象,用CGLib合適,反之,使用JDK方式要更爲合適一些。同時,因爲CGLib因爲是採用動態建立子類的方法,對於final方法,沒法進行代理

補充—————————————————— 代理是爲一個類設置代理,不是針對方法

jdk的代理須要接口是由於內存中動態生成的字節碼須要實現那個接口,以便於強轉類型不出現報錯,即超類一致

cglib不須要是由於他是在須要代理的類的基礎上生成的子類,也作到了超類一致,強轉不會報錯

相關文章
相關標籤/搜索