spring代理模式

代理模式優勢及意義:1.安全上,能夠屏蔽客戶端訪問真是對象。2.可以在目標對象實現的基礎上,增強額外的功能操作,即功能擴展(相當於明星和經紀人)。3.系統性能上,可以對真是對象進行延時加載,從而達到按需分配的設計思想。(@lazy對真正需要加載數據時才加載。這樣節省資源分配;ioc管理bean對象時,都是通過代理模式來管理對象)

 

定義:給指定的目標對象提供了一種通過代理對象訪問的方式。

代理模式是一個結構化的設計模式。

 

結構圖:

 

      靜態代理實例:

1.

package propxy;

//接口

public interface ByCar {

  void buyCar();

}

2.

package propxy;

//目標對象

public class Person implements ByCar{

    @Override

    @ask(askTr="i have 15000$")

    public void buyCar() {

        System.out.println("i want by care;");

    }

}

3.

package propxy;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

//對目標對象增強   採用註解方式

public @interface ask {

  String askTr();

}

 

4.

package propxy;

//代理對象

public class ProxyBy implements ByCar {

  // 目標對象的引用

  private ByCar bc;

  public ProxyBy(ByCar bc) {

      super();

      this.bc = bc;

  }

  @Override

  public void buyCar() {

      // 對目標對象增強

      String askTr = null;

      try {

  askTr=bc.getClass().getMethod("buyCar")

.getAnnotatio(ask.class).askTr();

      } catch (Exception e) {

          e.printStackTrace();

      }

      System.out.println(askTr);

      bc.buyCar();

  }

}

 

5.

package propxy;

public class test {

  public static void main(String[] args) {

      ByCar byCaryCar=new ProxyBy(new Person());

      byCaryCar.buyCar();

  }

}

 

靜態代理常用環境:項目中的三層架構就是靜態代理;controller層調用service層;service層調用dao層;就是典型的靜態代理;

 

  1. JDK動態代理  2.cglib代理
    1、如果目標對象實現了接口,默認情況下會採用JDK的動態代理實現AOP
    2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP
    3、如果目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

 

動態代理:通過字節碼重組的方式動態的創建proxy類實例和proxy實例;生成的class文件就是所謂的字節碼;如xx.getclass();

 

Jdk動態代理:

  1. 聲明subject接口,且realsubject必須實現該接口
  2. 必須實現invocationhandler接口,且覆蓋invoke方法
  3. 可以通過jdk提供的proxy.newproxyinstance靜態來構造代理對象

實例:

package propxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

//實現invocationhandler 重寫invoke()方法

public class DynamicProxy implements InvocationHandler{

  private ByCar bc;

  @SuppressWarnings("unchecked")

  public <T> T getProxy(ByCar bc) {

      this.bc=bc;

       //通過jdk自帶靜態的方法 proxy.newproxyinstance() 來生成代理對象

      //通過字節碼重組的方式動態的生成一個class字節碼,在通過class字節碼反射的方式  創建一個對象

      return (T) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), bc.getClass().getInterfaces(), this);

  }

  @Override

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//目標對象增強

      String askTr = bc.getClass().getMethod("buyCar").getAnnotation(ask.class).askTr();

      System.out.println(askTr);

//反射機制

      return method.invoke(bc, args);

  }

}

 

調用:

public static void main(String[] args) {

        ByCar byCar =new DynamicProxy().getProxy(new Person());

        byCar.buyCar();

}

 

Cglib動態代理:通過jvm(java虛擬機)中,通過動態的組裝當前類的一個子類來實現的動態代理;

需引入cglib的依賴插件;

 

三大步驟:

 

  1. 引入cglib插件;
  2. 實現methodinterceptor  用於攔截  重寫intercept方法
  3. 通過Enhancer enhancer=new Enhancer();  enhancer.create() 創建代理對象
  4. 實例:

1.

package propxy;

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 DynamicCglib implements MethodInterceptor{

@SuppressWarnings("unchecked")

public <T> T GetInstances(Class<?> clazz) {

  //通過傳入的字節碼文件(clazz) 從新生成一個字節碼文件繼承於它 

  Enhancer enhancer=new Enhancer();

  enhancer.setSuperclass(clazz);

  enhancer.setCallback(this);

  return (T) enhancer.create();

}

  @Override

  public Object intercept(Object obj, Method method, Object[] objs, MethodProxy methodProxy) throws Throwable {

      System.out.println("方法前--------------操作");

      Object invokeSuper = methodProxy.invokeSuper(obj, objs);

      System.out.println("方法後--------------操作");     

return invokeSuper;

  }

}

2.

package propxy;

public class User {

    public void eat() {

        System.out.println("user:eat............");

    }

}

       調用:

package propxy;

public class test {

    public static void main(String[] args) {

        //靜態

        ByCar byCaryCar=new ProxyBy(new Person());

        byCaryCar.buyCar();

        //動態jdk

        ByCar byCar =new DynamicProxy().getProxy(new Person());

        byCar.buyCar();

        //動態cglib

        User obj = new DynamicCglib().GetInstances(User.class);

        obj.eat(); 

    }

}

Jdk動態代理與cglib的區別:1.如果bean實例沒有實現某一接口 就採用cglib代理模式;如果bean實例實現了某一接口,spring就會採用jdk動態代理模式;

                           2.cglib採用的是繼承於原來的一個類;調用目標對象的方法,不是採用的反射機制,而是採用的緩存fast機制;而jdk代理是採用實現接口的方式,調用目標對象的方法是採用的是反射機制

 

Jdk動態代理與cglib的優劣:1.cglib的動態代理不需要目標對象實現任何接口,但是如果目標對象裏的方法是final時,就不能實現動態代理;jdk動態代理需要目標對象繼承某一接口,沒有方法final的限制;

2.cglib動態代理採用的緩存fast機制,它爲每個方法體編了一個記號,像用索引的方式去快速調用,而jdk採用的反射機制的調用該方法體;所以cglib動態代理在jvm編譯生成class文件的過程中較於jdk動態代理要慢一些;但是調用方法體要快一些;jdk採用反射機制的去調用方法體 是比較耗費資源的;

 

在項目中:service接口對象往往採用的是cglib的動態代理  而mybatis裏的mapper接口對象是採用的jdk動態代理的方式;

 

Spring的aop採用面向切面編程,代理模式在其中表現的淋淋盡致,比如我們放在service層方法上的註解如@transition 我們都知道是事物註解;那spring是怎麼來處理它的呢? 在我們代理模式中爲目標對象增強其功能,我們是不是會定義一個註解,放在目標對象方法上;在我們通過代理模式調用目標對象方法時通過方法上定義的註解 我們可以在方法前後做一些操作 就是對目標對象方法的一些增強;

比如  @transition,我們都是放在service層的方法上,而service層採用的是cglib動態代理的方式。我們在調用目標對象的方法時通過攔截器transitioninterceptor捕獲到方法上有@transition的註解(transitioninterceptor肯定實現了methodinterceptor方法攔截器),那麼,spring就會在目標方法前做事物開啓操作,然後動態代理的方式調用目標對象方法(方法裏肯定就是些mapper裏相關的事物操作啦  我這裏用的是mybatis框架 注意:mapper採用的是jdk的動態代理方式),方法後

如果有異常就做事物回滾操作;這些都是通過代理模式來實現的;

service層調用採用的cglib代理方式圖解:代理名字都含有cglib  通過上面實例 debug模式可驗證

mybatis裏mapper代理模式圖解:代理名字都是$proxy開頭  通過上面實例 debug模式可驗證

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章