A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© Spring up 中级黑马   /  2012-11-3 18:47  /  1599 人查看  /  2 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

哪哥们能清楚的帮助我很好的理解代理类?谢谢!

2 个回复

倒序浏览
不清楚,嘿嘿
回复 使用道具 举报
通过张老师的视频总结了一下,希望对你有帮助
生活中的代理,济南人想买海尔的电脑,可以从济南海尔代理商那里购买,也可以直接跑到青岛来买海尔的电脑。主体的业务目标是一样的,就是买了一台电脑,从代理处购买既可以省时也省力,虽然有一些代理费,但是比自己跑到青岛买还是会便宜一些。

应用程序中的代理。编写一个和目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法是加上系统功能代码。如果采用工厂模式和配置文件的方式进行管理,在配置文件中使用目标类,还是代理类,这样很容易切换。譬如,想要日志功能是就配置代理类,否则配置目标类,这样增加系统功能很容易。张老师在这里体现了面向对象的一个很重要的思想,即面向接口的编程。

交叉业务编程问题即为面向方面编程(Aspect oriented Program  AOP),AOP的目标就是要使用交叉业务模块化。可以采用将切面代码移到原始方法周围,这与直接在方法中编写切面代码的效果一样。例如学生服务,课程服务,教室服务到都用到了,安全,事务,日志的功能,这些安全、事务、日志就是交叉业务。

动态代理技术。JVM可以再运行期动态生成类的字节码,这种动态生成的类,往往被用作代理类。JVM生成的动态类必须实现一个或多个接口,所以java生成的动态类只能用作具有相同接口的目标的目标类的代理。

CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类动态生成代理类,那么可以使用CGLIB库。系统功能添加代码的位置:调用目标方法之前,调用目标方法之后,调用目标方法前后,处理目标方法异常模块catch中。

创建动态类及其查看方法列表信息代码如下:

//获取代理类
  Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
  System.out.println(clazzProxy1.getName());
  //创建缓冲区
  StringBuilder sb = new StringBuilder();
  //取得构造方法集合,并遍历该集合
  Constructor[] constructors =clazzProxy1.getConstructors();
  for (Constructor construtor : constructors) {
   //System.out.println(constructor.getName());
    //sb=new StringBuilder();
   sb.append(construtor.getName());
   sb.append('(');
   Class[] clazzParams=construtor.getParameterTypes();
   for (Class clazzParam : clazzParams) {
    sb.append(clazzParam.getName()).append(',');
   }
   if(clazzParams.length!=0&&clazzParams!=null){
    sb.deleteCharAt(sb.length()-1);
   }
  
   sb.append(')');
   //System.out.println(sb.toString());
   sb.append("\r\n");
  }
  //取得代理类的方法集合,并遍历
  Method[] methods =clazzProxy1.getMethods();
  for (Method method : methods) {
   //System.out.println(constructor.getName());
    //sb=new StringBuilder();
   sb.append(method.getName());
   sb.append('(');
   Class[] clazzParams=method.getParameterTypes();
   for (Class clazzParam : clazzParams) {
    sb.append(clazzParam.getName()).append(',');
   }
   if(clazzParams.length!=0&&clazzParams!=null){
    sb.deleteCharAt(sb.length()-1);
   }
  
   sb.append(')');
   //System.out.println(sb.toString());
   sb.append("\r\n");
  }
  System.out.println(sb.toString());

对于方法的遍历,因为代理类要有一个接口类,所以所以方法含有接口中的所有的方法。

创建动态类的实例及其调用方法

Collection foo3 = (Collection) Proxy.newProxyInstance(Collection.class
    .getClassLoader(), new Class[] { Collection.class },
    new InvocationHandler() {
     ArrayList target = new ArrayList();

     @Override
     public Object invoke(Object proxy, Method method,
       Object[] args) throws Throwable {
     long startTime=System.currentTimeMillis();
      Object retValue = method.invoke(target, args);
      long endTime=System.currentTimeMillis();
      System.out.println("Runtime:"+(endTime-startTime));      return retValue;
     }
    });

  foo3.add("zhangsan");
  System.out.println(foo3.toString());
  System.out.println(foo3.size());

动态生成类每次操作都会调用invocationHandler实现类的invoke方法,他会把自己的操作方法和参数传给InvocationHandler方法,真正的操作在InvocationHandler中实现,所以将target作为成员变量。 在代理实例上的java.lang.Object 中声明的hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的invoke 方法,也就是说InvocationHander实例覆盖hashCode ,equals和toString方法覆盖式有效的。这个目标类必须实现了接口的所有的方法,代理类就是很好的体现了面向接口编程。

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马