使用ASM對JAVA class file進行修改的技術 — 添加類成員

來源:互聯網
上載者:User

使用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 支援Regex,並且可以為NULL,都為NULL時表示添加所有Fields;

 

寫好後一個小問題:

如果是靜態變數並且由賦值的時候發現只copy了聲明部分,並未拷貝賦值部分,這個是啥原因呢?

原來是忘記拷貝了cinit()函數而引起的,不過這個屬於下一部分要講的內容了。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.