java設計模式:代理模式(二)動態代理

java的代理模式有兩種,一種是jdk自帶的動態代理,一種是cglib動態代理。html

一、jdk動態代理:java

/** * 1.建立接口 */
public interface Subject { int sellBook(); String speak(); } /** * 2.建立真實對象 */
public class RealSubject implements Subject { @Override public int sellBook() { System.out.println("賣書"); return 1; } @Override public String speak() { System.out.println("說話"); return "張三"; } } /** * 3.建立真實對象的代理類,代理類要實現InvocationHandler接口,表示使用jdk動態代理 * 代理模式的重要做用是用於在被代理類的方法執行先後切入代碼 */
public class JdkProty implements InvocationHandler { /** * 由於須要處理真實對象,因此須要將真實對象傳過來,經過構造方法賦值 */
    private Subject subject; public JdkProty(Subject subject) { this.subject = subject; } /** * @param proxy 被代理的對象 * @param method 正在調用的方法 * @param args 方法的參數 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("調用代理類"); if (method.getName().equals("sellBook")) { int invoke = (int) method.invoke(subject, args); System.out.println("調用了賣書的方法"); return invoke; } else if (method.getName().equals("speak")) { String string = (String) method.invoke(subject, args); System.out.println("調用的是說話的方法"); return string; } return null; } }
/**
*測試方法
*/
public class TestDemo { public static void main(String[] args) { Subject subject = new RealSubject(); JdkProty jdkProty = new JdkProty(subject); /** * 代理對象(本方法能夠封裝入代理類中,被代理類對象做爲obj參數傳遞) *subject.getClass().getClassLoader():被代理類對象的加載器 * subject.getClass().getInterfaces():獲得被代理類對象的全部接口 * jdkProty:與被代理類對象相關的代理類對象 */ Subject proxyInstance = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), jdkProty); proxyInstance.sellBook(); proxyInstance.speak(); } }

 使用jdk動態代理的前提條件:被代理的類必定要有實現的接口node

/**
* java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口用來完成動態代理。
* InvocationHandler接口:
* 當咱們經過代理對象調用一個方法時,這個方法的調用就會被轉發爲由InvocationHandler這個接口的invoke方法進行調用
* Proxy類
* 該類提供了一個建立動態代理對象的方法
*/

 二、cglib動態代理ide

Cglib 動態代理是針對代理的類, 動態生成一個子類, 而後子類覆蓋被代理類中的方法, 若是是private或是final類修飾的方法,則不會被重寫。性能

CGLIB是一個功能強大,高性能的代碼生成包。它爲沒有實現接口的類提供代理,爲JDK的動態代理提供了很好的補充。一般可使用Java的動態代理建立代理,但當要代理的類沒有實現接口或者爲了更好的性能,CGLIB是一個好的選擇。測試

使用cglib動態代理須要導包:cglib-nodep-2.2.2.jarthis

/** * 1.建立真實類對象 */
public class Ennigeer { //能夠被代理的方法
    public int eat() { System.out.println("工程師在吃飯"); return 1; } //final修飾的方法不會被cglib生成的子類覆蓋,能夠經過代理類調用,可是不能執行代理類的方法
    public final String work() { System.out.println("工程師在工做"); return "hello"; } //private修飾的方法不會被生成的子類覆蓋,不能經過代理類調用
    private void play() { System.out.println("工程師在玩"); } } public class CglibProxy implements MethodInterceptor { /** * 該intercept方法相似於jdk的invoke方法發 * @param o 被代理類對象 * @param method 被代理類的方法對象 * @param objects 傳進去的參數 * @param methodProxy 代理方法的代理對象 * @return * @throws Throwable */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("方法調用以前"); Object invoke = methodProxy.invokeSuper(o, objects); System.out.println("方法調用以後"); return invoke; } /** * 獲得代理類 * @param obj 被代理類對象 * @return 代理類對象 */
    public Object getProxy(Object obj) { //建立增強器,用來建立被代理類的對象
        Enhancer enhancer = new Enhancer(); //爲增強器指定要代理的業務類(即:爲下面生成的代理類指定父類)
 enhancer.setSuperclass(obj.getClass()); //設置回調,對代理類上全部方法的調用,都會調用CallBack(),而CallBack()方法則須要intercept方法進行攔截
        enhancer.setCallback(this); //建立被代理類對象的特殊格式的對象
        return enhancer.create(); } } public class TestDemo { public static void main(String[] args) { Ennigeer ennigeer = new Ennigeer(); CglibProxy cglibProxy = new CglibProxy(); Ennigeer proxy = (Ennigeer) cglibProxy.getProxy(ennigeer); int eat = proxy.eat();//能夠被代理的方法
        String work = proxy.work();//不能被代理的方法,能夠調用該方法,可是不能代理
 System.out.println(eat); System.out.println(work); } }
/**
* net.sf.cglib.proxy.MethodInterceptor接口:是最通用的回調(callback)類型,
* 它常常被AOP用來實現攔截(intercept)方法的調用。這個接口只定義了一個方法。
* public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) 
* proxy:代理的對象;
* method:委託類執行的方法;
* params:方法中的參數;
* methodProxy:代理的方法
* 因爲性能的緣由,對原始方法的調用咱們使用CGLIB的net.sf.cglib.proxy.MethodProxy對象,而不是反射中通常使用java.lang.reflect.Method對象
*/


兩種代理方式的比較:url

  CGLib動態代理建立代理實例速度慢,可是運行速度快;JDK動態代理建立實例速度快,可是運行速度慢。若是實例是單例的,推薦使用CGLib方式動態代理,反之則使用JDK方式進行動態代理。Spring的實例默認是單例,因此這時候使用CGLib性能高。 spa

  JDK動態代理是經過接口中的方法名,在動態生成的代理類中調用業務實現類的同名方法;.net

  CGlib動態代理是經過繼承業務類,生成的動態代理類是業務類的子類,經過重寫業務方法進行代理;

   靜態代理是經過在代碼中顯式定義一個業務實現類一個代理,在代理類中對同名的業務方法進行包裝,用戶經過代理類調用被包裝過的業務方法;

相關文章
相關標籤/搜索