在这篇HowTo帖子中,我将展示如何将一个类与另一个类一起定型。 为什么这有用?
- 当您的项目中发生大量BCI时,让每个开发人员编写BCI代码都是不明智的。
- 首先,这不会抽象出所使用的BCI库。
- 鉴于Java不支持多重继承,构造型可用于实现多重继承而无需委派。 在此处查看多个继承选项。
- 存在诸如配置文件之类的代码方面,仅在测试代码时才需要提供。 生产代码最好不要被调试代码所困扰。 在这种情况下,可以通过完全改变用于加载类的类加载器来使用构造型,一种添加配置文件代码,另一种不添加配置文件代码。
立体打字意味着……
假设有一个界面:
public interface PerfInterface
{
public void start(String nm);
public void end();
public String getValue(String value);
}
您为此接口编写一个实现:
import java.util.Stack;
public class PerfTemplate implements PerfInterface
{
private Stack _stats;
public void start(String nm)
{
PerfStats stat = new PerfStats();
stat.start(nm);
if (_stats == null)
_stats = new Stack();
_stats.push(stat);
}
public void end()
{
try
{
PerfStats stat = (PerfStats)_stats.pop();
stat.end();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public String getValue(String val)
{
return "PerfTemplate:Modified:" + val;
}
}
您希望所有需要实现该接口的类都使用上述实现,而无需开发人员对其进行实际编码。 在运行时,您希望类适应这种行为。 例如,您要将一个类编码为:
public class ClassToStereoType
{
.....
}
哪个没有实现PerfInterface。 但是,为了提高性能而运行它时,您希望该类为:
public class ClassToStereoType implements PerfInterface
{
.....
}
实现了PerfInterface的所有功能。 这称为定型。
如何定型?
在这里,我们将使用asm库进行原型化。 我们将遵循与“ 运行时BCI ”中相同的步骤。 我们将从必须刻板印象的类中创建一个ClassNode对象,如下所示:
InputStream nstr = new FileInputStream("PerfTemplate.class");
ClassReader n = new ClassReader(nstr);
ClassNode cn = new ClassNode();
n.accept(cn, ClassReader.EXPAND_FRAMES);
在这里,我们阅读PerfTemplate.class类,并将其接受到ClassNode中,该类节点现在包含PerfTemplate类中的所有字段和方法。
我们将编写一个重写visitEnd的ClassVisitor,以从创建的ClassNode中添加字段和方法。
public void visitEnd()
{
System.out.println("In visit End. Adding Fields");
for (Iterator it = _cn.fields.iterator(); it.hasNext();)
{
((FieldNode) it.next()).accept(cv);
}
for(Iterator it = _cn.methods.iterator(); it.hasNext();)
{
MethodNode mn = (MethodNode) it.next();
if (!mn.name.equals("")) //ignore constructor
{
String[] exceptions = new String[mn.exceptions.size()];
mn.exceptions.toArray(exceptions);
MethodVisitor mv = cv.visitMethod( mn.access, mn.name, mn.desc, mn.signature, exceptions);
mn.instructions.resetLabels();
mn.accept(new RemappingMethodAdapter( mn.access, mn.desc, mv, new SimpleRemapper(_cn.name, _name)));
}
} super.visitEnd();
}
在上面的代码中,我们遍历字段并将其添加到我们正在修改的类(即ClassToStereoType)中。 添加方法时,应注意确保将对PerfTemplate类的所有引用都修改为ClassToStereoType。 为此,我们使用RemappingMethodAdapter,它是asm提供的类。
为了将接口从PerfTemplate添加到ClassToStereoType,我们重写了visit方法。 在这里,我们将接口从ClassNode添加到当前类。
public void visit (int version, int access, String name, String signature, String superName, String[] interfaces)
{
System.out.println("Class Name is: " + name + ":" + signature + ":" + superName);
int len = 0;
List ndeints = _cn.interfaces;
if (interfaces != null) len = interfaces.length;
String[] modinterfaces = new String[len + ndeints.size()];
int cnt = 0;
for (cnt = 0; (interfaces != null) && ( cnt < interfaces.length); cnt++)
{
modinterfaces[cnt] = interfaces[cnt];
}
for (String inter : ndeints)
modinterfaces[cnt++] = inter;
cv.visit(version, Opcodes.ACC_PUBLIC, name, signature, superName, modinterfaces);
_name = name;
}
在上面的代码中,我们将通过调用_cn.interfaces获得的来自ClassNode的所有接口附加到ClassToStereoType的接口。 我们使用此接口的修改列表访问该类。 这样可以确保在已加载的类中实现接口。
现在,通过这些更改实现的ClassVisitor可用于修改类加载器中的字节以构造类。
该HOWTO的代码可以在这里找到。 运行compiletst.sh中的命令以尝试示例。
翻译自: https://www.javacodegeeks.com/2013/11/how-to-stereotyping-a-java-class.html