概述
使用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 支持正则表达式,并且可以为NULL,都为NULL时表示添加所有Methods;
呵呵,大功告成了,大家可以测试一下,你会发现原来代码“迁移”如此容易.
最后
以上就是谦让大门为你收集整理的使用ASM对JAVA class file进行修改的技术 -- 添加类方法的全部内容,希望文章能够帮你解决使用ASM对JAVA class file进行修改的技术 -- 添加类方法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复