Bytecode Manipulation with a Java Agent and Byte Buddy

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. Bytecode Manipulation with a Java Agent and Byte Buddy Koichi Sakata PONOS Corporation
2. About Me • • • • Koichi Sakata (阪田 浩一) KanJava JUG Leader Java Champion PONOS Corporation 2 #oc1bcm
3. Intended Audience • Those who want to start manipulating bytecode • Not for experts 3 #oc1bcm
4. Java Bytecode .java javac • Java Code .class • Bytecode Gets loaded by JVM 4 #oc1bcm
5. What is bytecode manipulation? • Editing bytecode in the class files – Add – Delete – Replace 5 #oc1bcm
6. Why do we manipulate? It's fun! 6 #oc1bcm
7. Not Only Fun • To change code at runtime – Generate Entity objects in Hibernate – Create mock objects in Mockito 7 #oc1bcm
8. Bytecode Manipulation • Hard to manipulate by hand... – A class file is a binary file – JVM has a verification process 8 #oc1bcm
9. Bytecode Manipulation Libraries • • • • • • Byte Buddy Byteman Javassist cglib ASM Apache Commons BCEL 9 #oc1bcm
10. Byte Buddy • • • • http://bytebuddy.net Easy-to-use API Java 11 Support Duke's Choice Award 2015 Winner 10 #oc1bcm
11. Byte Buddy • Usage in major libraries – Hibernate – Mockito – Jackson – etc. 11 #oc1bcm
12. Hello World in Byte Buddy Class<?> subClass = new ByteBuddy() .subclass(Object.class) .method(ElementMatchers.named("toString")) .intercept(FixedValue.value ("Hello World!" )) .make() .load(getClass().getClassLoader()) .getLoaded(); 12 #oc1bcm
13. Hello World in Byte Buddy Class<?> subClass = new ByteBuddy() .subclass(Object.class) .method(ElementMatchers.named("toString")) 1. Create a subclass of Object 2. Choose the toString() 13 #oc1bcm
14. Hello World in Byte Buddy .method(ElementMatchers.named("toString")) .intercept(FixedValue.value ("Hello World!" )) 1. Intercept toString() to return a fixed string value "Hello World" 14 #oc1bcm
15. Hello World in Byte Buddy .make() .load(getClass().getClassLoader()) .getLoaded(); 1. Make a new type (unloaded class) 2. Load the type 3. Get the new loaded class 15 #oc1bcm
16. Hello World in Byte Buddy Class<?> subClass = new ByteBuddy() .subclass(Object.class) .method(ElementMatchers.named("toString")) .intercept(FixedValue.value ("Hello World!" )) .make() .load(getClass().getClassLoader()) .getLoaded(); 16 #oc1bcm
17. Demo 1 • Run Hello World – See a generated class 17 #oc1bcm
18. Demo code is available • http://bit.ly/oc1bcm – github.com/jyukutyo/SampleCodeForByt eBuddySeeesion 18 #oc1bcm
19. Usage in Mockito • SubclassBytecodeGenerator – mockClass() 19 #oc1bcm
20. How to use BM? 1. Directly in your application 2. At build time via Maven/Gradle 3. With Java Agent – No need for changing code – Detachable 20 #oc1bcm
21. Java Agent? • Do espionage activities in your application – Profiling – Logging – Changing the target application itself 21 #oc1bcm
22. Java Agent • Instrument programs running on the JVM – JSR 163: JavaTM Platform Profiling Architecture – JVM calls Java Agent method with proper timing 22 #oc1bcm
23. Create a Java Agent • Just define methods – premain() Method – agentmain() Method 23 #oc1bcm
24. Create a Java Agent • premain() Method – Gets called before main() method • agentmain() Method – Gets called when the agent is attached after JVM startup 24 #oc1bcm
25. With Java Agent We can run bytecode manipulation code at runtime 25 #oc1bcm
26. 26 #oc1bcm
27. premain() public static void premain(String agentArgs, Instrumentation inst) • Provides opportunity to modify classes before loading – Instrumentation class has useful methods 27 #oc1bcm
28. MANIFEST.MF Premain-Class: com.s.logging.LoggingAgent Boot-Class-Path: byte-buddy-1.8.22.jar 28 #oc1bcm
29. Demo 2 • Create a simple Java Agent 29 #oc1bcm
30. Another Example • Add outputting logs before executing a particular method on a particular class 30 #oc1bcm
31. Example 1. Manipulate bytecode before loading – Use premain() 2. Replace the class file – Use ByteBuddy – Not create a subclass 31 #oc1bcm
32. Target Class public class Target { public void foo() { System.out.println("foo!"); } public static void main(String[] args) { new Target().foo(); } } 32 #oc1bcm
33. Replace the class TypePool pool = TypePool.Default.ofClassPath(); return new ByteBuddy() .rebase(pool.describe("c.Target").resolve(), ClassFileLocator.ForClassLoader .ofClassPath() ) 33 #oc1bcm
34. Replace the class return new ByteBuddy() .rebase(...) • Replace the original implementation with new one • Save the original implementation under a different method name 34 #oc1bcm
35. Enhance Existing Method • ByteBuddy#subclass() – Override the method • ByteBuddy#redefine() – Replace the implementation • ByteBuddy#rebase() – Copy&Rename the method – Replace the implementation 35 #oc1bcm
36. Replace the class TypePool pool = TypePool.Default.ofClassPath(); return new ByteBuddy() .rebase(pool.describe("c.Target") .resolve(), • Can't use a class literal – JVM loads class before changing the class • Use the TypePool class and describe() 36 #oc1bcm
37. Replace the class .rebase(pool.describe(...).resolve(), ClassFileLocator.ForClassLoader .ofClassPath() ) • Allow to locate a class file • ofClassPath() - Scan the running application's class path 37 #oc1bcm
38. Intercept the method return new ByteBuddy() ... .method(ElementMatchers.named("foo")) .intercept( MethodDelegation .to(LoggingInterceptor.class) .andThen(SuperMethodCall.INSTANCE) ) 38 #oc1bcm
39. Delegation • MethodDelegation.to() – Specify a Class to delegate • A best match method is used 39 #oc1bcm
40. Intercept the method return new ByteBuddy() ... .method(ElementMatchers.named("foo")) .intercept( MethodDelegation .to(LoggingInterceptor.class) .andThen(SuperMethodCall.INSTANCE) ) 40 #oc1bcm
41. Interceptor Class public static class LoggingInterceptor { public static void intercept( @Origin Method m) { ...println("Call " + m.getName()...); } } 41 #oc1bcm
42. Delegated Method • Annotations for Parameter – @Origin • Reference to the method/constructor 42 #oc1bcm
43. Delegated Method • Annotations for Parameter – @This/@Super • Dynamic/Super type's instance – @Argument, @AllArguments – @SuperCall • Callable/Runnable instance of the super's implementation of the method 43 #oc1bcm
44. premain() public static void premain(String args, Instrumentation inst) { inst.addTransformer( new ClassFileTransformer() { @Override public byte[] transform(...) { 44 #oc1bcm
45. transform() public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { // Call ByteBuddy } 45 #oc1bcm
46. transform() public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) • className – Class name to be loaded • classfileBuffer – Class file content 46 #oc1bcm
47. transform() public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) • Return the class file content 47 #oc1bcm
48. Demo 3 • Add outputting logs before executing a method 48 #oc1bcm
49. 49 #oc1bcm
50. Features in ByteBuddy • Add fields/methods • Manipulate stack • Generate CONDY bytecode – Constant Dynamic – JEP 309 • Simplify Java Agent with AgentBuilder • lots of other features 50 #oc1bcm
51. Byte Buddy's AgentBuilder • Simplify the Java Agent implementation 51 #oc1bcm
52. AgentBuilder new AgentBuilder.Default() .type(ElementMatchers.named("com.sakatakoic hi.logging.Target")) .transform(new AgentBuilder.Transformer() { ... }) .installOn(inst); 52 #oc1bcm
53. AgentBuilder 1. Use AgentBuilder – instead of ByteBuddy class 2. Use AgentBuilder.Transformer – instead of ClassFileTransformer 3. Call installOn() 53 #oc1bcm
54. Transformer .transform(new AgentBuilder.Transformer() { public DynamicType.Builder<?> transform( DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) { return builder .method(ElementMatch...named("foo")) .intercept(...); } }) 54 #oc1bcm
55. Without -javaagent • -javaagent option can be removed – Call ByteBuddyAgent.install() – Call AgentBuilder#installOnByteBuddyAgent() • instead of installOn() – Need to add byte-buddy-agent.jar 55 #oc1bcm
56. Without -javaagent ByteBuddyAgent.install(); new AgentBuilder.Default() .type(ElementMatchers.named("com.sakatakoic hi.logging.Target")) .transform(new AgentBuilder.Transformer() { ... }) .installOnByteBuddyAgent(); 56 #oc1bcm
57. Demo 4 • Add outputting logs without -javaagent option 57 #oc1bcm
58. Wrap Up • BM is fun and close to us! – Many libraries are using • Application Examples – Hot Swap – Advanced Dynamic Proxy 58 #oc1bcm

Home - Wiki
Copyright © 2011-2024 iteam. Current version is 2.139.0. UTC+08:00, 2024-12-24 04:20
浙ICP备14020137号-1 $Map of visitor$