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

本文介绍如何使用ASM库实现向现有类中添加属性的过程。通过构造特定的访问者类和工具类,能够从一个类中抽取属性并将其添加到另一个类中。文中详细展示了具体的实现代码,包括属性提取、类结构修改及最终的类合并。

使用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()函数而引起的,不过这个属于下一部分要讲的内容了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值