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

來源:互聯網
上載者:User

使用ASM包進行Class File修改真是很方便,不過可惜的是ASM不提供現成的方法修改工具,那我們就利用它提供的強大的位元組碼操作能力,自己來做一個吧:

基本思路如下:假設操作類為A, 假設要加的方法為MA,我們為了操作上方便,構造類B,將類B的MA方法加到A上就可以了,有了這個思路即可快速實現如下代碼:

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

首先構造Visitor用於添加方法MA:

public class BytecodeClassMethodAdder extends ClassAdapter {
 
 // method nodes for appending operation
 private final List<MethodNode> methodNodesToAppend;
 
 /**
  * Construction for bytecode class method adder operation
  *
  * @param cv
  * @param methodNodes
  */
 public BytecodeClassMethodAdder(final ClassVisitor cv, List<MethodNode> methodNodes) {
  super(cv);
  
  // all method nodes needed to append for current class
  this.methodNodesToAppend = methodNodes;
 }
 
 /**
  * visit end of this adapter for current class
  *
  */
 @SuppressWarnings("unchecked")
 public void visitEnd() {
  for (MethodNode mn : this.methodNodesToAppend) {
   List exceptions = mn.exceptions;
   String [] earray = null;
   if (exceptions.size() > 0){
    earray = new String[exceptions.size()];
    for (int i=0; i<exceptions.size(); i++){
     String exception = (String)exceptions.get(i);
     earray[i] = exception;
    }
   }
   mn.accept(cv);    //add this method node to the visitor operation
  }
  
  // overload the visiting operation
  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 addMethodToClass(String src, String des, String combine, String nameFilter, String descFilter)
   throws IOException {
  BytecodeClassFilterUtil util = new BytecodeClassFilterUtil(src);
  List<MethodNode> methods = util.getMethodNode(nameFilter, descFilter);
  
  // visitor current class
  if (methods.size() == 0) {
   System.out.println("ERROR: No method is chosen out by the filter.");
  } else {
   ClassWriter cw = new ClassWriter(0);
   BytecodeClassMethodAdder adder = new BytecodeClassMethodAdder(cw, methods);
   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();
  }
 }

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

最後掛接操作介面:

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

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

                     targetFile = A.class;

                     destFile = 合成後的A.class

nameFilter,descFilter 支援Regex,並且可以為NULL,都為NULL時表示添加所有Methods;

 

呵呵,大功告成了,大家可以測試一下,你會發現原來代碼“遷移”如此容易.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.