閱讀984 返回首頁    go 阿裏雲 go 技術社區[雲棲]


JAVA核心層--反射--動態代理

本文發表於2010年,時間較早,部分問題解釋不是十分準確,所以需要進一步了解,請參看2012年版本:

java之架構基礎-動態代理&cglib


要在JAVA技術上突破普通的層麵,並擁有一翻設計理念的高度,除了要有很好的設計思維之外,反射在適當的使用下,將會把框架做得非常清晰,並且代碼編寫也非常簡便。

在麵向對象的編程中,我們為什麼要忌諱去大量使用if else switch語句,因為這樣寫是將邏輯硬編碼了,JAVA的思想就是將其配置化,一旦可配置化後,就逐漸可管理化,並隨著產品的成熟逐步實現自動化管理的過程(這又涉及到設計能力了),我們這裏先拋開語言的界限,但說技術上如何實現反射,在適當的位置會提及在實踐中如何靈活應用它就會得心應手。本文主要闡述的內容有:

 

1、動態代理的意義

2、動態代理入門

3、模擬Struts分發、填充核心原理

4、模擬Spring IoC、AOP核心原理

5、書寫框架中的一些技巧

 

 言歸正傳==》

1、動態代理的意義

       從個人的語言來說,動態代理機製是每一門語言的基礎,沒有動態代理什麼也做不了,也就是動態代理是運行語言的代碼,在JAVA中請最先記住一個invoke(),後麵會細節說明,其實在麵向對象的設計中,實現了繼承、封裝、多態三大特征,不過僅僅是使用這些功能,你會發現和C語言沒有什麼區別,使用其充分的擴展性才能體現麵向對象的好處,而其擴展性在諸多方麵有所體現,而擴展性的基礎來源於總體的設計和架構,動態代理將會提供非常好的機製統一總控管理和分發代碼片段,將邏輯層抽象成模型,將判定作為分發方式,便於相應模型擴展就變成了配置,而不是代碼片段(因為代碼需要全線跟蹤,全局觀念很低就會導致整篇修改的跡象,而且修改任何一個小問題需要重新測試、發包、上線周期很長而且會導致很多BUG),也就是說做配置的目的就是把邏輯層抽象為模型,將模型的類型進行分解進行配置化,這樣的代碼下即可實現產品化,又可以實現個性化,當然總有一個限度,至於到達何種程度,關鍵看設計者的境界了。

      以前我在部分博客上有人寫道:如果你發現你的代碼中幾乎沒有if else的時候,你麵向對象的設計思想就達到一個境界了,其實他說得很多方麵也是很有道理的,因為這類層套層的判定邏輯,是很難維護的,除非編寫非常複雜的算法,業務上是不太可能有這樣的情況。因為很多寫代碼的人都是從C過來的,其算法學得不錯,而分析問題的方式和JAVA完全不一樣(其實我也是那樣走過來的,我曾經寫代碼也是那樣),JAVA最重要的就是先業務問題抽象化,抽象為模型去解決,而不是隻想到這段代碼怎麼去寫,然後開始棒棒棒的寫起來,也許你寫的代碼很長,你覺得很牛,而且寫得很快,不過當軟件逐漸做大做複雜的時候,麻煩就要開始了。

       軟件設計中,並不是不讓你用if else,而是業務層不應該這樣使用,業務千變萬化,無窮無盡,除非這個軟件周期很短或者根本就隻是為了應付而不想做出什麼好的產品出來,那可以這樣,因為這樣在開發小軟件的時候周期非常快。模型在業務設計中不斷抽象和尋找最佳切入點後,你會發現在複雜的業務,也可以抽象為幾種簡單的模型,而模型變化的可能性很小,沒有大的動作就是那麼一些東西,一旦將這些模型抽象出來後,逐漸產品化就可以實現界麵配置化,甚至於提取抽象層次和自動化管理,現在比較經典的就是工作流(用硬編碼去寫工作流會死人,除非沒有業務可說),工作流就是將運行過程中的業務過程的環節、路由、資源、崗位、人員、權限等信息抽象出來,並相應組織,利用驅動引擎將配置信息跑起來的一個過程,不過細節追蹤就是技術問題了,如何讓他可以並行運行和加快速度,我隻能說這還是設計問題(技術設計),設計得好就會成倍提速,設計得不好往往會適得其反。

       其實要一個進步的訣竅,就是不要妄下結論,不論達到什麼境界,遇到任何問題都要想一下,思考一下全局觀,將邏輯層抽象,大部分係統的業務層其實並不複雜(業務和邏輯並不完全一樣,可以說業務包含邏輯吧,我們要做的就是首先將業務中的邏輯抽象出來,並將相應邏輯進行合並和再次抽象,反複思量才下手,時間長了你會發現,你寫一行代碼就可以搞定一堆事情,而不是寫一堆代碼搞定一件事情,這樣的JAVA層次其實不用多說了,至少是入道了);其次,一定要coding,優秀的架構師一定要coding(估計這也算是國內和國外的一個區別吧,國內的架構師普遍不做coding),因為隻有摸著,而且摸得恨透,設計的時候才會得心應手,而且coding的能力要遠遠超越普通程序員。

      閑話扯遠了,開始扯動態代理吧(本文全部以JDK1.5為基礎進行說明)。

 

2、動態代理入門

2.1.一兩個例子,如何簡化代碼:

首先,引入一個話題,逐漸切入,平時大部分人都會寫的類似代碼:

if(a >= b) {
    return true;
}else {
    return false;
}

我們覺得是很簡單東西,其實,這樣寫是沒有問題的,隻是,這部分大家可以想一下,我們暫時拋開JAVA提供的是否有這樣的比較,這樣的比較在代碼中應當是反複出現的邏輯,而合業務並不相關,那麼首先想到的第一步就是,這樣簡單的東西怎麼就那麼複雜呢,這個本來就是一行代碼搞定的事情:

return a>=b;--其實很容易就想到了,因為要返回的結果就是這個,為什麼還要自己去判定一次呢?

 

好繼續深入,有沒有可以直接判定的,連大於小於都不用,答案是肯定的,至少可以通過很多方式得到類似的結果:

JAVA提供的符號位方式:Integer.signum(a - b)通過符號位即可知道大小情況。

(a - b)>>>31; 得到是否為1也可以知道a是否大於等於b。等等。

 

這樣寫程序,為什麼很簡單,因為共享的邏輯已經被抽象了。

 

我們再來舉個簡單常用的代碼:

if(a !=null && "1".equals(a)) {
    ....
}else  if(a!=null && "2".equals(a)){
    ....
}else....

 

這種代碼我相信是很常見的,這種代碼給人第一感覺就是:不幹淨,更加不利落。如何抽象第一層,將首相不想看到的層數,抽象到框架的公共方法中,由於是對比字符串,那麼我們就給一個工具方法調用:

public static boolean equalString(String a,String b) {
   if(a ==null || b==null) {
       return false;
   }
   return a.intern() == b.intern();
}

 

上述代碼就把框架部分寫完,假如寫在一個叫做StringUtils的類中,那麼上述代碼就變成了:

if(StringUtils.equalString(a,"1")) {
  ....
}else if(StringUtils.equalString(a,"2"))  {
  ...
}else {
  ...
}

這樣至少幹淨多了,那麼你會覺得還不夠利落,因為代碼畢竟還是在用判定語句,不過就普通的代碼,我們寫成這樣也無所謂了,隻是共享的部分把它繼續抽象即可,但是你要將複雜的問題繼續簡單化,我們就需要繼續使用反射(抽象的技術原理有很多,反射隻是其中一例,類似動態參數也在很多時候用好了非常好用)

再舉個例子,我想啥係統都會遇到拚字符串的代碼吧(不論是前台還是後台),在我所接觸的係統中很多這部分都是放到業務上去編寫,我就沒搞懂這個拚串和業務有關係嗎?而且程序員很多時候寫得花花綠綠,各種各樣的寫法,稍有差錯,各種各樣的小BUG就出來了,而且對性能沒多大概念的人普遍用+去拚字符串,因為那樣覺得很簡單,為了將業務層代碼簡單化、降低共享代碼不可控的BUG、以及綜合性能,我們將它抽象出來寫,簡單寫法就是(實際業務需要做一些改造):

public static String concatStr(int avglen,String conString,String ...strings) {
    if(strings == null || strings.length == 0) {
        return DEFAULT_NULL_STR;
    }
    StringBuilder strValue = new StringBuilder(avglen*strings.length);
    for(String str : strings) {
       strValue.append(str).append(conString);
    }
    return strValue.delete(strValue.length()-conString.length(), strValue.length()).toString();
}

是不是覺得一個簡單的拚串搞得這麼麻煩,不是的,不然框架用來幹什麼呢,框架要解決的一大問題就是要把很多公共的事情做了,這部分又不需要程序員來寫,程序員隻需要做一步操作:StringUtils.concatStr(10,",","xieyu1","xieyu2","xieyu3");就能得到想要的結果了:“xieyu1,xieyu2,xieyu3”這樣對程序員來說統一編碼,共享問題共享解決(如這段代碼有BUG,全係統統一一個地方切入點去解決,而不是全盤複製,代碼具有可控性,其次避免程序員因為習慣不好,導致的一係列的常規性能問題),不僅僅是UTIL類,包含繼承將係統級別、業務級別的共享操作在父親類進行封裝、接口用以定義統一規範或交互協議、在這個基礎上當然可以實現多態了。

這裏順便提及一下多態:舉個生活中的例子,定義個汽車類的統一規範,上層人員不需要知道汽車是啥樣子的,隻要定義汽車有什麼大致屬性(作為、輪胎、方向盤、GPS等)以及有哪些功能(啟動、加速、刹車)等,這些功能如何去實現就交予第三方去完成,而客戶不必關心,各個開發商可能實現機製不同,內部可能定義一些自己的屬性,但是根據統一接口對客戶開放的功能就隻有要求的哪些,如啟動功能,每個製造商在啟動功能的實現方式上都不一樣,也會定義不同的屬性,但是我想買誰的就買誰的,買到誰的就用誰的功能,選擇性很強,但是隻要你會開這類車,其它的車也差不多會開。

而較為明顯的例子就是JDBC連接池,我們之所以需要驅動包就是因為SUN公司並沒有實現這部分,但是為了代碼的統一性,便於移植,他定義了統一規範,也就是調用數據庫的統一接口,在不同的數據庫,即使同一門數據庫不同的驅動版本或者不同開發商開發的驅動,他們內部實現都是不一樣的,但是無論那個開發商拿過來的驅動程序以及無論哪一門數據庫,都可以用同樣的代碼規範去調用(SQL建議不要想去統一,這個會有很多問題,代碼上可以一樣,SQL可以根據數據庫各自去做一套配置,根據頂層配置選擇走那一套配置SQL就OK了),這就是我們所謂的多態性的調用,在企業級應用中靈活性非常強。

由於這裏隻是開一個頭,所以我隻是從技術角度說明一個最簡單的反射調用是如何調用的,根據下麵如何解析Struts和Spring常規框架代碼的實現過程來說明反射如何應用在實際中的。

一個最簡單的反射代碼:

//創建一個被處理類

class ReflectObject {
   //測試信息
   private String info;
   public String getInfo() {
    return info;
   }
   public void setInfo(String info) {
    this.info = info;
   }
}

 

public class SampleReflect {
   //根據類的全名創建對象的方法:
   public static Object createObject(String javaName) {
     try {
         return Class.forName("ReflectObject").newInstance();
     } catch (InstantiationException e) {
         e.printStackTrace();
     } catch (IllegalAccessException e) {
         e.printStackTrace();
     } catch (ClassNotFoundException e) {
         e.printStackTrace();
     }
     return null;
  }
  //根據對象和方法信息執行:
  public static Object invokeMethod(Object obj, String methodName,
   Object[] params, Class clazz[]) {
     try {
         Method method = obj.getClass().getMethod(methodName, clazz);
         return method.invoke(obj, params);
    } catch (IllegalArgumentException e) {
         e.printStackTrace();
    } catch (IllegalAccessException e) {
         e.printStackTrace();
    } catch (InvocationTargetException e) {
         e.printStackTrace();
    } catch (SecurityException e) {
         e.printStackTrace();
    } catch (NoSuchMethodException e) {
         e.printStackTrace();
    }
     return null;
 }
  //測試代碼(這部分代碼也是業務運行中除框架部分的業務代碼):
  public static void main(String[] args) {
        Object obj = createObject("ReflectObject");
        invokeMethod(obj, "setInfo", new Object[] { "xieyu" },
               new Class[] { String.class });
        System.out.println(invokeMethod(obj, "getInfo", null, null));
 }
}


可能你會想,幹嘛搞這麼複雜,不就是為了執行一個set和一個get操作嘛,而且把簡單的問題複雜化了,其實不然,我們這裏隻是為了說明技術問題,開了一個頭,一旦我們可以動態創建,可以動態執行方法,那麼一切都活起來了,當你的代碼中存在大量複雜邏輯的時候,抽象出來後,既可以配置化管理,也可以注入內存,更加可以將相似的判定邏輯進行合並,便於整個管理體係清晰化,並逐步實現界麵和自動化管理的過程。

著:這從一個角度闡述了JAVA在相對其他語言開發項目時,前期的開發時間消耗是較長的,而且越到後麵,越加顯示出J2EE體係的擴展性,隻要上層設計者有全局觀和較好的技術功底,下麵的程序員也有一定的功底(但是千萬不要輕易的認為任何人都可以寫好業務代碼,即使是普通的代碼也不是學兩天寫代碼就可以寫好的)。

 

3、模擬Struts分發、填充核心原理

3.1、struts分發原理:

struts在分發的過程中,底層采用了類似的動態代理,即,調用那個類的那個方法(這些方法都是相同的入參,並返回相同類型的結果),那麼這就和我們上麵的動態調用結合起來了,那麼我們這裏動態調用過程中首先確定的就是入參的類型,一會在3.3的實例中會有所體現。

這裏舉例如何實現基本的分發方式:

首先建立一個BeanUtils,用於中間處理(實際運行中不是這樣的,是通過代理實現,這裏我們用一個UTIL手工調用來模擬):

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class BeanUtils {
 
 private final static String DEFAULT_NULL_STR = "";
 
 /**
  * 模擬配置
  */
 private static Map config = new HashMap();
 
 static {
  Map map1 = new HashMap();
  map1.put("key1", "getInfo1");//這裏假定模擬一下數據裝入緩存的過程
  map1.put("key2", "getInfo2");
  config.put("Hello", map1);
 }
 
 private static String getMethod(String busi,String state) {
  Map map = (Map)config.get(busi);
  return map == null ? DEFAULT_NULL_STR:(String)map.get(state);
 }
 
 /**
  * 
  * @param obj
  * @param params
  * @param methodName
  * @param clazz
  * @param busi
  * @param state
  * @return
  */
 public static Object invokeMethod(Object obj, Object params[],Class clazz[],String busi,String state) {
  String methodName = getMethod(busi, state);
  return invokeMethod(obj, new Object[]{"2",new Integer(1)}, methodName, new Class[]{String.class,Integer.class});
 }
 
 
 /**
  * 動態方法調用
  * @param obj
  * @param params
  * @param methodName
  * @param clazz
  * @return
  */
 public static Object invokeMethod(Object obj, Object params[],String methodName,Class clazz[]) {
  try {
   Method method = obj.getClass().getMethod(methodName, clazz);
   return method.invoke(obj, params);
  } catch (IllegalArgumentException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (SecurityException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return null;
 }
 
 /**
  * 動態方法調用
  * @param obj
  * @param params
  * @param methodName
  * @param clazz
  * @return
  */
 public static Object invokeStaticMethod(Class objClass, Object params[],String methodName,Class clazz[]) {
  try {
   Method method = objClass.getMethod(methodName, clazz);
   return method.invoke(null, params);
  } catch (IllegalArgumentException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (SecurityException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return null;
 }
}
//此時需要創建一個被處理的結構:
public class Hello {
 private final static String BUSI_STR = "Hello";
 public String getInfo1(String value, Integer i) {
  return "你傳入的參數是:" + value + "/t" + i;
 }
 public String getInfo2(String value, Integer i) {
  return "你傳入的參數是:" + value;
 }
 public void testInfo(String state) {
  System.out.println(BeanUtils.invokeMethod(this, new Object[] { "1",
    new Integer(1) }, new Class[] { String.class, Integer.class },
    BUSI_STR, state));
 }
}
//最後任意創建一個測試類,然後在裏麵寫一個main方法如下調用:
public static void main(String []agrs) {
  /*在struts中,其實我們要繼承一個定層的Action,是因為有一個公共的方法調用後,其功能類似於這裏的testInfo方法,用於根據參數信息,動態調用指定的方法。*/
  Hello hello = new Hello();
  hello.testInfo("key1");//通過關鍵詞,動態調用指定的方法體
  hello.testInfo("key2");
}


3.2、填充核心原理:

填充其實和分發也是一樣的道理,隻是根據入參信息去填充對應的信息,當從request接受到一個參數時,根據參數的名稱,加上一個set+參數名稱首字母大寫,根據方法名稱和參數類型,查找是否存在這個方法,類型也會對應一下,如果存在就動態調用他,通過其BeanUtils內部的copyproperties方法也是同樣的原理,這樣就把複雜的問題抽象出去模型化,將業務簡單化,或者說業務就專門寫業務,而不必關心每一個參數交互的細節,這就是我們框架想要達到的基本目的之一。

這裏舉個填充的簡單例子(這裏的例子並不是實際的代碼,實際的代碼會考慮更多的問題,隻是為了說明一些問題,所以隻是簡單給一個原理上的思路,創建一個FillObject.java,把一下代碼拷貝進去):

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
//建立一個基本的FORM信息來模擬實際的FORM
class BaseForm {
 
 private String name;
 
 private String password;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
}
class HttpRequest {//模擬一個Request
 
 private HashMap map;
 
 private HashMap arrtibute = new HashMap();
 
 public HttpRequest(String url) {
  if(url == null) {
   return;
  }
  String []params = url.split("&");
  map = new HashMap(params.length*4/3+1);
  for(int i = 0,size = params.length ; i<size ; i++) {
   String []tmp = params[i].split("=");
   if(tmp.length > 1) {
    map.put(tmp[0], tmp[1]);
   }
  }
 }
 
 public String getParameter(String name) {
  return (String)map.get(name);
 }
 
 public Map getParameterMap() {
  return (Map)map.clone();
 }
 
 public void setAttribute(String key,Object obj) {
  arrtibute.put(key, obj);
 }
 
 public Object getAttribute(String key) {
  return arrtibute.get(key);
 }
}
public class FillObject {
 
 private String formName;
 
 public FillObject(String formName) {
  this.formName = formName;
 }
 
 private void fillInfo(BaseForm ele,String key,String value) {
  String first = String.valueOf(key.charAt(0)).toUpperCase();
  String methodName = "set" + first + key.substring(1);
  try {
   Method method = ele.getClass().getMethod(methodName,new Class[]{String.class});
   method.invoke(ele,new Object[]{value});
  }catch(Exception e) {
   e.printStackTrace();
  }
 }
 
 private BaseForm fillObject(HttpRequest request) {
  BaseForm ele = new BaseForm();
  Map map = request.getParameterMap();
  for(Iterator iter = map.keySet().iterator() ; iter.hasNext() ; ) {
   String key = (String)iter.next();
   String value = (String)request.getParameter(key);
   fillInfo(ele,key,value);
  }
  return ele;
 }
 
 public void doGet(HttpRequest request) {
  System.out.println(request.getParameter("password"));
  BaseForm ele = (BaseForm)request.getAttribute(formName);
  System.out.println(ele.getName());
  System.out.println(ele.getPassword());
 }
 
 public void init() {
  HttpRequest request = new HttpRequest("name=xieyuooo&password=123err");
  BaseForm form = this.fillObject(request);
  request.setAttribute(formName, form);
  this.doGet(request);
 }
 //測試代碼
 public static void main(String []args) {
  FillObject fill = new FillObject("baseForm");
  fill.init();
 }
}


4、Spring IoC、AOP核心原理

  4.1.其實上述過程看了後,尤其是填充部分,基本清楚為什麼IOC是怎麼回事。隻是我們這裏不討論複雜的各項數據類型,所以程序寫的比較簡單。而IOC比起填充來說比較多的一點就是有隱含的多態機製在內部,基於接口去實現,在下麵的代碼例子中,不會給出Spring的實質實現方式,隻是給出填充+多態的方式,在AOP中會提及其核心思想。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
interface Car {
 
 //接口規範定義要求實現一個現實操作
 public void display();
 
 //修改接口
 public void updateinfo(String info);
}
//頂一個拖拉機的類
class Tractor implements Car {
 
 //定義一個的名字
 private String name;
 /**
  * @param name the name to set
  */
 public void setName(String name) {
  this.name = name;
 }
 public void display() {
  System.out.println("/n拖拉機的名稱為:"+name);
 }
 
 public void updateinfo(String info) {
  this.name = info;
 }
}
//定義桑塔拉的類
class Santala implements Car {
 
 private String model;
 
 private String color;
 
 public void setModel(String model) {
  this.model = model;
 }
 
 public void setColor(String color) {
  this.color = color;
 }
 public void display() {
  System.out.println("/n==>桑塔拉的型號為:"+this.model+"/n==>桑塔拉的顏色是:"+this.color);
 }
 
 public void updateinfo(String info) {
  this.model = info;
 }
}
//創建一個處理調用類:
class ServiceFactor {
 //本來應該通過配置文件進行set,我這裏就直接通過靜態塊模擬,相當於將配置讀入內存
 private static Map configInfo = new HashMap();
 //實例化的BEAN放在這裏
 private volatile static Map beanObject = new HashMap();
 
 static  {//初始化一下信息,這裏相當於配置文件一樣
  Map temp = new HashMap(4);
  configInfo.put("tractor", temp);
  temp.put("name", "1號拖拉機");
  temp.put("className", "testreflect.Tractor");
  
  temp = new HashMap();
  configInfo.put("santala", temp);
  temp.put("model", "007");
  temp.put("color", "紅色");
  temp.put("className", "testreflect.Santala");
 };
 
 public static Object getBeans(String beanName) {
  if(beanObject.containsKey(beanName)) {
   return beanObject.get(beanName);
  }
  Map setInfo = (Map)configInfo.get(beanName);//獲取配置信息
  String className = (String)setInfo.get("className");
  Object obj = null;
  try {
   obj = Class.forName(className).newInstance();
   beanObject.put(beanName, obj);
   for(Iterator iterator = setInfo.keySet().iterator();iterator.hasNext();) {
    String key = (String)iterator.next();
    if(key.intern() != "className") {
     String methodName = toFirstUpper(key);
     Method method = obj.getClass().getMethod(methodName, new Class[]{String.class});//我這裏假如反射的都是String類型
     method.invoke(obj, new Object[]{setInfo.get(key)});
    }
   }
  } catch (IllegalArgumentException e) {
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   e.printStackTrace();
  }catch (SecurityException e) {
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   e.printStackTrace();
  }catch (InstantiationException e) {
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   e.printStackTrace();
  } catch (ClassNotFoundException e) {
   e.printStackTrace();
  }
  return obj;
 }
 

 private static String toFirstUpper(String key) {
  String temp = key.substring(0,1).toUpperCase();
  return  "set" + temp + key.substring(1);
 }
}


最後創建一個測試類,寫一個main方法開始測試:

public static void main(String []agrs) {
  Car  car1 = (Car)ServiceFactor.getBeans("tractor");
  Car  car2 = (Car)ServiceFactor.getBeans("santala");
  Car  car3 = (Car)ServiceFactor.getBeans("tractor");
  car1.display();
  car2.display();
  car3.display();
  car1.updateinfo("2號拖拉機");//講car1進行修改
  car3.display();//顯示car3的信息
 }

該程序實現單例對象獲取,並根據參數不同得到不同實例(這些參數在實際運行中可以有動態來源,實現企業級應用中的擴展性)

  4.2.Spring AOP就是切入式編程,也有說法是麵向切麵的編程,什麼意思呢,就是可以在調用方法前執行一些東西,並且可以獲取傳入參數,調用方法後執行一些東西,可以獲取返回參數,剛開始接觸這一塊的時候覺得不可思意,甚至於打破曾經自己認為的所有的第三方框架都會給予JAVA基礎編寫起來,結果通過研究後,反射就是其基本原理,AOP就是通過其底層真正的動態代理來完成的,上麵的隻是開個頭,這才是關鍵。我們以JDK1.5寫一個例子,這個東西是如何做到切入的。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//模版程序,創建泛型代碼 支持JDK5或更高版本
interface Hello<T1, T2> {
 public T1 getInfo();
 public <TX> void setInfo(T1 infos1, T2 infos2, TX a);
 public void display();
}

//編寫一個實現類:

class HelloInfo<T1, T2> implements Hello<T1, T2> {
 private volatile T1 infos1;
 private volatile T2 infos2;
 public T1 getInfo() {
  return infos1;
 }
 public <TX> void setInfo(T1 infos1, T2 infos2, TX a) {
  this.infos1 = infos1;
  this.infos2 = infos2;
  System.out.println("多餘的參數就是我了:" + a);
 }
 public void display() {
  System.out.println("/t/t" + infos1 + "/t" + infos2);
 }
}

//實現InvocationHandler 並重寫invoke方法是這裏的關鍵點

class AOPFactory implements InvocationHandler {
 private Object proxyed;
 public AOPFactory(Object proxyed) {
  this.proxyed = proxyed;
 }
 public Object invoke(Object proxyed, Method method, Object[] args)
   throws IllegalArgumentException, IllegalAccessException,
   InvocationTargetException {
  System.out.println("/n====>調用方法名:" + method.getName());//測試用於輸出
  printInfo("傳入的參數為:", args);
  Object result = method.invoke(this.proxyed, args);
  printInfo("傳出的參數為:", result);
  return result;
 }
  //用於輔助輸出一些內容
 
public void printInfo(String info, Object... args) {
  System.out.println(info);
  if (args == null) {
   System.out.println("/t空值。");
   return;
  }
  for (Object obj : args) {
   System.out.println("/t" + obj);
  }
 }
}

// 創建一惡搞BeanService,根據類全名(本來應該有一個關鍵詞和一個類全名對應關係,一般寫入配置文件中,這裏為了方便就直接用類的全名):

class AOPService {
 public static Object getBean(String className)
   throws InstantiationException, IllegalAccessException,
   ClassNotFoundException {
  Object obj = Class.forName(className).newInstance();
  InvocationHandler handler = new AOPFactory(obj);
  return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
    .getClass().getInterfaces(), handler);
 }
}
//編寫一段測試代碼:
public static void main(String[] args) {
  try {
   Hello<String, String> hello = (Hello<String, String>) AOPService.getBean("com.lianchuang.pkg.HelloInfo");
   hello.setInfo("xieyu1", "xieyu2", "謝宇");
   hello.getInfo();
   hello.display();
   } catch (Exception e) {
   e.printStackTrace();
  }
 }

當你運行這段代碼的時候,你會發現,雖然是直接調用你的方法,但是,它會通過AOPFactory類的invoke方法去間接調用你的方法,而不是直接調,在這個過程中,就可以做想做的事情了。

AOP一般用於:通用日誌編寫、公用事務編寫等等。不要濫用AOP,讀過他的源碼就知道,它的性能並不是很樂觀。

5、書寫框架中的一些技巧

上麵說了那麼多其實對於框架的編寫都是廢話,因為在框架編寫中有諸多技巧性的東西,而反射隻是幫助我們解決其中一部分問題,而框架編寫中到底有哪些技巧可言呢,這就個人經驗和其理論功底而論了,因為經驗會在不同行業上產生不同的問題,讓人在按照現在所在公司的規範去解決這些問題,而功底是看人悟性以及對於問題的看法是否有把技術抽象出來。

就我個人來說一般有以下一些小經驗,請多指教:

編寫公司級別的框架:

編寫公司級別的框架應當是輕量級的,而不是非常重的框架,這類框架適合開發商做公司業務上的絕大部分項目的基礎平台,其包含大量的工具和基礎類庫,這些代碼最好由高手來寫,因為它的每一行代碼將會決定後來擴展框架的性能和穩定性的基礎,這些裏麵大部分類都是後來要完成的係統的頂層父類、統一接口、組件包、工具等。

編寫業務級框架:

在公司級框架基礎上不可能一層不變的搬過來照常用,除非公司的框架是按照工具進行開發的,這種產品化容易做到,但是整體體係容易分散,關鍵看管理了。根據通用框架編寫業務級別的框架的擴展,這些普遍應用於一個行業領域,根據行業領域有一個實踐性的分析,在業務框架中包含大量對於行業內的通用業務處理,並建立業務體係和大量的基層數據結構,並將數據分類,定義相應的緩存和交換法則在框架中實現,在實際的項目中在依賴於業務框架基礎上幾乎不用做太大的變化就可以實現。

產品化的軟件:

軟件產品化一般指一個小的子係統,整個體係的產品化需要進一步整合,產品化的軟件就是為了以最小的代價實現絕大部分行業類的響應軟件需求,開發模式一般以一個集中的方式來方便版本的控製,如果抽象得很好的,會將各個層次抽象為模型,進行設計和研發,在現場基本全是配置化的管理,而沒有代碼的開發,若弄不好,代碼冗餘程度很高,還不如不這樣做;也有個性化強度很高的,就是做一個基礎版本,各個現場使用基礎產品來完成實際軟件的研發。

性能和穩定性:

在抽象層次的基礎上,軟件的擴展性很多還要靠程序員的配合來完成,也就是最終還是落實到程序員,但是性能呢?

從係統的層麵,係統的性能主要決定於:應用係統的設計、數據庫結構設計、數據庫架構以及硬件選型、SQL優化等。

在應用開發層麵主要關心的是應用係統設計、數據結構設計、SQL優化,而應用係統設計主要就是框架的設計,哪些是做緩存處理、如何做緩存處理、輸出壓縮、提供共享操作高效且低BUG的代碼、如何充分利用多線程、如何通過框架降低BUG的概率、如何充分利用CPU、如何解決IO瓶頸(從代碼策略和配置兩方麵)、如何負載均衡、失敗切換等都是我們需要考慮的問題,如果有必要還會去考慮內存通信等。

其次就是數據結構設計,數據結構設計將會決定在絕大部分情況下數據庫的查詢性能的長期目標,如果結構設計得不好,數據量一上來,馬上非常慢,怎麼優化SQL也都是治標不治本的方法,通常這個需要在結合業務經驗的基礎上,要有一些數據庫體係結構思想。

最後才是SQL優化,當然對於程序員來說這是最重要之一,因為前麵的不管程序員的事情,除了編寫一點多線程(最好在框架中分出來,而不是讓程序員去編寫),程序員隻關心業務過程的編寫,以及如何使用框架提供的基礎類庫來完成這些業務編碼。而SQL呢,SQL程序員是必備掌握的,而且也是一門很深的學問,可以說簡單,也可以說複雜,要研究進去很多事情很不好解釋,不研究這些東西就顯得就那麼回事,SQL基本要達到從開始掌握語法會寫SQL開始、如何變換SQL改善性能、如何查找複雜SQL中的邏輯可合並的部分進行合並、如何通過體係結構的學習通過本質和提供的方案來解決、如何從設計角度規範化統一優化方法等等,逐步的一個過程。

 

所談之處尚欠,望指教中。。。

最後更新:2017-04-02 06:51:22

  上一篇:go JavaScript:Select標簽
  下一篇:go magento -- 根據產品的sku獲得產品