java 靜態代理、JDK代理、Cglib代理

1、代理模式
    代理對象代表目標對象被訪問,代理類可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。
      

2、常見的三種代理實現
  • 靜態代理
  • 動態代理
    • java JDK代理
    • Cglib代理

3、靜態代理
  • 代理類和被代理類實現相同的接口或繼承相同的父類,代理對象通過他們的共同的接口或父類訪問被代理類。
  • 實現:下面是問手機(YourPhone)的價格,但實際上是問華爲手機的價格。
//公共接口:手機
    publicinterface Phone{
         publicvoid price();
    }
    
    //被代理類:華爲
    classHuaWei implements Phone{
         @Override
         publicvoid price() {
             System.out.println("1000RMB");
         }
    }
    
    //代理類
    classYourPhone implements Phone{
         HuaWeiphone;    //必須要有被代理類的對象
         
         publicYourPhone(HuaWei phone){
             this.phone=phone;
         }
         
         @Override
         publicvoid price() {
             phone.price(); //實際是訪問被代理類的價格
         }
    }
    
    //測試代理
    publicvoid testProxy(){
         Phonep=newYourPhone(newHuaWei());   //代理對象
         p.price(); //訪問代理類的價格
    }


  • 優點:可以看到:在YourPhone類的price函數里加上其他功能,如:敬語。即添加額外功能。
  • 缺點:每一個類都應要有一個代理類,即類數量增多了。接口一旦要更改,其實現類也需要更改。

4、java JDK動態代理
  • 特點:代理類不需要實現共同接口或繼承共同父類,直接通過JDK的API生成代理對象。
  • 要動態創建代理對象需要用到InvocationHandler這個接口
    //公共接口:手機
    publicinterface Phone{
         publicvoid price();
    }
    
    //被代理類:華爲
    classHuaWei implements Phone{
         @Override
         publicvoid price() {
             System.out.println("1000RMB");
         }
    }
    
    /*編寫一個動態代理,不需實現接口,但需要制定接口。
     * 值得注意的是,這可以代理不同的被代理類了*/
    classProxyFactory{
         Objectphone;    //被代理類對象,不應指定某個代理類,讓其動態綁定
         
         publicProxyFactory(Object phone) {
             this.phone=phone;
         }
         
         //h獲取代理對象
         publicObject getProxyInstance() {
             //創建被代理類的事務處理器
             InvocationHandlerih=newInvocationHandler() {
                 @Override
                 publicObject invoke(Object proxy, Methodmethod, Object[]args)throws Throwable {
                     returnmethod.invoke(phone,args);
                 }
             };
             /*
              * phone.getClass().getClassLoader():被代理類的類加載器
              * phone.getClass().getInterfaces():被代理類的接口
              */
             returnProxy.newProxyInstance(phone.getClass().getClassLoader(),phone.getClass().getInterfaces(),ih);
         }
    }
    
    //測試動態代理
    publicvoid testProxy(){
         Phonep=newHuaWei();//指定具體代理類
         PhoneDProxy=(Phone)newProxyFactory(p).getProxyInstance();        //創建動態代理對象
         DProxy.price();  
    }



5、動態代理與靜態代理的區別
  • 上面說過,動態代理不用創建每一個代理類。即對於2個被代理類,靜態代理需要創建2個代理類,而動態代理只需創建另一個動態代理對象即可。
  • 下面增加apple手機的類,高亮的爲新增代碼
    • 靜態代理類:必須創建代理類Apple
         //公共接口:手機
         public interface Phone{
             public void price();
         }
         
         //被代理類:華爲
         class HuaWei implements Phone{
             @Override
             public void price() {
                 System.out.println("1000RMB");
             }
         }
         
         //被代理類:蘋果
         class Apple implements Phone{
             @Override
             public void price() {
                 System.out.println("5000RMB");
             }
         }
         
         //代理類HuaWei
         class YourPhone implements Phone{
             HuaWei phone;    //必須要有被代理類的對象
             
             public YourPhone(HuaWei phone){
                 this.phone=phone;
             }
             
             @Override
             public void price() {
                 phone.price();  //實際是訪問被代理類的價格
             }
         }
         
         //代理類apple
         class YourPhone2 implements Phone{
             Apple phone//必須要有被代理類的對象
             
             public YourPhone2(Apple phone){
                 this.phone=phone;
             }
             
             @Override
             public void price() {
                 phone.price();  //實際是訪問被代理類的價格
             }
         }
         
         //測試靜態代理
         public void testProxy2(){
             Phone p=new YourPhone(new HuaWei());    //代理對象
             Phone p2=new YourPhone2(new Apple());
             p.price();  //訪問代理類的價格
             p2.price();
         }

  • 動態代理類:只需創建另一個代理對象
        //公共接口:手機
         public interface Phone{
             public void price();
         }
         
         //被代理類:華爲
         class HuaWei implements Phone{
             @Override
             public void price() {
                 System.out.println("1000RMB");
             }
         }
         
         //被代理類:蘋果
         class Apple implements Phone{
             @Override
             public void price() {
                 System.out.println("5000RMB");
             }
         }
         
         /*編寫一個動態代理,不需實現接口,但需要制定接口。
          * 值得注意的是,這可以代理不同的被代理類了*/
         class ProxyFactory{
             Object phone;    //被代理類對象,不應指定某個代理類,讓其動態綁定
             
             public ProxyFactory(Object phone) {
                 this.phone=phone;
             }
             
             //h獲取代理對象
             public Object getProxyInstance() {
                 //創建被代理類的事務處理器
                 InvocationHandler ih=new InvocationHandler() {
                     @Override
                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          return method.invoke(phone, args);
                     }
                 };
                 /*
                  * phone.getClass().getClassLoader():被代理類的類加載器
                  * phone.getClass().getInterfaces():被代理類的接口
                  */
                 return Proxy.newProxyInstance(phone.getClass().getClassLoader(),phone.getClass().getInterfaces(),ih);
             }
         }
         
         //測試動態代理
         public void testProxy(){
             Phone p=new HuaWei();//指定具體被代理類
             Phone DProxy=(Phone)new ProxyFactory(p).getProxyInstance();        //創建動態代理對象
             DProxy.price();  
             Phone p2=new Apple();
             Phone DProxy2=(Phone)new ProxyFactory(p2).getProxyInstance();
             DProxy2.price();
         }



6、Cglib代理
  • Cglib代理也是動態代理,其與JDK代理不同的地方在於:JDK的被代理類需實現接口,而Cglib代理的被代理類不需實現接口。Cglib以被代理類的子類對象作爲其代理類。
  • 需下載底層包:Cglib-nodep 或其核心包Cglib-core
    • 將下載的jar包導入項目:右鍵項目——Properties——Java Build Path,選Libraries選卡項,點擊Add External JARs...。選擇包導入後確認即可
  • Cglib包的底層是通過使用一個字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
  • 被代理的類不能爲final,否則報錯
  • 被代理對象的方法如果爲final/static,那麼就不會被攔截,而是直接執行該方法.
    //被代理類(需要注意的是,該類不能是內部類)
    publicclass HuaWei {
        publicvoid price() {
             System.out.println(this+"1000RMB");
        }
    }
    //另起一類
    publicclass Cglib {
         
    //Cglib代理
    classProxyFactory2 implements MethodInterceptor{
         Objectphone;
         
         publicProxyFactory2(Object phone) {
             this.phone=phone;
         }
         
         publicObject getProxyInstance() {
             Enhancerenhance=newEnhancer();  //工具類
             enhance.setSuperclass(phone.getClass());   //設置父類:被代理類
             enhance.setCallback(this);    //設置回調函數,這裏的this是函數所在的類
             //返回創建的子類:代理對象
             returnenhance.create();
         }
         
         /**
          * 執行被代理類的方法時攔截,並執行該方法
          *@param
          * obj:被代理類的對象
          * method:被代理類的方法反射對象
          * args:方法參數
          * proxy:代理類對象
          */
         @Override
         publicObject intercept(Object obj, Methodmethod, Object[]args, MethodProxyproxy)throws Throwable {
             returnmethod.invoke(phone,args);
             //寫法2
//           proxy.invokeSuper(obj,args);
//           return null;

         }
    }
    
    //測試Cglib代理
    publicvoid testProxy3() {
         HuaWeihw=newHuaWei();
         HuaWeiproxy=(HuaWei)newProxyFactory2(hw).getProxyInstance();
         proxy.price();
    }
         
    publicstatic void main(String[] args) {
         //TODO Auto-generated method stub
         Cglibc=newCglib();
         c.testProxy3();
    }
}


  • 程序分析:從testProxy3()函數開始,生成HuaWei類對象,這裏我踩了一下坑:先前把這個類設置爲內部類(原因不知道是什麼,估計要看源碼,望告知)。然後生成ProxyFactory2對象,主要是爲了執行get類的getProxyInstance()方法,從而得到被代理類的子類:代理對象
  • 該代理對象執行被代理類的方法時,都會被intercept 方法攔截(爲了得到被代理對象)。



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