黑馬程序員 - Java 高新技術2

 

---------------------- android培訓java培訓、期待與您交流! ----------------------


內省-->瞭解JavaBean

 

IntroSpector --> JavaBean --> 特殊的Java類。其名字要按特定的規則獲取。

      JavaBean是一種特殊的Java類,主要用於傳遞數據信息,這種java類中的方法主要用於訪問私有的字段,且方法名符合某種命名規則

      符合特殊命名規則的Java類,就稱爲JavaBean。(可以將JavaBean當作一般類操作,但一般類不能當作JavaBean操作。)

int getAge()

void setAge(int age)                                                                                                                                            

 

      如果要在兩個模塊之間傳遞多個信息,可以將這些信息封裝到一個JavaBean中,這種JavaBean的實例對象通常稱之爲值對象(Value Object,簡稱VO)[30]。這些信息在類中用私有字段來存儲,如果讀取或設置這些字段的值,則需要通過一些相應的方法來訪問,大家覺得這些方法的名稱叫什麼好呢?JavaBean的屬性是根據其中的setter和getter方法來確定的,而不是根據其中的成員變量。如果方法名爲setId,中文意思即爲設置id,至於你把它存到哪個變量上,用管嗎?如果方法名爲getId,中文意思即爲獲取id,至於你從哪個變量上取,用管嗎?去掉set前綴,剩餘部分就是屬性名,如果剩餘部分的第二個字母是小寫的,則把剩餘部分的首字母改成小的。

      setId()的屬性名-->id

      isLast()的屬性名-->last

      setCPU的屬性名是什麼?-->CPU

      getUPS的屬性名是什麼?-->UPS

      總之,一個類被當作javaBean使用時,JavaBean的屬性是根據方法名推斷出來的,它根本看不到java類內部的成員變量。

      一個符合JavaBean特點的類可以當作普通類一樣進行使用,但把它當JavaBean用可以帶來一些額外的好處:

      在Java EE開發中,經常要使用到JavaBean。很多環境就要求按JavaBean方式進行操作,別人都這麼用和要求這麼做,沒什麼選擇的餘地!

      JDK中提供了對JavaBean進行操作的一些API,這套API就稱爲內省。如果要自己去通過getX方法來訪問私有的x,會有一定難度,而用內省這套api操作JavaBean比用普通類的方式更方便。

 

PropertyDescriptor描述JavaBean通過一對存儲器方法到處的一個屬性。

      MethodgetReadMethod();

      Method getWritMethod();

      得到BeanInfo最好採用“obj.getClass()”方式,而不要採用“類名.class”方式,這樣程序更通用。

      採用遍歷BeanInfo的所有屬性方式來查找和設置某個RefectPoint對象的x屬性。在程序中把一個類當作JavaBean來看,就是調用IntroSpector.getBeanInfo方法,得到的BeanInfo對象封裝了把這個類當作JavaBean看的結果信息。[31]

       BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());

      PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();                                                                

      Object retVal = null;

      for(PropertyDescriptor pd : pds){

         if(pd.getName().equals(propertyName)){

            Method methodGetX = pd.getReadMethod();

            retVal = methodGetX.invoke(pt1);

            break;

         }

      }

    

 

 

beanutils工具包

      使用beanutils工具包操作JavaBean(需導入beanutils工具包和logging日誌包):

      BeanUtils.getProperty(pt1,"x");

      BeanUtils.setProperty(pt1,"x","9");

get屬性時返回的結果爲字符串,set屬性時可以接受任意類型的對象,通常使用字符串。

      /*

      //Java7新特性:

      Map map = {name:"xxx",age:18};

      BeanUtils.setProerty(map,"name","lhm");

      */

PropertyUtils:

      PropertyUtils.getProperty(pt1,"x");

      PropertyUtils.setProperty(pt1,"x",9);

get屬性時返回的結果爲該屬性本來的類型,set屬性時只接受該屬性本來的類型。

BeanUtils.setProperty(pt1, "birthday", "111");//字符串

System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));//getTime--[32]

     

PropertyUtils.setProperty(pt1, "x", 9);//Integer整數。

System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());

get回來的結構爲java.lang.Integer[32]

 

 

註解(Annotation)

@SuppressWarnings

壓制警告。

@Deprecated (過時)

直接在剛纔的類中增加一個方法,並加上@Deprecated標註,在另外一個類中調用這個方法。

@Override

public boolean equals(Reflect other)方法與HashSet結合講解。

 

總結:

1、註解相當於一種標記,在程序中加了註解就等於爲程序打上了某種標記,沒加,則等於沒有某種標記,以後,javac編譯器,開發工具和其他程序可以用反射來了解你的類及各種元素上有無何種標記,看你有什麼標記,就去幹相應的事。標記可以加在包,類,字段,方法,方法的參數以及局部變量上。

2、看java.lang包,可看到JDK中提供的最基本的annotation。

 

註解的應用結構:

註解類:

@interface A

{}

應用了“註解類”的類:

@A

Class B

{}

對“應用了‘註解類’的類”進行反射操作的類:

Class C

{

      B.class.isAnnotionPresent(A.class);

      A a =B.class.getAnnotion(A.class);

}

      註解就相當於一個你的源程序中要調用的一個類,要在源程序中應用某個註解,得先準備好了這個註解類,就像你要調用某個累,得先要開發好這個類。

 

自定義註解及其應用

定義一個最簡單的註解:

      public @interfaceMyAnnotation {}

把它加在某個類上:

      @MyAnnotation

      public classAnnotationTest{}

用反射進行測試AnnotationTest的定義上是否有@MyAnnotation

     

@Retention元註解(註解的註解):  

@Retention(RetentionPolicy.RUNTIME) //運行期間都保留                                                                    

@Target({ElementType.METHOD,ElementType.TYPE})

     

      一個註解的生命週期有三個階段。源文件,class文件,內存中的字節碼。設計註解時,在註解上加上@Retention說明,可以分別表示註解的生命週期在哪個階段。默認值是在class階段。

三種取值:

      RetetionPolicy.SOURCE -->  java源文件

      RetetionPolicy.CLASS(默認)  -->  class文件(默認)

      RetetionPolicy.RUNTIME -->  內存中的字節碼

@SuppressWarnings;@Deprecated和@Override這三個註解的屬性值分別爲:

      @SuppressWarnings -->  RetetionPolicy.SOURCE

      @Deprecated -->  RetetionPolicy.RUNTIME

      @Override  --> RetetionPolicy.SOURCE

 

@Target元註解:

      默認值爲任何元素,設置Target等於ElementType.METHOD,原來加在類上的註解就報錯了,改爲用數組方式設置{ElementType.METHOD,ElementType.TYPE}就可以了。

      元註解以及其枚舉屬性值不用記,只要會看jdk提供那幾個基本註解的API幫助文檔的定義或其源代碼,按圖索驥即可查到,或者直接看java.lang.annotation包下面的類。

 

爲註解增加基本屬性

什麼是註解的屬性:

      一個註解相當於一個胸牌,如果你胸前貼了胸牌,就是傳智播客的學生,否則,就不是。如果還想區分出是傳智播客哪個班的學生,這時候可以爲胸牌在增加一個屬性來進行區分。加了屬性的標記效果爲:@MyAnnotation(color="red")

定義基本類型的屬性和應用屬性:

      在註解類中增加String color();

      @MyAnnotation(color="red")

用反射方式獲得註解對應的實例對象後,再通過該對象調用屬性對應的方法:

      MyAnnotation a =

(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);

      System.out.println(a.color());

      可以認爲上面這個@MyAnnotation是MyAnnotaion類的一個實例對象。

爲屬性指定缺省值:

      String color()default "yellow";

·value屬性:

      String value()default "zxx";

       如果註解中有一個名稱爲value的屬性,且你只想設置value屬性(即其他屬性都採用默認值或者你只有一個value屬性),那麼可以省略value=部分。

例如:@MyAnnotation("lhm")。

 

爲註解增加高級屬性

數組類型的屬性

      int []arrayAttr() default {1,2,3};

      @MyAnnotation(arrayAttr={2,3,4})

      如果數組屬性中只有一個元素,這時候屬性值部分可以省略大括號。[35]

枚舉類型的屬性

      EnumTest.TrafficLamplamp();

      @MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)

註解類型的屬性

      MetaAnnotationannotationAttr() default @MetaAnnotation("xxxx");

      @MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )

      可以認爲上面這個@MyAnnotation是MyAnnotaion類的一個實例對象,同樣的道理,可以認爲上面這個@MetaAnnotation是MetaAnnotation類的一個實例對象,調用代碼如下:

      MetaAnnotation ma=  myAnnotation.annotationAttr();

      System.out.println(ma.value());

註解的詳細語法可以通過看java語言規範瞭解,即看java的language specification。

 

@Retention(RetentionPolicy.RUNTIME) //運行期間都保留

@Target({ElementType.METHOD,ElementType.TYPE})

public @interface ItcastAnnotation {

   String color() default "blue";//設置默認值

   String value();//value屬於特殊情況

   int[] arrayAttr() default {2,3,4};//數組

   EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;                                                        

   MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");

}

 

  

泛型


體驗泛型 [36] 

       ArrayList collection1 = new ArrayList();

      collection1.add(1);

      collection1.add(1L);

      collection1.add("abc");

      int i = (Integer)collection1.get(0);//運行時出錯。                                                                                      

 

      Jdk 1.5的集合類希望你在定義集合時,明確表示你要向集合中裝哪種類型的數據,無法加入指定類型以外的數據。

      沒有使用泛型時,只要是對象,不管是什麼類型的對象,都可以存儲進同一個集合中。使用泛型集合,可以將一個集合中的元素限定爲一個特定類型,集合中只能存儲同一個類型的對象,這樣更安全;並且當從集合獲取一個對象時,編譯器也可以知道這個對象的類型,不用進行強制類型轉換,這樣更加方便。

      泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入,編譯器編譯帶類型說明的集合時會去除掉“類型”信息,使程序運行效率不受影響,對於參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。由於編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。

 

//collection3.add("abc");Integer裏面存String,編譯器肯定報錯。採用反射方式。

collection3.getClass().getMethod("add", Object.class).invoke(collection3, 1);

System.out.println("add Integer: "+collection3.get(0));//將1添加進去

collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");

System.out.println("add string: "+collection3.get(1)); //將"abc"添加進去

 

瞭解泛型

ArrayList<E>類定義和ArrayList<Integer>類引用中涉及如下術語:

      整個稱爲ArrayList<E>泛型類型

      ArrayList<E>中的E稱爲類型變量或類型參數

      整個ArrayList<Integer>稱爲參數化的類型(parameterd)parameterization  參數化

      ArrayList<Integer>中的Integer稱爲類型參數的實例實際類型參數

      ArrayList<Integer>中的<>念typeof

      ArrayList稱爲原始類型(什麼都沒有的原始類型)

 

參數化類型與原始類型的兼容性:

      ①參數化類型可以引用一個原始類型的對象,編譯報告警告,例如:

           Collection<String>c = new Vector();//可不可以,不就是編譯器一句話的事嗎?

      ②原始類型可以引用一個參數化類型的對象,編譯報告警告,例如:

           Collection c= new Vector<String>();//原來的方法接受一個集合參數,新的類型也要能傳進去。

 

參數化類型不考慮類型參數的繼承關係:

      Vector<String>v = new Vector<Object>(); //錯誤!不寫<Object>沒錯,寫了就是明知故犯。

      Vector<Object>v = new Vector<String>(); //也錯誤!

編譯器不允許創建泛型變量的數組。即在創建數組實例時,數組的元素不能使用參數化的類型,例如,下面語句有錯誤:

      Vector<Integer>vectorList[] = new Vector<Integer>[10];

思考題:下面的代碼會報錯誤嗎?

      Vector v1 = newVector<String>();

      Vector<Object>v = v1;

不會報錯,因爲編譯時只檢查語法錯誤。

 

 

 

泛型中的?通配符

      問題:定義一個方法,該方法用於打印出任意參數化類型的集合中的所有數據,該方法如何定義呢? [38]

      使用?通配符可以引用其他各種參數化的類型,?通配符定義的變量主要用作引用,可以調用與參數化無關的方法(size()),不能調用與參數化有關的方法。

 

 

泛型中的?通配符的擴展

 

限定通配符的上邊界:(它或者它的子類)

      正確:Vector<? extends Number> x = newVector<Integer>();

      錯誤:Vector<? extends Number> x = newVector<String>();

限定通配符的下邊界:(它或者它的父類)

      正確:Vector<? super Integer> x = newVector<Number>();

      錯誤:Vector<? super Integer> x = newVector<Byte>();

·提示:限定通配符總是包括自己。?只能用作引用,不能用它去給其他變量賦值。


泛型集合

      HashMap集合沒有實現iterable接口,不能迭代。[39]

      Set<Map.Entry<K,V>>entrySet() 

       HashMap<String,Integer> maps = new HashMap<String,Integer>();                                                  

      maps.put("zxx",28);

      maps.put("lhm",35);

      maps.put("flx",33);

     

      Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();

      for(Map.Entry<String, Integer> entry : entrySet){

         System.out.println(entry.getKey() + "::" + entry.getValue());

        } 

 

 

自定義定義泛型

 

·Java的泛型方法沒有C++模板函數功能強大,java中的如下代碼無法通過編譯:

      <T> T add(Tx,T y)

      {

           return (T)(x+y);

           //returnnull;

      }

Java中的泛型類型(或者泛型)類似於C++中的模版,但是這種相似性僅限於表面,Java語言中的泛型基本上完全是在編譯器中實現,用於編譯器執行類型檢查和類型推斷,然後生成普通的非泛型的字節碼,這種實現技術稱爲擦除(erasure)(編譯器使用泛型類型信息保證類型安全,然後在生成字節碼之前將其清除)。這是因爲拓展虛擬機指令集來支持泛型被認爲是無法接收的,這會爲Java廠商升級其JVM造成難以逾越的障礙。所以,Java的泛型採用了可以完全在編譯器中實現的擦除方法。

 

      用於放置泛型的類型參數的尖括號應出現在方法的其他所有修飾符之後和在方法的返回類型之前,也就是緊鄰返回值之前。按照慣例,類型參數通常用單個大寫字母表示。

交換數組中的兩個元素的位置的泛型方法語法定義如下:

swap(new String[]{"abc","hijk","itcast"},1,2);

 

private static <T> void swap(T[] a,int i,int j){                                                                                              

      T tmp = a[i];

      a[i] = a[j];

      a[j] = tmp;

   }

 

      只有引用類型才能作爲泛型方法的實際參數,swap(new int[]{1,2,3,4,5},3,4);語句會報告編譯錯誤。     

      除了在應用泛型時可以使用extends限定符,在定義泛型時也可以使用extends限定符,例如,Class.getAnnotation()方法的定義。並且可以用&來指定多個邊界,如<V extends Serializable& cloneable> void method(){}

      普通方法、構造方法和靜態方法中都可以使用泛型。

      也可以用類型變量表示異常,稱爲參數化的異常,可以用於方法的throws/throw列表中,但是不能用於catch子句中。

      在泛型中可以同時有多個類型參數,在定義它們的尖括號中用逗號分,例如:[40]

public static <K,V> V getValue(K key) { return map.get(key);}

 

 

類型參數的類型推斷

[41]

      編譯器判斷泛型方法的實際類型參數的過程稱爲類型推斷,類型推斷是相對於知覺推斷的,其實現方法是一種非常複雜的過程。

根據調用泛型方法時實際傳遞的參數類型或返回值的類型來推斷,具體規則如下:

      1、當某個類型變量只在整個參數列表中的所有參數和返回值中的一處被應用了,那麼根據調用方法時該處的實際應用類型來確定,這很容易憑着感覺推斷出來,即直接根據調用方法時傳遞的參數類型或返回值來決定泛型參數的類型,例如:

      swap(newString[3],3,4)-->static <E> void swap(E[] a, int i, int j)

      2、當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型都對應同一種類型來確定,這很容易憑着感覺推斷出來,例如:

      add(3,5)-->static<T> T add(T a, T b)

      3、當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型對應到了不同的類型,且沒有使用返回值,這時候取多個參數中的最大交集類型,例如,下面語句實際對應的類型就是Number了,編譯沒問題,只是運行時出問題:

      fill(newInteger[3],3.5f)-->static <T> void fill(T[] a, T v)

      4、當某個類型變量在整個參數列表中的所有參數和返回值中的多處被應用了,如果調用方法時這多處的實際應用類型對應到了不同的類型, 並且使用返回值,這時候優先考慮返回值的類型,例如,下面語句實際對應的類型就是Integer了,編譯將報告錯誤,將變量x的類型改爲float,對比eclipse報告的錯誤提示,接着再將變量x類型改爲Number,則沒有了錯誤:

      int x=(3,3.5f)-->static <T> T add(T a, T b)

      5、參數類型的類型推斷具有傳遞性,下面第一種情況推斷實際參數類型爲Object,編譯沒有問題,而第二種情況則根據參數化的Vector類實例將類型變量直接確定爲String類型,編譯將出現問題:

      copy(newInteger[5],new String[5])-->static <T> void copy(T[] a,T[]  b);

      copy(newVector<String>(), new Integer[5])-->static <T> voidcopy(Collection<T> a , T[] b);

 

 

 

定義泛型的類型

      在類身上定義泛型。

DAO[data access object] 

CRUDCreate Read Update Delete增刪改查  JavaEE 數據庫的添刪改查

 

      如果類的實例對象中的多處都要用到同一個泛型參數,即這些地方引用的泛型類型要保持同一個實際類型時,這時候就要採用泛型類型的方式進行定義,也就是類級別的泛型,語法格式如下:

public class GenericDao<T>

   {

      private T field1;

      public void save(T obj){}                                                               

      public T getById(int id){}

   }

 

      類級別的泛型是根據引用該類名時指定的類型信息來參數化類型變量的,例如,如下兩種方式都可以:

      GenericDao<String>dao = null;

      newgenericDao<String>();

注意:

1、在對泛型類型進行參數化時,類型參數的實例必須是引用類型,不能是基本類型。

2、當一個變量被聲明爲泛型時,只能被實例變量、方法和內部類調用,而不能被靜態變量和靜態方法調用。因爲靜態成員是被所有參數化的類所共享的,所以靜態成員不應該有類級別的類型參數。靜態方法不能有類級別的類型參數。自己獨立使用泛型。

 

問題:類中只有一個方法需要使用泛型,是使用類級別的泛型,還是使用方法級別的泛型?

           使用類級別的泛型。

 

通過反射獲得泛型的實際類型參數

高難度知識點[43]

示例代碼         

Class GenericalReflection

   {

      private Vector<Date> dates = new Vector<Date>();

      public void setDates(Vector<Date> dates)

      {

         this.dates = dates;

      }

      public static void main(String[] args)

      {

         Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric", Vector.class);

         ParameterizedType pType = (ParameterizedType)(methodApply .getGenericParameterTypes())[0];

         System.out.println("setDates(" + ((Class) pType.getRawType()).getName() + "<" + ((Class) (pType.getActualTypeArguments()[0])).getName() + ">)" );

      }

   }

 

public static void main(String[] args) throws Exception

 {

       //<Date> v1 = new Vector<Date>();

     

      Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);

      Type[] types = applyMethod.getGenericParameterTypes();//Parameter:參數

      ParameterizedType pType = (ParameterizedType)types[0];//參數化的類型

      System.out.println(pType.getRawType());//獲取原始的類型

      System.out.println(pType.getActualTypeArguments()[0]);//獲取實際類型參數        

   }

    

   public static void applyVector(Vector<Date> v1){

     

   }

//通過反射,利用方法獲取泛型的實際類型參數  

 

 

類加載器

      Java虛擬機中可以安裝多個類加載器,系統默認三個主要類加載器,每個類負責加載特定位置的類:

      BootStrap(不是Java類,鑲嵌在JVM內核中,用C++編寫的二進制代碼)

      ExtClassLoader

      AppClassLoader

      類加載器本身也是Java類,因爲其他是java類的類加載器本身也要被類加載器加載,顯然必須有第一個類加載器不是不是java類,這正是BootStrap。

 

      Java虛擬機中的所有類裝載器採用具有父子關係的樹形結構進行組織,在實例化每個類裝載器對象時,需要爲其指定一個父級類裝載器對象或者默認採用系統類裝載器爲其父級類加載。

 

類加載器之間的父子關係和管轄範圍


編寫自己的類加載器:

      自定義的類加載器的必須繼承ClassLoader

      覆蓋loadClass  findClass方法 defineClass方法

 

 

代理

 

代理的概念與作用[49]

·生活中的代理

·程序中的代理

      1、要爲已存在的多個具有相同接口的目標類的各個方法增加一些系統功能,例如,異常處理、日誌、計算方法的運行時間、事務管理、等等,你準備如何做?

      2、編寫一個與目標類具有相同接口的代理類,代理類的每個方法調用目標類的相同方法,並在調用方法時加上系統功能的代碼。

      3、如果採用工廠模式和配置文件的方式進行管理,則不需要修改客戶端程序,在配置文件中配置是使用目標類、還是代理類,這樣以後很容易切換,譬如,想要日誌功能時就配置代理類,否則配置目標類,這樣,增加系統功能很容易,以後運行一段時間後,又想去掉系統功能也很容易。

 

 

 

AOP面向方面的編程(Aspect oriented program)

 

      系統中存在交叉業務,一個交叉業務就是要切入到系統中的一個方面;

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

     

      method1 method2method3 

      {   {  {  

      -----------------------切面 

      ... ... ... 

      -----------------------切面 

      }   }  } 

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

     

      -----------------------切面 

      func1   func2  func3 

      {   {  { 

      ... ... ... 

      }   }  } 

      -----------------------切面 

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

 

動態代理技術

      要爲系統中的各種接口的類增加代理功能,那將需要太多的代理類,全部採用靜態代理方式,將是一件非常麻煩的事情!寫成百上千個代理類太累!

      JVM可以在運行期動態生成出類的字節碼,這種動態生成的類往往被用作代理類,即動態代理類。

      JVM生成的動態類必須實現一個或多個接口,所以,JVM生成的動態類只能用作具有相同接口的目標類的代理。

      CGLIB庫可以動態生成一個類的子類,一個類的子類也可以用作該類的代理,所以,如果要爲一個沒有實現接口的類生成動態代理類,那麼可以使用CGLIB庫。

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

      1、在調用目標方法之前

      2、在調用目標方法之後

      3、.在調用目標方法前後

      4、在處理目標方法異常的catch塊中

      總結思考:讓jvm創建動態類及其實例對象,需要給它提供哪些信息?

三個方面:

      1、生成的類中有哪些方法,通過讓其實現哪些接口的方式進行告知;

      2、產生的類字節碼必須有個一個關聯的類加載器對象;

      3、生成的類中的方法的代碼是怎樣的,也得由我們提供。把我們的代碼寫在一個約定好了接口對象的方法中,把對象傳給它,它調用我的方法,即相當於插入了我的代碼。提供執行代碼的對象就是那個InvocationHandler對象,它是在創建動態類的實例對象的構造方法時傳遞進去的。在上面的InvocationHandler對象的invoke方法中加一點代碼,就可以看到這些代碼被調用運行了。

      用Proxy.newInstance方法直接一步就創建出代理對象。

 

      動態生成的類實現了Collection接口(可以實現若干接口),生成的類有Collection接口中的所有方法和一個如下接受InvocationHandler參數的構造方法。

      構造方法接受一個InvocationHandler對象,接受對象了要幹什麼用呢?該方法內部的代碼會是怎樣的呢?

      實現Collection接口的動態類中的各個方法的代碼又是怎樣的呢?InvocationHandler接口中定義的invoke方法接受的三個參數又是什麼意思?說明如下:

Client程序調用objProxy.add(“abc”)方法時,涉及三要素:

objProxy對象、add方法、“abc”參數

 

Class Proxy$ {add(Object object) {return handler.invoke(Object proxy,Method method, Object[] args);}} 

 

      分析先前打印動態類的實例對象時,結果爲什麼會是null呢?調用有基本類型返回值的方法時爲什麼會出現NullPointerException異常?

      分析爲什麼動態類的實例對象的getClass()方法返回了正確結果呢?

      調用調用代理對象的從Object類繼承的hashCode, equals, 或toString這幾個方法時,代理對象將調用請求轉發給InvocationHandler對象,對於其他方法,則不轉發調用請求。

 

讓動態生成的類成爲目標類的代理

 

怎樣將目標類傳進去?

1、直接在InvocationHandler實現類中創建目標類的實例對象,可以看運行效果和加入日誌代碼,但沒有實際意義。

2、爲InvocationHandler實現類注入目標類的實例對象,不能採用匿名內部類的形式了。

3、讓匿名的InvocationHandler實現類訪問外面方法中的目標類實例對象的final類型的引用變量。

      將創建代理的過程改爲一種更優雅的方式,eclipse重構出一個getProxy方法綁定接收目標同時返回代理對象,讓調用者更懶惰,更方便,調用者甚至不用接觸任何代理的API。

      將系統功能代碼模塊化,即將切面代碼也改爲通過參數形式提供,怎樣把要執行的系統功能代碼以參數形式提供?

1、把要執行的代碼裝到一個對象的某個方法裏,然後把這個對象作爲參數傳遞,接收者只要調用這個對象的方法,即等於執行了外界提供的代碼!

2、爲bind方法增加一個Advice參數。

 

 

實現AOP功能的封裝與配置

     

對於JavaBean必須要有一個不帶參數的構造方法。

      工廠類BeanFactory負責創建目標類或代理類的實例對象,並通過配置文件實現切換。其getBean方法根據參數字符串返回一個相應的實例對象,如果參數字符串在配置文件中對應的類名不是ProxyFactoryBean,則直接返回該類的實例對象,否則,返回該類實例對象的getProxy方法返回的對象。

 

BeanFactory的構造方法接收代表配置文件的輸入流對象,配置文件格式如下:

           #xxx=java.util.ArrayList

           xxx=cn.itcast.ProxyFactoryBean

           xxx.target=java.util.ArrayList

           xxx.advice=cn.itcast.MyAdvice

ProxyFacotryBean充當封裝生成動態代理的工廠,需要爲工廠類提供哪些配置參數信息?

目標和通知。

 

Spring兩大核心: Bean工廠和AOP框架。

 


---------------------- android培訓java培訓、期待與您交流! ----------------------

詳細請查看:http://edu.csdn.net/heima



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