Java Bytecode Manipulation and Java reflection API

2019-04-17 00:18发布

问题:

I recently came across the term "Bytecode manipulation" (what made to look into this, by chance I saw bytecode provider while seeing the logs in an application which used Hibernate) . I also know (a bit) about Java Reflection API.

Are these two concepts similar? What are the difference between them? When to use which?

回答1:

Reflection API allows you to access the information about the members (fields, methods, interfaces, e.t.c.) of classes which are already loaded to the JVM. This API doesn't allow to modify the behaviour of the class except for some basic stuff like calling private methods.

Some examples where Reflection API is applicable

  • Dependency Injection framework can set the dependencies to the private fields of owner objects.
  • You can use reflection to generate equals/hashCode/toString methods for your class without having to enumerate all the fields and modifying these methods when you add new fields or remove existing ones
  • Serialize your class to JSON/XML/Yaml or any other format using Relfection Api again without having to enumerate over all the fields

ByteCode manipulation on the contrary allows you to make any changes you want either to some .class files on your disk or also to the classes which are already loaded to the JVM using Java Agent API

A few examples where bytecode manipulations are applicable:

  • Proxies from Java standard library only supports proxying interfaces; bytecode manipulation allows you to add advices around class methods as well
  • Mock frameworks for unit test allows to substitue the return values of private static methods - it is implemented using bytecode manipulations
  • Profilers wrap each method with time recording code

Here's how it might look

private void foo() {
    long start = System.currentTimeMillis(); // inserted by bytecode manipulation
    Profiler.enterMethod("foo"); // inserted by bytecode manipulation
    try { // inserted by bytecode manipulation
        //  original method code
    } finally { // inserted by bytecode manipulation
        Profiler.exitMethod("foo", System.currentTimeMillis() - start); // inserted by bytecode manipulation
    } // inserted by bytecode manipulation
}