HOW TO:构造Java类

在这篇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中的命令以尝试示例。

参考:操作方法 在我们的JCG合作伙伴 Raji Sankar的反射博客上定型Java类

翻译自: https://www.javacodegeeks.com/2013/11/how-to-stereotyping-a-java-class.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值