首頁 > Java > java教程 > 設計模式之動態代理

設計模式之動態代理

巴扎黑
發布: 2017-06-26 09:35:48
原創
1192 人瀏覽過

一、動態代理程式概念

動態代理程式分為JDK動態代理程式和cglib動態代理程式兩種方式。

jdk動態代理程式是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。

總的來說,反射機制在生成類別的過程中比較高效,而asm在生成類別之後的相關執行過程中比較高效(可以透過將asm生成的類別進行緩存,這樣解決asm生成類過程低效率問題)。

還有一點必須注意:jdk動態代理程式的應用前提,必須是目標類別基於統一的介面。如果沒有上述前提,jdk動態代理不能應用。

由此可以看出,jdk動態代理有一定的局限性,cglib這種第三方類別庫實現的動態代理應用更加廣泛,且在效率上更有優勢。

二、JDK動態代理

以下程式碼使用代理模式實作一個大小寫字元轉換的功能。

定義介面與實作類別:

ISomeService介面:

package com.ietree.basicskill.designpattern.dynamicproxy.jdk;/**
 * 接口类
 * 
 * @author Root */public interface ISomeService {
    
    String doFirst();    void doSecond();
}
登入後複製

SomeServiceImpl實作類別:

package com.ietree.basicskill.designpattern.dynamicproxy.jdk;/**
 * 实现类
 * 
 * @author Root */public class SomeServiceImpl implements ISomeService {

    @Overridepublic String doFirst() {
        System.out.println("执行doFirst()...");
        String result = "abcde";return result;
    }

    @Overridepublic void doSecond() {
        System.out.println("执行doSecond()...");
    }

}
登入後複製

JDK動態代理類別:

package com.ietree.basicskill.designpattern.dynamicproxy.jdk;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {final ISomeService target = new SomeServiceImpl();        // 使用JDK的Proxy动态代理,要求目标类和代理类必须实现相同的接口,因为其底层的执行原理与静态代理的相同ISomeService service = (ISomeService) Proxy.newProxyInstance(// 目标类的类加载器                target.getClass().getClassLoader(),// 目标类所实现的所有接口                target.getClass().getInterfaces(), new InvocationHandler() {// proxy:代理对象// method:目标方法// args:目标方法的参数列表                    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 调用目标方法Object result = method.invoke(target, args);if (result != null) {
                            result = ((String) result).toUpperCase();
                        }return result;
                    }
                });
        String result = service.doFirst();
        System.out.println(result);

        service.doSecond();
    }
}
登入後複製

三、cglib動態代理

Cglib是一個優秀的動態代理框架,它的底層使用ASM在記憶體中動態的生成被代理類別的子類,使用CGLIB即使代理類別沒有實作任何介面也可以實現動態代理功能。 CGLIB具有簡單易用,它的運行速度要遠遠快於JDK的Proxy動態代理:

CGLIB的核心類別:
    net.sf.cglib.proxy.Enhancer – 主要的增強類別
    net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback接口的子接口,需要用戶實現
    net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect. Method類別的代理類,可以方便的實作對來源物件方法的呼叫,如使用:
    Object o = methodProxy.invokeSuper(proxy, args);//雖然第一個參數是被代理對象,也不會出現死循環的問題。

net.sf.cglib.proxy.MethodInterceptor介面是最通用的回呼(callback)類型,它經常被基於代理的AOP用來實現攔截(intercept)方法的呼叫。這個介面只定義了一個方法
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;

第一個參數是代理對像,第二和第三個參數分別是攔截的方法和方法的參數。原來的方法可能透過使用java.lang.reflect.Method物件的一般反射調用,或使用 net.sf.cglib.proxy.MethodProxy物件調用。 net.sf.cglib.proxy.MethodProxy通常被首選使用,因為它更快。

以下程式實作了大小寫轉換的功能:

實作類別SomeService:

package com.ietree.basicskill.designpattern.dynamicproxy.cglib;/**
 * 实现类
 * 
 * @author Root */public class SomeService {public String doFirst() {
        System.out.println("执行doFirst()...");
        String result = "abcde";return result;
    }public void doSecond() {
        System.out.println("执行doSecond()...");
    }

}
登入後複製

代理程式類別MyCglibFactory:

#
package com.ietree.basicskill.designpattern.dynamicproxy.cglib;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class MyCglibFactory implements MethodInterceptor {private SomeService target;    public MyCglibFactory() {super();
        target = new SomeService();
    }public SomeService myCglibCreator() {// 创建增强器对象Enhancer enhancer = new Enhancer();// 指定目标类,即父类enhancer.setSuperclass(SomeService.class);// 设置回调接口对象enhancer.setCallback(this);return (SomeService) enhancer.create();
    }

    @Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 调用目标方法Object result = method.invoke(target, args);if (result != null) {
            result = ((String) result).toUpperCase();
        }return result;
    }

}
登入後複製

測試:

package com.ietree.basicskill.designpattern.dynamicproxy.cglib;public class Main {public static void main(String[] args) {

        SomeService service = new MyCglibFactory().myCglibCreator();
        
        String result = service.doFirst();
        System.out.println("result = " + result);

        service.doSecond();
    }
}
登入後複製

運行結果:

执行doFirst()...
result = ABCDE
执行doSecond()...
登入後複製

 

以上是設計模式之動態代理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板