如何在使用新运算符的同时动态加载基类的子类

时间:2018-09-06 10:11:57

标签: java reflection instrumentation java-bytecode-asm bytecode-manipulation

我有一个文件 TestInstrumentation.java 如下-

public class TestInstrumentation {
    public static void main(String args[]) throws InterruptedException {
        Lion l = new Lion();
        l.runLion();
    }
}

有一个类 Lion.java 如下-

public class Lion {
    String str = "abc";
    public void runLion() throws InterruptedException {
        System.out.println("Lion is going to run........");
    }
}

还有另一个类 LionExt.java 如下-

public class LionExt extends Lion {
    String str = "xyz";
    String str1 = "xyz";
    @Override
    public void runLion() throws InterruptedException {
        //super.runLion();
        System.out.println("LionExt is going to run........" + str + "--" + str1 + "++");
        sayHello();
    }

    public void sayHello() {
        System.out.println("OK");
    }
}

我希望TestInstrumentation.java中的行Lion l = new Lion();返回LionExt而不是Lion的实例,而不更改TestInstrumentation.java的源代码

这意味着我需要使用一些库来动态加载Test类的相应子类,而不是创建新实例的类本身。

为此,我使用了Java Instrumentation API和Javaassist API,并且我的Instrumentation转换器类如下所示-

public class DurationTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className,
        Class classBeingRedefined, ProtectionDomain protectionDomain,
        byte[] classfileBuffer) throws IllegalClassFormatException {
    byte[] byteCode = classfileBuffer;

    if (className.equals("com/javapapers/java/instrumentation/Lion")) {
        System.out.println("Instrumenting......");
        try {
            ClassPool classPool = ClassPool.getDefault();
            CtClass ctClassold = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
            CtClass ctClassnew = classPool.getCtClass("com.javapapers.java.instrumentation.LionExt");
            CtField[] fields = ctClassnew.getDeclaredFields();
            for(CtField field : fields) {
                if(!Arrays.asList(Arrays.stream(ctClassold.getDeclaredFields()).map(m -> m.getName()).toArray()).contains(field.getName()))
                    ctClassold.getClassFile2().addField2(field.getFieldInfo2());
            }
            CtMethod[] methods = ctClassnew.getDeclaredMethods();
            for (CtMethod method : methods) {
                if(Arrays.asList(Arrays.stream(ctClassold.getDeclaredMethods()).map(m -> m.getName()).toArray()).contains(method.getName()))
                    ctClassold.getDeclaredMethod(method.getName()).setBody(method, new ClassMap());
                else ctClassold.getClassFile2().addMethod2(method.getMethodInfo2());
            }
            byteCode = ctClassold.toBytecode();
            ctClassold.detach();
            System.out.println("Instrumentation complete.");
        } catch (Throwable ex) {
            System.out.println("Exception: " + ex);
            ex.printStackTrace();
        }
    }
    return byteCode;
    }
}

我已经在代理类的premain方法中添加了转换器-

public class DurationAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("Executing premain.........");
        inst.addTransformer(new DurationTransformer());
    }
}

我在覆盖转换方法中所做的就是将所有方法和子类的字段复制到超类,而下面是文件的输出-

Executing premain.........
Instrumenting......
Instrumentation complete.
LionExt is going to run........abc--null++
OK

因此有效!除了不太一样!

您看到用子类方法主体覆盖超类不是一个好主意,并且您不能在任何子类方法中使用super()调用,并且字段的值不会生效。

使用此库或其他任何库,还有其他方法可以做我想做的事吗?

0 个答案:

没有答案