bytecode操作

来源:互联网 发布:乐视网络电视下载安卓 编辑:程序博客网 时间:2024/06/10 13:07

问题: 调用一个方法传入List of Object  然后根据配置文件去调用Object中的get方法,可是我只有一个List of Map。 map的key 就是get方法所需要调用的 比如map中有name   那个方法要调用的是getName

问题分析: 为了解决问题可有两个途径 1 就是老老实实的将map转化为一个javabean,那么就需要写多个javabean ,实现比较简单,工作量较大。2 修改需要此list的方法,让他能够读取map。

问题解决: 因为所要调用的工具设计的合理,很容易的扩充了原来的功能,专门做了一个读取map的datasource,用几行代码轻松解决。

问题深入:如果调用的工具没有进行如此合理的设计,应该怎么做?

问题继续: 采用分析出来的第一种方法,那就是转化为javabean,避免大工作量就需要写一个代码生成器。

问题缺点:虽然工作量不是十分巨大,但是直接生成大量的代码,不方便日后维护。

期待结果:创建一个类,内部有一Map 成员变量,但是没有getXXX的方法,在调用这种方法时,直接进入某一方法返回map中内容。

问题突破口:记得jdk 1.5的动态代理,开始研究。

新问题出现:动态代理,虽然可以进行方法拦截,但是必须有方法接口,否则无法调用。

寻找出路: 向Michael询问,得到答案:bytecode如asm ,或者aop 如aspectJ

继续分析: aspectJ语法已经忘记,aop方式感觉不能实现。理由如下,aop需要寻找横切点,但是横切点不存在。

查看asm: 官方文档看的云里雾里,示例代码感觉冗长拖沓,除非对class文件格式了解深入,很难快速编写代码。

新的突破:bytecode工具大搜索,找到javassist。 javassist:jboss子项目之一。可以以asm之类直接字节码操作,也可以已源代码级别的操作。查看示例代码,清晰明了。决定采用它来尝试。

资源: http://www.csg.is.titech.ac.jp/~chiba/javassist/ 

           http://www.csg.is.titech.ac.jp/~chiba/javassist/tutorial/tutorial.html

            http://zyl.javaeye.com/blog/53740

代码: 

1  CallClass.java   需要传入list 并且调用list中元素getXXX方法的类

   package com.jeaomachine;

import java.lang.reflect.Method;
import java.util.List;

public class CallClass {
 public static void call(List list) {
  try {
   for (Object o : list) {
    Method m = o.getClass().getMethod("getName", null);
    System.out.println("getName:/t"
      + m.invoke(o, null));
    m = o.getClass().getMethod("getGendar", null);
    System.out.println("getGendar:/t"
      + m.invoke(o, null));
    m = o.getClass().getMethod("getTime", null);
    System.out.println("getTime:/t"
      + m.invoke(o, null));
   }
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
}

2  MainCalling.java  主程序,修改字节码,调用CallClass

package com.jeaomachine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class MainCalling {
 public static void main(String[] args) {
  try {
   CtClass ctClass = ClassPool.getDefault().get(
     "com.jeaomachine.VoClass");

   List<String> strList = new ArrayList<String>();
   strList.add("name");
   strList.add("gendar");
   strList.add("time");
   for (int i = 0; i < strList.size(); i++) {
    String name = (String) strList.get(i);
    String name2 = "";

    name2 = new String("" + name.charAt(0)).toUpperCase()
      + name.substring(1);
    String mname = "public String get" + name2
      + "(){return (String)map.get(/"" + name + "/");}";

    CtMethod mnew = CtNewMethod.make(mname, ctClass);

    ctClass.addMethod(mnew);

   }
   ctClass.toClass();
   VoClass a = new VoClass();

   List<VoClass> list = new ArrayList<VoClass>();
   Map<String, String> map = new HashMap<String, String>();
   map.put("name", "the name");
   map.put("gendar", "the gendar");
   map.put("time", "the time");
   a.setMap(map);
   list.add(a);
   map = new HashMap<String, String>();
   map.put("name", "the name 2");
   a = new VoClass();
   a.setMap(map);
   list.add(a);
   CallClass.call(list);
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
}

 

3 VoClass.java 存放Map的javaBean ,编写时没有getXXX 方法 但是期望被调用到

package com.jeaomachine;

import java.util.Map;

public class VoClass {
 Map map=null;
 
 public void setMap(Map map){
  this.map=map;
 }
 
// public String getName(){
//  return "";
// }
}

代码分析: MainCalling 更改VoClass的字节码,根据map中的值(目前为hardcode)创建getXXX方法 将其加入list并且调用CallClass的call方法  此方法调用list中元素的getXXX方法。

 至此调用不存在的方法的方法终于实现。

运行结果:

getName: the name
getGendar: the gendar
getTime: the time
getName: the name 2
getGendar: null
getTime: null

遇到障碍:使用过程中出现过classCast的异常,即将新的方法加入VoClass的类中时 强制转换newInstance出来的对象时出现。另外会出现重复的VoClass的异常。

解决方法:查看最新javassist版本为3.4 而我使用的是2.4 马上重新下载,编译,运行,一切正常。


 

 

原创粉丝点击