黑马程序员------- java高新技术(下)-------

来源:互联网 发布:农业电子商务网络搭建 编辑:程序博客网 时间:2024/06/05 20:51

------- android培训、java培训、期待与您交流! ---------- 

AOP

AOP(Aspect Oriented program,简称AOP)即为面向方面的编程
·系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
{安全、事务、日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务。}
  安全 事务 日志 
StudentService|
CourseService| 
MiscService|
·用具体的程序代码描述交叉业务员:
method1  method2  method3
{  {  {
切面
}  }  }
·交叉业务的编程问题即为面向方面的编程(Aspect Oriented program,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
切面
·使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

 

一:内省->了解JavaBean

 1.JavaBean-->特殊的java类
 2.一个符合JavaBean特点的类可以当做普通类一样来进行使用,也可当做JavaBean方式进行。
 3.用Eclipse将读取属性和设置属性的流水账代码分别抽取成方法:
   3.1:只要调用这个方法,并给这个方法传递一个对象,属性名和设置值,他就能完成属性修改的功能
   3.2:得到BeanInfo最好采用“obj.getClass()”的方式,而不要采用“类名.class”方式,这样程序更通用
 4.采用遍历BeanInfo的所有属性方式来查找和设置某个ReflectPoint对象的x属性,在程序中把一个类当做         JavaBean来看,就是调用IntroSpector.getBeanInfo方法,得到的BeanInfo对象封装了把这个类当做JavaBean    的结果信息。


二:重构-->抽取方法
 eg:   
        PropertyDescriptor pd=new PropertyDescriptor(propertyName,pt1.getClass());
        Method methodGetx=pd.getReadMethod();
        Object reval=methodGetx.invoke(pt1);
        //对这三句重构生成下面的代码        
        Object reval = getProperties(pt1, propertyName);
        private static Object getProperties(ReflectPoint pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd=new PropertyDescriptor(propertyName,pt1.getClass());
        Method methodGetx=pd.getReadMethod();
        Object reval=methodGetx.invoke(pt1);
return reval;
}     
Object value=7; //重构时加入的参数
      
        setProperties(pt1, propertyName, value);
        private static void setProperties(Object pt1, String propertyName,
Object value) throws IntrospectionException,
        IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2=new PropertyDescriptor(propertyName,pt1.getClass());
        Method methodSetx=pd2.getWriteMethod();
        methodSetx.invoke(pt1,value);
}
三:内省综合案例
 eg:private static Object getProperties(ReflectPoint pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
     /*PropertyDescriptor pd=new PropertyDescriptor(propertyName,pt1.getClass());
                Method methodGetx=pd.getReadMethod();
                Object reval=methodGetx.invoke(pt1);*/
              //这部分相当于下面注释部分的简写形式  


     /*BeanInfo beanInfo=Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
Object reval=null;
for(PropertyDescriptor pd:pds)
if(pd.getName().equals(propertyName)) { 
Method methodGetx=pd.getReadMethod();
                reval=methodGetx.invoke(pt1);
                break;
}*/
return reval;
}
四:Beanutils工具包
 1.演示用Eclipse如何加入jar包,先只是引入beanutils包,等程序运行出错后再引入logging包
 2.在内省例子的基础上,用BeanUtils类先get原来设置好的属性,在将其set为一个新值。
   get属性时返回的结果为字符串,set属性时可以接受任意类型的对象,通常使用字符串。 
 3.用PropertyUtils类先get原来设置好的属性,再将其set为一个新值
   get属性时返回的结果为该属性本类的类型,set属性时只接受该属性本来的类型。 
 4.BeanUtils可以自动进行类型转换
   System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());
        BeanUtils.setProperty(pt1, "x","9");
 5.Beanutils的setProperty()和PropertyUtils的setProperty方法的不同之处
   System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());
        BeanUtils.setProperty(pt1, "x","9");        
        System.out.println(pt1.getX());
        
      /*java7的新特性
        Map map=(name:"xxx",age:18);
        BeanUtils.setProperty(map,"name", "1hm");*/
        
        BeanUtils.setProperty(pt1,"birthday.time","111");
        System.out.println(BeanUtils.getProperty(pt1,"birthday.time"));
        
        PropertyUtils.setProperty(pt1, "x",9);
        System.out.println(PropertyUtils.getProperty(pt1, "x").getClass());
五:注解及java提供的几个基本注解
 1.注解相当于一种标记,在程序中加入了注解就等于打上了某种标记,没加,则等于没有某种标记,以后javac     编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无任何标记,看你有什么标记就去干相    应的事。标记可以加在包,类,字段,方法,方法的参数,以及局部变量上。
 2.看java.lang包,可看到JDK中提供的最基本的Annotation。
 3.注解的应用结构图
   @interface A         @A            Class C{
   {            ------>Class B ------> B.Class.isAnnotionPresent(A.class)
   }                   {               A a=B.class.getAnnotion{A.class)
                       }               }


六:自定义注解及其应用
 1.定义一个最简单的注解:public @interface MyAnnotation
 2.把他加在某个类上:@MyAnnotation public class AnnotationTest{}
 3.用反射进行测试AnnotationTest的定义上是否有@MyAnnotation
 4.根据发射测试的问题,引出@Retention元注释,其三种取值:
   RetetionPolicy.SOURCE.RetentionPollcy.class
   RetetionPolicy.RUNTIME;分别对应:java源文件-->class文件-->内存中的字节码
   @Override是给javac看的,属于段代码source阶段  
   @SuppressWarinings保持在编译阶段,给编译器看的

@Deprecated也是给编译器看的
 5.Class只是java中的一种,class的父类为Type
 6.Target的默认值为任何元素,设置Target等于ElementType.METHOD,原来加在类上的注解就报错了,改为用数   组方式设置(ElementType.METHOD,ElementType.TYPE)就可以了
 7.元注解以及其枚举属性值不用记,只要会看jdk提供的那几个基本注解的API帮助文档的定义或启源代码,按图   即可查到,或者直接看java.lang.annotation包下面的类     
 8.注解的返回值人为注解  
 
七:为注解增加统计属性
 1.数组类型的属性
   int[]arrayAttr()default{1,2,3} 
   @MyAnnortation(arrayAttr={2,3,4}) 
   如果数组属性中只有一个元素,这时属性值部分可省略大括号
 2.枚举类型的属性
   EnumTest.TrafficLamp lamp()
   @MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)  

3.注释类型的属性
   MetaAnnotation annotationAttr() default @MetaAnnotation("xxx");
   @MyAnnotation(annotationAttr=@MetaAnnotation("yyy"))
   可以认为上面这个@MyAnnotation是MyAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
   MetaAnnotation ma=myAnnotation.annotationAttr();
   System.out.println(ma.value()); 
 4.注解的详细语法可以通过看java语言规范了解,即看java的languageSpecification 
 eg:package test.oop.Annotation;
   //@ItcastAnnotation()
     @ItcastAnnotation(/*annotationAttr=@MetaAnnotation     ("flx"),*/color="red",value="abc",arrayAttr={1})
     public class AnnotationTest {
     @SuppressWarnings("deprecation")
     //@ItcastAnnotation("abc")
     public static void main(String[] args) {
     // TODO Auto-generated method stub
     System.runFinalizersOnExit(true);
     if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class))
     {
         ItcastAnnotation annotation=(ItcastAnnotation)AnnotationTest.class.getAnnotation        (ItcastAnnotation.class);
System.out.println(annotation.color());     

System.out.println(annotation.value());
System.out.println(annotation.arrayAttr().length);
       //System.out.println( annotation.lamp().nextLamp().name()); 
     }

}}


     import java.lang.annotation.ElementType;
     import java.lang.annotation.Retention;
     import java.lang.annotation.RetentionPolicy;
     import java.lang.annotation.Target;
     import test.oop.meiju.EnumTest;


     /*元注解 
       元数据
       元信息*/
      @Retention(RetentionPolicy.RUNTIME)
    //@Target(ElementType.METHOD)
      @Target(ElementType.TYPE)//这里要想那部分有,就标记那部分,METHOD
    //为方法部分,TYPE为文件头部分
      public @interface ItcastAnnotation {
      String color() default "blue";
      String value();
      int []arrayAttr();   
    //EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED ;
    //MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
    }

八:泛型
 1.泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器     编译带类型说明的集合时会去掉“类型”信息,使程序运行效率不受影响,对应参数化的泛型类型,            getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编    译器,就可以往某个泛型集合中加入其他类型的数据,例如:用反射得到集合,在调用其add方法即可
 2.泛型定义其实就是给不同类中的对象添加一种固定的数据类型,如同C++中的模板Class<T>一样,例如:集合     中的泛型定义 
   eg:ArrayList<String> collection2=new ArrayList<String>();
       collection2.add("abc");
       String element=collection2.get(0);
       
       ArrayList<Integer>collection3=new ArrayList<Integer>();
       System.out.println(collection3.getClass()==collection2.getClass());
   
       collection3.getClass().getMethod("add",Object.class).invoke(collection3,"abc");
       System.out.println(collection3.get(0));
 3.ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语
   3.1:整个成为ArrayList<E>泛型类型
   3.2:ArrayList<E>中的E称为类型变量或类型参数
   3.3:整个ArrayList<Integer>称为参数化的类型
   3.4:ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
   3.5:ArrayList<Integer>中的<>念着typeof
   3.6:ArrayList称为原始类型
 4.参数化类型与原始类型的兼容性

4.1:参数化类型可以引用一个原始类型的对象,编译器报警告,例如:Collection<String>c=new Vector();
   4.2:原始类型可以引用一个参数化类型的对象,编译器报警告,例如:Collection c=new Vector<String>();
 5.参数化类型不考虑类型参数的继承关系
   Vector<String>v=new Vector<Object>(); //错误
   Vector<Object>v=new Vector<String>(); //也错误
 6.编译器不允许创建类型变量的数组,即在创建数组实例时,数组的元素不能使用参数化的类型,例如: 
   Vector<Integer>vectorList[]=new Vector<Integer>[10]; 有错误 
 7.总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用在引用,可以调用与参数化无         关的方法,不能调用与参数化有关的方法
   eg: printCollection(collection3);
       public static void printCollection(Collection<?>collection) {
//collection.add(e);//这个方法不能调用
System.out.println(collection.size());
for (Object obj:collection ) {
                    System.out.println(obj);
}

九:泛型中的?通配符的扩展
 1.限定通配符的上边界:
   正确:Vecotor<?extends Number>x=new Vector<Integer>();
   错误:Vecotor<?extends Number>x=new Vector<String>();
 2.限定通配符的下边界:
   正确:Vecotor<?super Number>x=new Vector<Number>();
   错误:Vecotor<?super Number>x=new Vector<Byte>();
 3.限定通配符总是包括自己
 4.不能把问号给具体的类型,但可以把具体的类型给问号
   eg:  
        String.class.asSubclass(Number.class);
        Class<?>y;
        Class<String>x;
        y=x;
        x=y;


十:泛型综合案例
 1.Map内部的一个类Entry
 2.java泛型集合类
 eg: HashMap<String, Integer>maps=new HashMap<String, Integer>();
      maps.put("xxx", 28);
      maps.put("lhm", 35);
      maps.put("flx", 33);
      Set<Map.Entry<String,Integer>>enter=maps.entrySet();

 for (Map.Entry<String, Integer>en:enter){
        System.out.println(en.getKey()+":"+en.getValue());

十一:由C++的模板函数引入自定义泛型
 1.函数的结构很相似,仅类型不同:
   int add(int x,int y)
   {
      return x+y;
   }
   float add(float x,float y)
  {
      return x+y;
   }
   double add(double x,double y)
  {
      return x+y;
   }
 2.C++用模板函数解决,只写一个通用的方法,它可以适应各种类似,代码如下:
   template<class T>
   T.add(T x,T y)
 {
   return (T) (x+y);
  } 
 3.java的泛型定义方法没有C++模板函数功能强大,java中的如下代码无法通过编译:
   <T>T add(T x,T y){ return (T)(x+y);}
   eg:add(3, 5);
      Number x1=add(3.5,3);
      Object x2=add(3,"abc");        
 4.交换数组中的两个元素的位置的泛型方法语法定义如下:

static <E>void swap(E[]a,int,int){
           E t=a[i]; a[i]=a[j]; a[j]=t;}
 5.用于放置泛型类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回     值之前,按照惯例,类型参数通常用单个大写字母表示
 6.只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3.5),语句会报告编译错误
   eg: add(3, 5);
       Number x1=add(3.5,3);
       Object x2=add(3,"abc");        
       swap(new String[]{"abc","xyz","itcast"},1,2);
     //swap(new int[]{1,3,5,4,5},1,2); 实际类型只能是引用类型,而非实际类型        
 7.除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如:
   Class.getAnnotation()方法的定义,并且可以用&来指定多个边界,如<V extends Serializable &       cloneable>void method(){}
 8.普通方法、静态方法、构造方法中都可以使用泛型。编译器也不允许创建类型变量的数组
 9.也可以用类型变量表示异常,称为参数化的异常,可以用于方法的thows列表中,但是不能用于catch字句中
 10.在泛型中可以同时有多个类型的参数,在定义他们的尖括号中用逗号分,例如:
    public static<k,v>getValue(k key){return map.get(key)} 
 11.泛型对应异常的处理
    private static <T extends Exception> sayHello() throws T {  
    {
    try{
    }catch(Exception e){
     throw(T)e;
    }}
} 

12.实例
  eg:1.泛型方法,自动将Object类型的对象转换成其他类型
        Object obj="abc";
        String x3=AutoConnect(obj);

private static <T>T AutoConnect(Object obj)
{ return (T)obj;}
  eg:2.将任意类型的数组中的所有元素填充为相应类型的某个对象
        private static<T> void fillArray(T[]a,T obj){
for (int i = 0; i < a.length; i++) {
a[i]=obj;
  eg:3.自定义泛型方法的方式打印出任意参数化类型的集合中的所有内容
        public static<T>void printCollection2(Collection<T>collection)
{
System.out.println(collection.size());
for (Object obj:collection ) {
             System.out.println(obj);
}

}
}
十二:类型参数的类型诊断
 1.编译器判断泛型方法的实际类型参数的过程称为类型诊断,类型诊断相对于知觉推断的,其实现方法是一种非   常复杂的过程
 2.根据调用泛型方法时传递的参数类型或返回值的类型来推断,具体规则如下:
   2.1.当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的       实际应用类型来确定,这很容易凭着感觉推断出来,及直接根据调用方法时传递的参数类型或饭后值类型       来决定泛型参数的类型,例如:
       Swap(new String[3],3.4)-->static <E>void swap(E[]a,int i,int j) 
   2.2.当某个类型变量在参数列表中的所有参数和返回值中的多处被应用了,如果通用方法时这多处的实际应用       类型都对应同一种类型来确定,例如:
       add(3,5)-->static<T>T add(T a,T b)
   2.3.当某个类型变量在参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用       类型都对应到了不同的类型,却没有使用返回值,这时读取多个参数中的最大交集类型,例如:
       fill(new Integer[3],3.5)-->static <T>void fill(T[]a,T v)
   2.4.当某个类型变量在参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用       类型都对应到了不同的类型,却使用返回值,这时有些、优选考虑返回值的类型,例如:
       int x=add(3,3.5)-->static<T>T add(T a,T b)//编译器报错
       改为 Number x1=add(3,3.5)将不报错;
   2.5.参数类型的类型诊断具有传递性,下面第一种情况推断实际参数类型为Object,编译器没有问题,而第二       种情况则根据参数化的Vector类实例将类型变量确定为String类型,编译器出现问题
       copy(new Integer[5],new String[5])-->static<T>void copy(T[]a,T[]b);
       copy(new Vector<String>(),new Integer[5])-->static<T>void copy(collection<T>a,T[]b);

十三:自定义泛型类应用
 1.crud:只对数据库进行增删改查
 2.如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型要保持同一个实际类型时,就要      采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式为:
   public class GenericDao<T>{
     private T field1;
     public void save(T obj){}
     public T getById(int id){}
   }
 3.类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,类如,如下两种方式
   GenericDao<String>dao=null;
   new GenericDao<String>();
 4.注意:
   4.1:在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型
   4.2:当一个变量被申明为泛型时。只能被实例变量法和方法调用(还有内部嵌套类型),而不能被静态变量             和静态方法调用,因为静态成员是被所以参数化的类所共享的,所以静态成员不应该有类级别的类型参数
 5.类中只有一个方法需要使用泛型,则使用类级别的 
 eg: //Vector<Data>v1=new Vector<Data>();
        Method applyMethod=GenericTest.class.getMethod("applyVector",Vector.class);
        Type[] types=applyMethod.getGenericParameterTypes();
        ParameterizedType pType=(ParameterizedType)types[0];
        System.out.println(pType.getRawType());
        System.out.println(pType.getActualTypeArguments()[0]);


public static void applyVector(Vector<Data>vi) {

}
十四:类加载器
 1.java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类加载器负责加载指定位置的类:
   为BootStrap,ExtClassLoader,AppClassLoader
 2.类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是
   java类,这正是BootStrac
 3.java虚拟机中的所以类加载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其   制定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载 
 4.类加载器之间的父子关系和管辖范围图
                      BootStrap---------------->JPE/lib/rt.jar      (根)
                      ExtClassLoader----------->JRE/lib/ext/*.jar    (子)复制加载ext目录下的jar文件
   System
   ClassLoader------->AppClassLoader----------->CLASSPATH指定得到所有jar或目录 (孙子)   
              MyClassLoader    iitcastClassLoder-------->传智播客指定的特殊目录
 
   eg:public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(
   ClassLoaderTest.class.getClassLoader().getClass().getName());
        System.out.println(
            System.class.getClassLoader());
        ClassLoader loader=ClassLoaderTest.class.getClassLoader();
        while (loader!=null) {
        System.out.println(loader.getClass().getName()); 

loader=loader.getParent();
}
        System.out.println(loader);
}

十五:类加载器的委托机制
 1.当java虚拟机要加载一个类时,到底该派出哪个类去加载呢
   1.1:首先当前线程的类加载器去加载线程中的第一个类
   1.2:如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B
   1.3:还可以直接调用ClassLoader.LoadClass()方法来指定某个类加载器去加载某个类
 2.每个加载器加载类时,又先委托给上级类加载器
   2.1:当所有祖宗类类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException
       不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,哪有多个儿子,找那个呢?
   2.2:对着类加载器 的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成JRE/lib//ext目录下的
       itcast.jar包中后,运行结果为ExtClassLoader的原因


十六:自定义类加载器
  eg:public class MyClassLoader extends ClassLoader{
      public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String srcPath=args[0];
String destDir=args[1];
FileInputStream fis=new FileInputStream(srcPath);
String destFileName=srcPath.substring(srcPath.lastIndexOf('\\')+1);
String destFilePath=destDir+"\\"+destFileName;
FileOutputStream fos=new FileOutputStream(destFilePath);
Cypher(fis, fos);
fis.close();

fos.close();


}
      private static void Cypher(InputStream ips,OutputStream ops)throws Exception {
int b=-1;
while((b=ips.read())!=-1){
ops.write(b^0xff);
}
}
    
   
      private String classDir;
      protected Class <?>findClass(String name)throws ClassNotFoundException
      {
    String classFileName=classDir+"\\"+name+/*name.substring(name.lastIndexOf('.')+1)*/".class";
        //注释的部分表示用自定义的类加载器加载某类时,是否需要写上包名
    try {
    FileInputStream fis=new FileInputStream(classFileName);
    ByteArrayOutputStream bos=new ByteArrayOutputStream();
    Cypher(fis,bos);

 fis.close();
    System.out.println("aaa");
    byte[]bytes=bos.toByteArray();
    return defineClass(bytes, 0, bytes.length);
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
    return super.findClass(name);
}
      public MyClassLoader(){
    
      }
      public MyClassLoader (String classDir) {
    this.classDir=classDir;
}
      }

装载类:
            Class clazz=new  MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
           //调用时若带有包名,则调用父类的类加载器,若不带包名,则调用自定义的类加载器
   Date d1=(Date)clazz.newInstance();
   System.out.println(d1);   


十七:代理的慨念与作用
 1.程序中的代理
   编写一个与目标类具有相同接口的代理类,它的每个方法调用目标类的相同方法,并在调用方法时加上系统功    能的代码
   如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序
   在配置文件中配置是使用目标类,还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,    否则配置目标类,这样增加系统功能很容易,以后运行一段时间后,又想去调系统功能也容易


十八:AOP:面向方面的编程
     Aop的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写     切面代码的运行效果是一样的
十九:动态代理技术
 1.JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,及动态代理类
 2.JVM生成动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理
 3.CGLB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口   的类生成动态代理类,那么可使用CHLB库
 4.代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果,外,还可以在代理方法的如   下四个位置加上系统功能代码:
   4.1:在调用目标方法之前
   4.2:在调用目标方法之后
   4.3:在调用目标方法前后
   4.4:在处理目标方法异常的catch块中  
  eg:System.out.println("--------begin methods list---------");
   /*$Proxyo() $ProxyO(InvocationEvent ioHandier,int);*/
   Method[] methods=clazzProxy.getMethods();
   for (Method method:methods) {
   String name=method.getName();
   StringBuilder sBuilder=new StringBuilder(name);
   sBuilder.append('[');
   Class[] clazzParams=method.getParameterTypes();
   for (Class clazzParm:clazzParams ) {
   sBuilder.append(clazzParm.getName()).append(',');
}
   if(clazzParams!=null&&clazzParams.length!=0)
   sBuilder.deleteCharAt(sBuilder.length()-1);
   sBuilder.append(']');
   System.out.println(sBuilder.toString());

}

二十:分析JVM动态生成的类
 1.创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数
 2.编码列出动态类中的所有构造方法和参数签名
 3.编码列出动态类中的所有方法和参数签名
 4.创建动态类的实例对象
   4.1:用反射获得构造方法
   4.2:编写一个最简单的invocationHandle类
   4.3:调用构造方法创建动态类的实例对象,并将编写的InvocationHandle类的实例对象传进去
   4.4:打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了          异常
   4.5:将创建动态类的实例对象的代理名改为佚名内部类的形式编写,锻炼大家习惯佚名内部类 
 5.三种创建动态类的实例对象的方法
    方法一:Collection proxy=(Collection)constructor.newInstance(new MyInvocationHander1());
   System.out.println(proxy);
   proxy.clear();
  // proxy.size();
   
    方法二:Collection proxy2=(Collection)constructor.newInstance(new InvocationHandler(){
   public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
      }    
            }); 

二十一:实现AOP功能的封装与配置
 1.工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换,其getBean方法根据参      数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直      接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象
 2.BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
   #xxx=java.util.ArrayList
    xxx=cn.itcast.ProxyFactoryBean
    xxx Target=java.util.ArrayList
    xxx advice=cn.itcast.MyAdvice
 3.ProxyFacotoryBean充当封装生产动态代理的工厂,需要为工厂类提供哪些配置参数信息
   目标,通知
 4.编写客户端应用
   编写实现Adivice接口的类和在配置文件中进行配置
   调用BeanFactory获取对象 
   eg:  public Object getBean(String name){
        String className=props.getProperty(name);
        Object bean=null;
try {
Class clazz = Class.forName(className);
bean = clazz.newInstance();
}
catch (Exception e) {
// TODO: handle exception
      e.printStackTrace();
}

if (bean instanceof ProxyFactoryBean) {
Object proxy=null;
ProxyFactoryBean proxyFactoryBean=(ProxyFactoryBean)bean;
try {
//Advice advice=(Advice) Class.forName((String) props.get(name + ".advice")).newInstance();
Advice advice = (Advice) Class.forName( props.getProperty(name + ".advice")).newInstance();
        //这两句的处理效果一样
//Object target=Class.forName((String) props.get(name + ".advice")).newInstance();
Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
        //这两句的处理效果也一样
proxyFactoryBean.setAdvice(advice);
        proxyFactoryBean.setTarget(target);
proxy = proxyFactoryBean.getProxy();
}
catch (Exception e) {
// TODO: handle exception
  e.printStackTrace();
}
return proxy;
}
return bean;
}

}


0 0