概述
使用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()函数而引起的,不过这个属于下一部分要讲的内容了。
最后
以上就是无情跳跳糖为你收集整理的使用ASM对JAVA class file进行修改的技术 -- 添加类成员的全部内容,希望文章能够帮你解决使用ASM对JAVA class file进行修改的技术 -- 添加类成员所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复