黑马程序员 - 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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值