使用ASM对JAVA class file进行修改的技术 -- 添加类成员

使用ASM包进行Class File修改真是很方便,不过可惜的是ASM不提供现成的工具,那我们就利用它提供的强大的字节码操作能力,自己来做一个吧:

基本思路如下:假设操作类为A, 假设要加的属性为PA,我们为了操作上方便,构造类B,将类B的PA属性加到A上就可以了,有了这个思路即可快速实现如下代码:

---------------------------------------------------------------------------------------------------------------

首先构造Visitor用于添加成员变量PA:

public class BytecodeClassFieldAdder extends ClassAdapter {

 private final List<FieldNode> fieldNodesToAppend;
 
 /**
  * construct for current class
  *
  * @param cv
  * @param fieldNode
  */
 public BytecodeClassFieldAdder(ClassVisitor cv, List<FieldNode> fieldNodes) {
  super(cv);
  this.fieldNodesToAppend = fieldNodes;
 }
 
 /**
  * visit to the end for current class, append to the vistor class
  *
  */
 public void visitEnd() {
  for (FieldNode fn : this.fieldNodesToAppend) {
   fn.accept(cv);
  }
  super.visitEnd();
 }
 

 

---------------------------------------------------------------------------------------------------------------

然后构造从另外一个类B中抽取成员的操作类:

 

public class BytecodeClassFilterUtil implements IBytecodeContainer{
 
 private ClassNode classNode = null;
 
 /**
  * bytecode class filter utility construction
  *
  * @param classFile
  * @param op
  * @throws IOException
  */
 public BytecodeClassFilterUtil(final String classFile) throws IOException {
  FileInputStream fis = new FileInputStream(classFile);
  ClassReader cr = new ClassReader(fis);
  BytecodeClassFilter ca = new BytecodeClassFilter(null);
  cr.accept(ca, ClassReader.EXPAND_FRAMES);
  if (fis != null) {
   fis.close();
  }
 }
 
 /**
  * bytecode class filter utility construction
  *
  * @param classFile
  * @param op
  * @throws IOException
  */
 public BytecodeClassFilterUtil(File classFile) throws IOException {
  FileInputStream fis = new FileInputStream(classFile);
  ClassReader cr = new ClassReader(fis);
  BytecodeClassFilter ca = new BytecodeClassFilter(null);
  cr.accept(ca, ClassReader.EXPAND_FRAMES);
  if (fis != null) {
   fis.close();
  }
 }
 
 /**
  * get a specified class node instance for current bytecode class filter utility
  *
  * @return
  */
 public ClassNode getClassNode() {
  return this.classNode;
 }
 
 /**
  * get a specified field node by a specified name pattern and description pattern
  *
  * @param name
  * @return
  */
 @SuppressWarnings("unchecked")
 public List<FieldNode> getFieldNode(String namePattern, String descPattern) {
  List<FieldNode> returnNodes = new ArrayList<FieldNode>();
  List fields = this.classNode.fields;
  if (fields != null) {
   for (Object ofield : fields) {
    FieldNode field = (FieldNode) ofield;
    boolean blnNameMatch = true;
    boolean blnDescMatch = true;
    if (namePattern != null) {
     blnNameMatch = Pattern.matches(namePattern, field.name);
    }
    if (descPattern != null) {
     blnDescMatch = Pattern.matches(descPattern, field.desc);
    }
    if (blnNameMatch && blnDescMatch) {
     returnNodes.add(field);
    }
   }
  }
  return returnNodes;
 }
 
 /**
  * get a specified method name or a list of them.
  *
  * @param name
  * @param description
  * @return
  */
 @SuppressWarnings("unchecked")
 public List<MethodNode> getMethodNode(String namePattern, String descPattern) {
  List<MethodNode> returnNodes = new ArrayList<MethodNode>();
  List methods = this.classNode.methods;
  if (methods != null) {
   for (Object omethod : methods) {
    MethodNode method = (MethodNode) omethod;
    boolean blnNameMatch = true;
    boolean blnDescMatch = true;
    if (namePattern != null) {
     blnNameMatch = Pattern.matches(namePattern, method.name);
    }
    if (descPattern != null) {
     blnDescMatch = Pattern.matches(descPattern, method.desc);
    }
    if (blnNameMatch && blnDescMatch) {
     returnNodes.add(method);
    }
   }
  }
  return returnNodes;
 }

 /**
  * get all of the field descriptions for a specified class
  *
  * @return
  */
 @SuppressWarnings("unchecked")
 public List<String> getFieldDescription() {
  List<String> descList = new ArrayList<String>();
  List fields = this.classNode.fields;
  if (fields != null) {
   for (Object ofield : fields) {
    FieldNode field = (FieldNode) ofield;
    StringBuilder sb = new StringBuilder();
    sb.append(field.name).append(":").append(field.desc);
    descList.add(sb.toString());
   }
  }
  return descList;
 }
 
 /**
  * get all of the method list for a specified class
  *
  * @return
  */
 @SuppressWarnings("unchecked")
 public List<String> getMethodDescription() {
  List<String> descList = new ArrayList<String>();
  List methods = this.classNode.methods;
  if (methods != null) {
   for (Object omethod : methods) {
    MethodNode method = (MethodNode) omethod;
    StringBuilder sb = new StringBuilder();
    sb.append(method.name).append(":").append(method.desc);
    descList.add(sb.toString());
   }
  }
  return descList;
 }
 
 /**
  * bytecode class filter extend from class adpater class.
  *
  */
 class BytecodeClassFilter extends ClassAdapter {

  // construction call for current class
  public BytecodeClassFilter(final ClassVisitor cv) {
   super(new ClassNode() {
    public void visitEnd() {
     if (cv != null) {
      accept(cv);
     }
    }
   });
  }

  // execute the next operation after this visit ending
  public void visitEnd() {
   classNode = (ClassNode) cv;
  }

 }

}

 

------------------------------------------------------------------------------------------------

构造调用函数,实现属性“转移”功能:

public void addFieldToClass(String src, String des, String combine, String nameFilter, String descFilter)
   throws IOException {
  BytecodeClassFilterUtil util = new BytecodeClassFilterUtil(src);
  List<FieldNode> fields = util.getFieldNode(nameFilter, descFilter);

  // visitor current class
  if (fields.size() == 0) {
   System.out.println("ERROR: No field is chosen out by the filter.");
  } else {
   ClassWriter cw = new ClassWriter(0);
   BytecodeClassFieldAdder adder = new BytecodeClassFieldAdder(cw, fields);
   FileInputStream fis = new FileInputStream(des);
   ClassReader cr = new ClassReader(fis);
   cr.accept(adder, ClassReader.EXPAND_FRAMES); // need to expand frames for current end user
   if (fis != null) {
    fis.close();
   }

   // convert the specified method into current class
   byte[] bytearray = cw.toByteArray();
   FileOutputStream fos = new FileOutputStream(combine);
   fos.write(bytearray);
   fos.flush();
   fos.close();
  }
 }

 

-------------------------------------------------------------------

最后挂接操作界面:

addFieldToClass(sourceFile, targetFile, destFile, nameFilter, descFilter);

需要注意的是: sourceFile = B.class;

                     targetFile = A.class;

                     destFile = 合成后的A.class

nameFilter,descFilter 支持正则表达式,并且可以为NULL,都为NULL时表示添加所有Fields;

 

写好后一个小问题:

如果是静态变量并且由赋值的时候发现只copy了声明部分,并未拷贝赋值部分,这个是啥原因呢?

原来是忘记拷贝了cinit()函数而引起的,不过这个属于下一部分要讲的内容了

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值