代理與動態代理

概述

代理

生活中的代理:好比暴雪又新出了一款遊戲,做爲一箇中國用戶,沒有很好的方式直接從美國拿到遊戲產品,就須要等到中國的某個代理商被暴雪受權代理,才能夠玩上暴雪新出的這款遊戲,那麼代理的做用也就顯而易見了,就是爲了方便的適應一些不一樣地區的用戶而產生的中間媒介,經過代理,能夠更有效的將一些產品發行出去,生活中這樣代理的例子比比皆是html

程序中的代理:若是一些實現了同一接口的類在實現方法的時候,有了新的需求,須要增長一些異常處理、日誌、計算方法的運行時間、事務管理等操做,可是咱們又不能去修改源代碼,這樣嚴重影響了程序的擴展性,這個時候,咱們就可使用代理,代理與目標類實現同一個接口,表明着代理類與目標類具備同名的方法,調用者不須要直接調用目標對象,而是使用代理間接調用目標對象的方法,並在這個過程當中加入本身的處理代碼,這就是程序中的代理java

代理實現圖示

proxy

代理能夠處理的問題

系統中存在交叉業務,一個交叉業務就是要切入到系統中的一個方面,以下所示: 編程

proxy2

用具體的程序代碼描述交叉業務:api

proxy3

 

交叉業務的編程問題即爲面向方面的編程(Aspect oriented program ,簡稱AOP),AOP的目標就是要使交叉業務模塊化。能夠採用將切面代碼移動到原始方法的周圍,這與直接在方法中編寫切面代碼的運行效果是同樣的,以下所示: 數組

proxy4

使用代理技術正好能夠解決這種問題,代理是實現AOP功能的核心和關鍵技術。 app

代理類的各個方法中一般除了要調用目標的相應方法和對外返回目標返回的結果外,還能夠在代理方法中的以下四個位置加上系統功能代碼: 框架

  1. 在調用目標方法以前
  2. 在調用目標方法以後
  3. 在調用目標方法先後
  4. 在處理目標方法異常的catch塊中

動態代理

瞭解了代理以後,咱們知道代理類要實現與目標類相同的接口,那麼現存的已知接口與其子類那麼多,咱們不可能一個個的手動實現他們的代理類,這個時候,java.lang.reflect中的Proxy類爲咱們提供了能夠動態生成代理類的方法,也就是所謂的動態代理技術 ide

動態代理就是不須要手動,而是經過JVM自動生成一份字節碼,這份字節碼實現了須要被代理的類的父類接口,這樣就使得建立代理類變成了一件模塊化的事情 模塊化

利用動態代理實現一個簡單代理過程

public class Proxy
extends Object
implements Serializable
 
 

Proxy 提供用於建立動態代理類和實例的靜態方法,它仍是由這些方法建立的全部動態代理類的超類。 測試

static InvocationHandler
getInvocationHandler(Object proxy)
          返回指定代理實例的調用處理程序。

static Class<?>
getProxyClass(ClassLoader loader, Class<?>... interfaces)
          返回代理類的 java.lang.Class 對象,並向其提供類加載器和接口數組。

static boolean
isProxyClass(Class<?> cl)
          當且僅當指定的類經過 getProxyClass 方法或 newProxyInstance 方法動態生成爲代理類時,返回 true。

static Object
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
          返回一個指定接口的代理類實例,該接口能夠將方法調用指派到指定的調用處理程序。

以上是Proxy代理類的一些方法,其中,getProxyClass能夠得到生成好的代理類字節碼,而newProxyInstance能夠直接得到一個動態代理類的對象

這裏咱們考慮,生成一個代理類,具體須要哪些東西?

  • 一份父類接口的字節碼
  • 一個用於加載生成的代理類的類加載器
  • 一系列的對目標對象的操做

這也就是newProxyInstance的參數含義,其中對目標對象的操做封裝在InvocationHandler類中

public interface InvocationHandler
 
 

InvocationHandler 是代理實例的調用處理程序 實現的接口。

Object
invoke(Object proxy, Method method, Object[] args)
          在代理實例上處理方法調用並返回結果。

這個類就是封裝了操做目標對象的具體實現代碼,也就是說,將功能也模塊化

InvocationHandler中只有一個方法,須要實現的其餘功能以及對目標對象的操做,須要在這裏完成

而後將這個對象做爲參數傳遞給newProxyInstance方法,從而構造代理類

下面的例子經過newProxyInstance方法實現動態代理

首先是一個公共接口

   1: public interface Speak {
   2:     public void sayHello();
   3: }

以及一個該接口的子類

   1: public class MySpeak implements Speak{
   2:     @Override
   3:     public void sayHello() {
   4:         // TODO Auto-generated method stub
   5:         System.out.println("Hello,World!");
   6:     }
   7: }

下面咱們能夠建立對該子類對象具體操做的InvocationHandler子類了

   1: public class SpeakHandler implements InvocationHandler {
   2:     //須要操做的目標對象
   3:     private Object obj;
   4:     
   5:     public SpeakHandler(Object obj) {
   6:         super();
   7:         this.obj = obj;
   8:     }
   9:  
  10:     /* (non-Javadoc)
  11:      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
  12:      */
  13:     @Override
  14:     public Object invoke(Object proxy, Method method, Object[] args)
  15:             throws Throwable {
  16:         //執行目標方法前的代碼
  17:         before();
  18:         
  19:         //利用反射執行目標對象的對應方法
  20:         Object retVal = method.invoke(obj, args);
  21:         
  22:         //執行目標方法後的代碼
  23:         after();
  24:         
  25:         return retVal;
  26:     }
  27:     
  28:     private void before(){
  29:         System.out.println("before");
  30:     }
  31:     
  32:     private void after(){
  33:         System.out.println("after");
  34:     }
  35: }

好了,準備工做完成了,這樣就能夠利用Proxy類獲取咱們的代理類對象了

   1: public class ProxyTest {
   2:  
   3:     /**
   4:      * @param args
   5:      */
   6:     public static void main(String[] args) {
   7:         
   8:         //生成目標對象
   9:         MySpeak ms = new MySpeak();
  10:         
  11:         //生成Handler對象
  12:         SpeakHandler speakHandler = new SpeakHandler(ms);
  13:         
  14:         //構造代理類,獲取對象
  15:         Speak speak = (Speak)Proxy.newProxyInstance(
  16:                 Speak.class.getClassLoader(),
  17:                 new Class[]{Speak.class},
  18:                 speakHandler);
  19:         
  20:         //執行代理類實現的接口方法
  21:         speak.sayHello();
  22:     }
  23: }

打印結果

before
Hello,World!
after

推測代理類內部代碼實現方式

根據Invocation的invoke方法的三個參數,咱們基本能夠推測出內部實現的代碼應該是這樣的:

sayHello(){

handler.invoke(this,this.getClass().getMethod(「sayHello」,null),null);

}

因此整個類的運行過程圖示爲:

proxy5

模塊化的代理

瞭解上面建立代理類的過程,咱們能夠抽取一些硬編碼進去的功能,提升程序的擴展性

首先,咱們要有一個接口用來表示建議執行的額外功能

   1: public interface Advice {
   2:     //目標對象執行前的操做
   3:     void beforeMethod();
   4:     //目標對象執行後的操做
   5:     void afterMethod();
   6: }

而後,就能夠完成咱們的一步構造代理類對象的方法了

   1: public class ProxyTest2 {
   2:  
   3:     /**
   4:      * @param args
   5:      */
   6:     public static void main(String[] args) {
   7:         
   8:         Collection col = (Collection)getProxy(new ArrayList(),new Advice() {
   9:             
  10:             @Override
  11:             public void beforeMethod() {
  12:                 // TODO Auto-generated method stub
  13:                 System.out.println("before!");
  14:             }
  15:             
  16:             @Override
  17:             public void afterMethod() {
  18:                 // TODO Auto-generated method stub
  19:                 System.out.println("after!");
  20:             }
  21:         });
  22:         
  23:         col.add(1);
  24:         col.add(2);
  25:         System.out.println(col.size());
  26:         System.out.println(col.getClass());
  27:  
  28:     }
  29:     
  30:     public static Object getProxy(final Object obj,final Advice advice){
  31:         //獲取代理類對象
  32:         Object retObj = Proxy.newProxyInstance(
  33:                 obj.getClass().getClassLoader(),//根據對象獲得類加載器
  34:                 obj.getClass().getInterfaces(),//根據對象獲得接口列表
  35:                 new InvocationHandler() {
  36:                     
  37:                     @Override
  38:                     public Object invoke(Object proxy, Method method, Object[] args)
  39:                             throws Throwable {
  40:                         //建議的系統功能
  41:                         advice.beforeMethod();
  42:                         
  43:                         Object retVal = method.invoke(obj, args);
  44:                         
  45:                         //建議的系統功能
  46:                         advice.afterMethod();
  47:                         
  48:                         return retVal;
  49:                         
  50:                     }
  51:                 });
  52:         return retObj;
  53:     }
  54:  
  55: }

這樣,咱們就將系統建議的功能以及須要操做的目標對象都暴露在外面供調用者使用,其餘所有封裝起來

注意:

這裏col.getClass打印的結果爲$Proxy

這是由於代理類在執行繼承自Object的方法時,只有執行到hashCode, equals, 或toString,纔會將方法傳遞到目標對象,也就是說只有執行到這三個方法時,纔會返回目標對象方法的返回值,而其餘的繼承自Object的方法目標則是代理類對象自己

實現AOP功能的封裝與配置

上面提到了動態代理是解決AOP的核心技術,那麼咱們來實現一個簡單的框架

BeanFactory類經過類中的getBean方法產生需求的對象

配置文件中配置一系列key value值,以下

#ArrayList=java.util.ArrayList
ArrayList=proxy.ProxyFactoryBean
ArrayList.target=java.util.ArrayList
ArrayList.advice=proxy.aopframework.MyAdvice

當value值不是一個ProxyFactoryBean對象時,直接返回讀取到的類的對象

當value值爲一個ProxyFactoryBean對象時,經過ProxyFactoryBean中的getProxy生成目標類的代理類對象,並返回

這樣就實現了一個簡單的獲取對象的框架

咱們須要作的就只是編寫本身須要用到的類,並配置到配置文件中,便可。

下面是實現過程

BeanFactory類

   1: /**
   2:  * @author Shawn
   3:  * Bean工廠
   4:  */
   5: public class BeanFactory {
   6:     Properties props = new Properties();
   7:     
   8:     public BeanFactory(InputStream ips){
   9:         try {
  10:             //載入配置文件讀取流
  11:             props.load(ips);
  12:         } catch (IOException e) {
  13:             // TODO Auto-generated catch block
  14:             e.printStackTrace();
  15:         }
  16:     }
  17:     
  18:     //主要方法,根據key值生成對象
  19:     public Object getBean(String name){
  20:         Object obj = null;
  21:         String className = props.getProperty(name);
  22:         try {
  23:             Class beanClass = Class.forName(className);
  24:             obj = beanClass.newInstance();
  25:         } catch (Exception e1) {
  26:             // TODO Auto-generated catch block
  27:             e1.printStackTrace();
  28:         }
  29:         //若是須要生成的是代理類對象
  30:         if(obj instanceof ProxyFactoryBean){
  31:             Object retObj = null;
  32:             try {
  33:                 //讀取對應的Advice系統功能類,並建立對象
  34:                 Advice advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance();
  35:                 //讀取對應的target目標類,並生成對象
  36:                 Object target = Class.forName(props.getProperty(name+".target")).newInstance();
  37:                 ProxyFactoryBean proxyObj = (ProxyFactoryBean)obj;
  38:                 //爲ProxyFactoryBean對象set相關屬性
  39:                 proxyObj.setAdvice(advice);
  40:                 proxyObj.setTarget(target);
  41:                 //經過getProxy方法得到代理類對象
  42:                 retObj = proxyObj.getProxy();
  43:             } catch (Exception e) {
  44:                 // TODO Auto-generated catch block
  45:                 e.printStackTrace();
  46:             }
  47:             return retObj;
  48:         }
  49:         else
  50:             return obj;
  51:     }
  52: }

ProxyFactoryBean類

   1: /**
   2:  * @author Shawn
   3:  * 代理工廠
   4:  */
   5: public class ProxyFactoryBean {
   6:     //目標類
   7:     private Object target;
   8:     //系統建議的方法
   9:     private Advice advice;
  10:     
  11:     
  12:     public Object getTarget() {
  13:         return target;
  14:     }
  15:  
  16:     public void setTarget(Object target) {
  17:         this.target = target;
  18:     }
  19:  
  20:  
  21:     public Advice getAdvice() {
  22:         return advice;
  23:     }
  24:  
  25:  
  26:     public void setAdvice(Advice advice) {
  27:         this.advice = advice;
  28:     }
  29:  
  30:     //獲取代理類對象
  31:     public Object getProxy() {
  32:         
  33:         Object retObj = Proxy.newProxyInstance(
  34:                 target.getClass().getClassLoader(),//根據對象獲得類加載器
  35:                 target.getClass().getInterfaces(),//根據對象獲得接口列表
  36:                 new InvocationHandler() {
  37:                     
  38:                     @Override
  39:                     public Object invoke(Object proxy, Method method, Object[] args)
  40:                             throws Throwable {
  41:                         //建議的系統功能
  42:                         advice.beforeMethod();
  43:                         
  44:                         Object retVal = method.invoke(target, args);
  45:                         
  46:                         //建議的系統功能
  47:                         advice.afterMethod();
  48:                         
  49:                         return retVal;
  50:                         
  51:                     }
  52:                 });
  53:         return retObj;
  54:     }
  55:  
  56: }

下面進行測試,編寫本身的MyAdvice類,實現Advice接口,表示系統建議的功能

   1: /**
   2:  * @author Shawn
   3:  *
   4:  */
   5: public class MyAdvice implements Advice {
   6:  
   7:     /* (non-Javadoc)
   8:      * @see proxy.Advice#afterMethod()
   9:      */
  10:     @Override
  11:     public void afterMethod() {
  12:         // TODO Auto-generated method stub
  13:         System.out.println("after");
  14:  
  15:     }
  16:  
  17:     /* (non-Javadoc)
  18:      * @see proxy.Advice#beforeMethod()
  19:      */
  20:     @Override
  21:     public void beforeMethod() {
  22:         // TODO Auto-generated method stub
  23:         System.out.println("before");
  24:  
  25:     }
  26: }

配置文件

   1: #ArrayList=java.util.ArrayList
   2: ArrayList=proxy.aopframework.ProxyFactoryBean
   3: ArrayList.target=java.util.ArrayList
   4: ArrayList.advice=proxy.aopframework.MyAdvice

測試類

   1: public class AopFrameworkTest {
   2:  
   3:     /**
   4:      * @param args
   5:      */
   6:     public static void main(String[] args) {
   7:         
   8:         //載入配置文件
   9:         InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
  10:         
  11:         //建立Bean工廠
  12:         BeanFactory factory = new BeanFactory(ips);
  13:         
  14:         //得到對象
  15:         Collection list = (Collection)factory.getBean("ArrayList");
  16:         
  17:         list.add(1);
  18:         list.add(2);
  19:         System.out.println(list);
  20:     }
  21: }

這樣就完成了一個簡單的框架

配置文件中

ArrayList=java.util.ArrayList時,獲得的是目標類對象

ArrayList=proxy.aopframework.ProxyFactoryBean時,獲得的是目標類的代理類對象

相關文章
相關標籤/搜索