黑马程序员-学习日记24(java高新技术 5 )

来源:互联网 发布:上海普旭网络 编辑:程序博客网 时间:2024/06/05 16:23

 

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

 

 

创建动态类的实例对象及调用其方法
 1.接口 InvocationHandler(java.lang.reflect)
  1)InvocationHandler 是代理实例的调用处理程序 实现的接口。 每个代理实例都具有一个关联的
  调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的
  invoke 方法。
  2)方法摘要
  Object invoke(Object proxy, Method method, Object[] args)
    在代理实例上处理方法调用并返回结果。
 2.eclipse使用技巧之实现接口中的方法:
  在子类MyInvocationHandler1--->Add unimplemented methods
 3.注意:
  若一个对象的打印显示为null,说明它的toString()方法返回为null,因为要是对象不存在而为null的话,
  会报空指针异常,打印为null的对象若调用有返回值的方法,就会报java.lang.NullPointerException异常。

52.完成InvocationHandler对象的内部功能
 1.每个代理实例都具有一个关联的调用处理程序(即InvocationHandler接口的实现类)。对代理实例调用方法时,
   将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 所以每调用一个代理实例的方法,
   都会先去找InvocationHandler的invoke方法。在invoke方法内调用方法的具体实现代码为:
   Object retVal = method.invoke(target,args);

53.分析InvocationHandler对象的运行原理
 猜想分析动态生成的类的内部代码:
 1.动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和
   一个如下接受InvocationHandler参数的构造方法。
 2.构造方法接收一个invocationHandler对象,接收对象了要干什么?该方法内部的代码会是怎样的呢?
  $Proxy0 implements Collection
  {
   InvocationHandler handler;
   public $Proxy0(InvocationHandler handler)
   {
    this.handler = handler;
   }
  }
 3.实现的Collection接口中的各个方法的代码又是怎样的呢?
  //生成的Collection接口中的方法的运行原理
   int size()
   {
    return handler.invoke(this,this.getClass().getMethod("size"),null);
   }
   void clear()
   {
    handler.invoke(this,this.getClass().getMethod("clear"),null);
   }
 4.InvocationHandler接口中定义的invoke方法接收的三个参数又是什么意思?图解说明如下:
  Client程序调用objProxy.add("abc")方法时,涉及三要素:objProxy对象,add方法,"abc"参数
   
   Class Proxy$
   {
    add(Object object)
    {
     return handler.invoke(Object proxy,Method method,Object[] args);
    }
   }
 5.分析先前打印动态类的空例对象时,结果为什么会是null呢?调用有基本类型返回值的方法为什么
   会出现NullPointerException异常?
     因为每次调用代理实例的方法,都会先找InvocationHandler的invoke方法,proxy1.size()返回值
  是Integer,而invoke返回的却是null,故报异常。
 6.分析为什么动态类的实例对象的getClass()方法返回了正确结果?
   System.out.println(prxoy3.getClass().getName());
   打印结果是:$Proxy0(而按刚才的分析,应该打印的是ArrayList)

  调用代理实例对像从Object类继承的hashCode,equals,或toString这几个方法时,代理对象将调用
  请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
 7.注意:
  1)将return retVal改成return method.invoke(proxy,args);这样便是死循环。同时也验证了
   在哪个代理上设置的参数。
  2)Object obj = proxy3.add("zxx");
   invoke返回值便是handler的返回值,也就是代理实例调用方法的返回值。

54.总结分析动态代理类的设计原理与结构
  让动态生成的类成为目标类的代理:
  
 1.分析动态代理的工作原理图如:汇集6-54-动态代理的工作原理图.
 2.怎样将目标类传进去?
  1)直接在InvocationHandler实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,
  但没有实际意义。
  2)为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了。
  3)让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量。
 3.将创建代理的过程必为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接收目标同时
   返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API。
 4.将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码
   参数形式提供?
  1)把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用
  这个对象的方法,即等于执行了外界提供的代码!
  2)为bind方法增中一个Advice参数。
 
55.编写可生成代理和插入通告的通用方法
 1.知识点:
  1)方法里的内部类只能访问加final的局部变量。
  2)eclipse使用技巧之重构方法:
  选中代码-->Refactor-->Extract Method(抽取方法)-->名称:getProxy-->OK
  3)希望你执行的系统功能(切面),这个接口的名称一般设成Advice
  4)class类中的方法:
   Class<?>[] getInterfaces()
    确定此对象所表示的类或接口实现的接口。

56.实现类似spring的可配置的AOP框架
 1.实现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)ProxyFactoryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
  <1>目标
  <2>通知
  4)编写客户端应用:
  <1>编写实现Advice接口的类和在配置文件中进行配置
  <2>调用BeanFactory获取对象
 2.对于设计思想,刚开始要在记事本中敲下如下代码和配置文件内容的方式进行比划:
  Object obj = BeanFactor.getBean("xxx");
  ..............
  config.properties
 3.eclipse使用技巧
  1)在eclipse中抓异常:
  选中代码-->右键-->Surround With try/catch
  2)先写好一个类,后运用该类的一个未创建的方法,之后在eslipse中自动生成该方法:
     Create method getProxy() in type ProxyFactoryBean
 4.知识点
  1)JavaBean必须有一个不带参数的构造方法
  2)配置文件一般都是properties格式的。
  3)Properties类中方法:
   String getProperty(String key)
    用指定的键在此属性列表中搜索属性。
   void load(InputStream inStream)
    从输入流中读取属性列表(键和元素对)。
  4)Class类中方法:
   T newInstance()
    创建此 Class 对象所表示的类的一个新实例。
  InputStream getResourceAsStream(String name)
    查找具有给定名称的资源。
 5.需要创建的类:
  BeanFactory.java(产生JavaBean的工厂,根据传入的名字,创建JavaBean对象)
  ProxyFactoryBean.java(创建代理的工厂,本身也是个JavaBean)
  测试类:AopFrameworkTest.java
  配置文件:config.properties(类型为File,该文件可配置是否要用代理)

 

 

以下是我的程序代码:

 

/*

对象调用toString方法返回了null,;

类 Proxy
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些
  方法创建的所有动态代理类的超类。

static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
          返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。

利用JVM的API动态的生成类,一定要告诉它一个接口,

 

接口 InvocationHandler

InvocationHandler 是代理实例的调用处理程序 实现的接口。

每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,
将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。


//我们成功的获取了一个动态类,并用它去创建了一个实例对象,动态类的构造方法要接收一个InvocationHandler的实例对象。
//所以要构造时,要准备一个它的对象,

总结思考:让JVM创建动态类及其实例对象,需要给它提供哪些信息。
三个方面:1 ,生成的类中有哪些方法,通过让其实现哪些接口的方式时行告知。
     2 ,产生的类字节码必须有一个关联的类加载器对象。
     3 ,生成的类中的方法的代码是怎样的,也得由我们提供,把我们的代码
     写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即
     相当于插入了我的代码,提供执行代码的对象就是那个InvocationHandler对象,
   它是在创建动态类的实例对象的构造方法时传递进去的,在上面的InvocationHandler
   对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。
   4 ,用Proxy.newInstance方法直接一步就创建出代理对象。


*/

 

 以下是我的程序代码:

 

 

import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.lang.reflect.InvocationHandler;
import java.util.ArrayList;


class Proxy1{
 public static void main(String args[])throws Exception{
  //从结果中看出它只有一个构造方法,而接收一个InvocationHandler的字节码。
  //生成的动态类有构造方法,它只有一个有参的构造方法,
  Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
  System.out.println(clazzProxy1.getName());
  System.out.println("begin--- constructor ----list\r\n");
  Constructor[] constructors = clazzProxy1.getConstructors();
  for(Constructor constructor : constructors){
   String name = constructor.getName();
   StringBuilder sBuilder = new StringBuilder(name);
   sBuilder.append("(");
   Class[] clazzParams = constructor.getParameterTypes();
   for(Class clazzParam : clazzParams){
    //clazzParam.getName();
    sBuilder.append(clazzParam.getName()).append(",");
   }
   if(clazzParams!=null && clazzParams.length!=0){
    sBuilder.deleteCharAt(sBuilder.length()-1);
   }
   sBuilder.append(")");
   System.out.println(sBuilder);
  }
  System.out.println("begin--- Method ----list\r\n");
  Method[] methods = clazzProxy1.getMethods();
  for(Method method : methods){
   String name = method.getName();
   StringBuilder sBuilder = new StringBuilder(name);
   sBuilder.append("(");
   Class[] clazzParams = method.getParameterTypes();
   for(Class clazzParam : clazzParams){
    clazzParam.getName();
    sBuilder.append(clazzParam.getName()).append(",");
   }
   if(clazzParams!=null && clazzParams.length!=0){
    sBuilder.deleteCharAt(sBuilder.length()-1);
   }
   sBuilder.append(")");
   System.out.println(sBuilder);
  }
  System.out.println("begin--- create ----instance .....\r\n");
  //这句话不行,因为它调用的是无参的构造方法而这字节码对象中没有无参的构造方法。
  //clazzProxy1.newInstance();
  Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
  class MyInvocationHandler implements InvocationHandler{
   public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
    return null; 
   }
  }
  //里面需要接收一个参数,但该参数是一个接口,无法直接new,
  Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler());
  //打印的结果是null,说明这个对象没有创建成功,或者是该对象的toString方法返回了null;
  //我们用toString方法试一下就知道,如果是没有创建功能的话,用该对象调用方法
  //定会报空指针异常,如果不报错,说明toString方法返回的是null。
   proxy1.clear();//没有返回值的方法我可以调用成功,
   //proxy1.size();//有返回值 的方法我不能调用成功,会报异常。
   Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
    public Object invoke(Object proxy,Method metho,Object[] args)throws Throwable{
     return null;
    }
   });
  Collection proxy3 = (Collection)Proxy.newProxyInstance(
   Collection.class.getClassLoader(),
   new Class[]{Collection.class},//这里没用可变参数是因为,可变参数必须出现在参数列表的最后。
   new InvocationHandler(){//匿名内部类
    ArrayList target = new ArrayList();
    public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
     //ArrayList target = new ArrayList();
     long beginTime = System.currentTimeMillis();
     Object retVal = method.invoke(target,args);
     //System.out.println(target.getClass().getName());
     long endTime = System.currentTimeMillis();
     System.out.println(method.getName()+"  running time of "+(endTime-beginTime)+"  "+retVal);
     return retVal;
     //return method.invoke(proxy,args);
    }
   }
   );
   //每调用生成的方法它都会调用invoke的方法,都会创建各自的Arraylist.
   proxy3.add("zxx");
   proxy3.add("lhm");
   proxy3.add("bxd");
   System.out.println(proxy3.size());
   System.out.println(proxy3.getClass().getName());
   //继承自Object的方法只对三个方法进行开发,交给Handler,其它的方法将不交给它。
   //System.out.println(ArrayList.class);
 }
}

 

 

///////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

class Proxy2{
 public static void main(String args[])throws Exception{
  //从结果中看出它只有一个构造方法,而接收一个InvocationHandler的字节码。
  //生成的动态类有构造方法,它只有一个有参的构造方法,
  Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
  System.out.println(clazzProxy1.getName());
  System.out.println("begin--- constructor ----list\r\n");
  Constructor[] constructors = clazzProxy1.getConstructors();
  for(Constructor constructor : constructors){
   String name = constructor.getName();
   StringBuilder sBuilder = new StringBuilder(name);
   sBuilder.append("(");
   Class[] clazzParams = constructor.getParameterTypes();
   for(Class clazzParam : clazzParams){
    clazzParam.getName();
    sBuilder.append(clazzParam.getName()).append(",");
   }
   if(clazzParams!=null && clazzParams.length!=0){
    sBuilder.deleteCharAt(sBuilder.length()-1);
   }
   sBuilder.append(")");
   System.out.println(sBuilder);
  }
  System.out.println("begin--- Method ----list\r\n");
  Method[] methods = clazzProxy1.getMethods();
  for(Method method : methods){
   String name = method.getName();
   StringBuilder sBuilder = new StringBuilder(name);
   sBuilder.append("(");
   Class[] clazzParams = method.getParameterTypes();
   for(Class clazzParam : clazzParams){
    clazzParam.getName();
    sBuilder.append(clazzParam.getName()).append(",");
   }
   if(clazzParams!=null && clazzParams.length!=0){
    sBuilder.deleteCharAt(sBuilder.length()-1);
   }
   sBuilder.append(")");
   System.out.println(sBuilder);
  }
  System.out.println("begin--- create ----instance .....\r\n");
  //这句话不行,因为它调用的是无参的构造方法而这字节码对象中没有无能的构造方法。
  //clazzProxy1.newInstance();
  Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
  class MyInvocationHandler implements InvocationHandler{
   public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
    return null; 
   }
  }
  //里面需要接收一个参数,但该参数是一个接口,无法直接new,
  Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler());
  //打印的结果是null,说明这个对象没有创建成功,或者是该对象的toString方法返回了null;
  //我们用toString方法试一下就知道,如果是没有创建功能的话,用该对象调用方法
  //定会报空指针异常,如果不报错,说明toString方法返回的是null。
   proxy1.clear();//没有返回值的方法我可以调用成功,
   //proxy1.size();//有返回值 的方法我不能调用成功,会报异常。
   Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
    public Object invoke(Object proxy,Method metho,Object[] args)throws Throwable{
     return null;
    }
   });

 

 

 


   //方法里面的内部类要访问临时局部变量,必须加final。
  final ArrayList target = new ArrayList();
  Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
   //每调用生成的方法它都会调用invoke的方法,都会创建各自的Arraylist.
   proxy3.add("zxx");
   proxy3.add("lhm");
   proxy3.add("bxd");
   System.out.println(proxy3.size());
   //继承自Object的方法只对三个方法进行开发,交给Handler,其它的方法将不交给它。
   //System.out.println(ArrayList.class);
 }
 private static Object getProxy(final Object target,final Advice advice){
    Object proxy3 = Proxy.newProxyInstance(
     target.getClass().getClassLoader(),
     //new Class[]{Collection.class},
     target.getClass().getInterfaces(),
     new InvocationHandler(){//内部类
      public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
       advice.beforeMethod(method);
       Object retVal = method.invoke(target,args);
       advice.afterMethod(method);
       return retVal;
      }
     }
    );
    return proxy3;
   }
}

interface Advice{
 void beforeMethod(Method method);
 void afterMethod(Method method);
}

class MyAdvice implements Advice{
 long beginTime = 0;
 public void afterMethod(Method method){
  System.out.println("从传智播客毕业上班了 ");
  long endTime = System.currentTimeMillis();
  System.out.println(method.getName()+"  running time of "+(endTime-beginTime));  
 }
 public void beforeMethod(Method method){
  System.out.println("到传智播客来学习啦。");
  beginTime = System.currentTimeMillis();
 }
}

 

 

 

/*

*/

 

原创粉丝点击