Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Having fun with
Javassist
JEEConf 2017
@antonarhipov
whoami
Anton Arhipov
@antonarhipov
Javassist inside!
@Entity

@Table(name = "owners")

public class Owner extends Person {

@Column(name = "address")

@NotEmpty

private Strin...
public class JavassistLazyInitializer
extends BasicLazyInitializer
implements MethodHandler {
final JavassistLazyInitializ...
public class JavassistLazyInitializer
extends BasicLazyInitializer
implements MethodHandler {
final JavassistLazyInitializ...
The main use case for
bytecode generation
in Java frameworks
is to generate proxies
Why?
Programming model (AOP, ORM, итд)
Monitoring agent (NewRelic, XRebel)
Specialized debuggers
Developer tools (JRebel)
...
Javassist 101
www.javassist.org
API
ClassPool
CtClass
CtClass
CtClass
CtClass
CtField
CtMethod
CtConst
CtMethod
insertBefore
insertAfter
instrument
https://speakerdeck.com/antonarhipov
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ct = cp.makeClass...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
cp.appendClassPath(new Cl...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(...
CtMethod foo = …
foo.insertBefore(…);
foo.insertAfter(…);
Can implement tracing
… or add logging
CtMethod foo = …
foo.insertBefore(…);
foo.insertAfter(…);
… or implement AOP
CtMethod foo = …
foo.insertBefore(…);
foo.insertAfter(…);
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(NewExpr e)
throws CannotCompileException {
...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(NewExpr e)
throws CannotCompileException {
...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(NewExpr e)
throws CannotCompileException {
...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(NewExpr e)
throws CannotCompileException {
...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(MethodCall m)
throws CannotCompileException...
CtMethod foo = …
foo.instrument(new ExprEditor() {
@Override

public void edit(FieldAccess f)
throws CannotCompileExceptio...
java.lang.ClassFormatError: LVTT entry
for 'callbackTypes' in class file
com/google/inject/internal/ProxyFactory
does not ...
This slide is intentionally left blank
Java agents
Java Agent
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
public class Age...
ClassFileTransformer
new ClassFileTransformer() {
public byte[] transform(ClassLoader loader,
String className,
Class<?> c...
new ClassFileTransformer() {
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
P...
new ClassFileTransformer() {
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
P...
new ClassFileTransformer() {
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
P...
https://github.com/zeroturnaround/callspy
Javassist in
JRebel
core
Spring
plugin
Hibernate
plugin
EJB
plugin
JRebel
core
Spring
plugin
Hibernate
plugin
EJB
pluginjrebel.jar
Reloads
classes
JRebel
core
Spring
plugin
Hibernate
plugin
EJB
plugin
Notifies
plugins
JRebel
core
Spring
plugin
Hibernate
plugin
EJB
plugin
JRebel
core
Spring
plugin
Hibernate
plugin
EJB
plugin
Refresh
configurations
Javassist lives here
Spring
plugin
Hibernate
plugin
EJB
plugin
JRebel
core
Spring
Hibernate
OpenEJB
JRebel
core
Spring
plugin
Hibernate
plugin
EJB
plugin
class Framework {
public void configure(){
//…

}
}
CtClass framework
= cp.get("com.zt.Framework");
framework.addInterface...
https://github.com/antonarhipov/jpoint
HowItWorks
This slide is intentionally left blank
Your task
Javassist
https://speakerdeck.com/antonarhipov
http://www.slideshare.net/arhan
@antonarhipov
anton@zeroturnaround.com
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
Upcoming SlideShare
Loading in …5
×

JEEConf 2017 - Having fun with Javassist

2,614 views

Published on

Javassist makes Java bytecode manipulation simple. At ZeroTurnaround we use Javassist a lot to implement the integrations for our tools.

In this talk we will go through the examples of how Javassist can be applied to alter the applications behavior and do all kind of fun stuff with it.

Why is it interesting? Because while trying to do unusual things in Java, you learn much more about the language and the platform itself and learning about Javassist will actually make you a better Java developer!

Published in: Technology
  • Be the first to comment

  • Be the first to like this

JEEConf 2017 - Having fun with Javassist

  1. 1. Having fun with Javassist JEEConf 2017 @antonarhipov
  2. 2. whoami Anton Arhipov @antonarhipov Javassist inside!
  3. 3. @Entity
 @Table(name = "owners")
 public class Owner extends Person {
 @Column(name = "address")
 @NotEmpty
 private String address;
 
 @Column(name = "city")
 @NotEmpty
 private String city;
 
 @Column(name = "telephone")
 @NotEmpty
 @Digits(fraction = 0, integer = 10)
 private String telephone;
 
 @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
 private Set<Pet> pets;
  4. 4. public class JavassistLazyInitializer extends BasicLazyInitializer implements MethodHandler { final JavassistLazyInitializer instance = new JavassistLazyInitializer(…); 
 ProxyFactory factory = new ProxyFactory();
 factory.setSuperclass(interfaces.length == 1?persistentClass:null);
 factory.setInterfaces(interfaces);
 factory.setFilter(FINALIZE_FILTER); 
 Class cl = factory.createClass();
 final HibernateProxy proxy = (HibernateProxy) cl.newInstance();
 ((ProxyObject)proxy).setHandler(instance);
 instance.constructed = true;
 return proxy;
  5. 5. public class JavassistLazyInitializer extends BasicLazyInitializer implements MethodHandler { final JavassistLazyInitializer instance = new JavassistLazyInitializer(…); 
 ProxyFactory factory = new ProxyFactory();
 factory.setSuperclass(interfaces.length == 1?persistentClass:null);
 factory.setInterfaces(interfaces);
 factory.setFilter(FINALIZE_FILTER); 
 Class cl = factory.createClass();
 final HibernateProxy proxy = (HibernateProxy) cl.newInstance();
 ((ProxyObject)proxy).setHandler(instance);
 instance.constructed = true;
 return proxy;
  6. 6. The main use case for bytecode generation in Java frameworks is to generate proxies
  7. 7. Why? Programming model (AOP, ORM, итд) Monitoring agent (NewRelic, XRebel) Specialized debuggers Developer tools (JRebel) Obfuscation (Proguard)
  8. 8. Javassist 101 www.javassist.org
  9. 9. API ClassPool CtClass CtClass CtClass CtClass CtField CtMethod CtConst CtMethod insertBefore insertAfter instrument
  10. 10. https://speakerdeck.com/antonarhipov
  11. 11. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); }
  12. 12. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } ClassPool cp = new ClassPool(null); cp.appendSystemPath();
  13. 13. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } public class A extends Clazz { 
 public A() {
 }
 }
  14. 14. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); }
  15. 15. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } mars:output anton$ javap -c com/zt/A.class public class com.zt.A extends com.zt.Clazz { public com.zt.A(); Code: 0: aload_0 1: invokespecial #10 4: return
  16. 16. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass("com.zt.A", cp.get("com.zt.Clazz")); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } Can generate classes from metadata at build time
  17. 17. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); cp.appendClassPath(new ClassPath(){ … }); CtClass ct = cp.get("com.zt.A"); CtMethod[] methods = ct.getMethods();
 for (CtMethod method : methods) {
 //… } ct.writeFile("/output"); } … or you can post process the compiled classes
  18. 18. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "()V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 }
  19. 19. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "()V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 }
  20. 20. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "()V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } public void foo() { 
 }
  21. 21. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } public void foo(String s) { 
 }
  22. 22. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lja foo.insertBefore("System.out.println();"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } Descriptors might get quite long ;)
  23. 23. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println($1)"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } $1, $2, $3 — local variables $0 — this for non-static methods
  24. 24. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println($1)"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 } Exception in thread "main" javassist.CannotCompileException: [source error] ; is missing at javassist.CtBehavior.insertBefore(CtBehavior.java:774) at javassist.CtBehavior.insertBefore(CtBehavior.java:734) at com.zt.basics.Ex.main(Ex.java:35)
  25. 25. public static void main(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get("com.zt.A"); CtMethod foo = ctClass.getMethod("foo", "(Ljava/lang/String;)V"); foo.insertBefore("System.out.println($1);"); Class c = ctClass.toClass();
 A a = (A) c.newInstance();
 a.foo("Hello");
 }
  26. 26. CtMethod foo = … foo.insertBefore(…); foo.insertAfter(…); Can implement tracing
  27. 27. … or add logging CtMethod foo = … foo.insertBefore(…); foo.insertAfter(…);
  28. 28. … or implement AOP CtMethod foo = … foo.insertBefore(…); foo.insertAfter(…);
  29. 29. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(NewExpr e) throws CannotCompileException {
 e.replace("{" +
 "$_ = $proceed($$);" +
 "System.out.println($_);" +
 "}");
 } });
  30. 30. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(NewExpr e) throws CannotCompileException {
 e.replace("{" +
 "$_ = $proceed($$);" +
 "System.out.println($_);" +
 "}");
 } });
  31. 31. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(NewExpr e) throws CannotCompileException {
 e.replace("{" +
 "$_ = $proceed($$);" +
 "System.out.println($_);" +
 "}");
 } }); Intercept new instances
  32. 32. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(NewExpr e) throws CannotCompileException {
 e.replace("{" +
 "$_ = $proceed($$);" +
 "System.out.println($_);" +
 "}");
 } }); Intercept new instances
  33. 33. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(MethodCall m) throws CannotCompileException {
 if(m.getMethodName().contains("println")) {
 m.replace("{}");
 } } }); Remove unwanted invocations
  34. 34. CtMethod foo = … foo.instrument(new ExprEditor() { @Override
 public void edit(FieldAccess f) throws CannotCompileException {
 if (f.isWriter()) {
 CtField field = f.getField();
 String setterName = findSetter(field);
 f.replace("{" + "$0." + setterName + "($$);" + "}");
 } } }); Replace direct field access with setter calls
  35. 35. java.lang.ClassFormatError: LVTT entry for 'callbackTypes' in class file com/google/inject/internal/ProxyFactory does not match any LVT entry
  36. 36. This slide is intentionally left blank
  37. 37. Java agents
  38. 38. Java Agent import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; public class Agent { public static void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { // here be code }); } } META-INF/MANIFEST.MF Premain-Class: Agent $> java –javaagent:agent.jar application.Main
  39. 39. ClassFileTransformer new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); } }
  40. 40. new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); } } ClassFileTransformer
  41. 41. new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); } } ClassFileTransformer
  42. 42. new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); } } ClassFileTransformer
  43. 43. https://github.com/zeroturnaround/callspy
  44. 44. Javassist in
  45. 45. JRebel core Spring plugin Hibernate plugin EJB plugin
  46. 46. JRebel core Spring plugin Hibernate plugin EJB pluginjrebel.jar
  47. 47. Reloads classes JRebel core Spring plugin Hibernate plugin EJB plugin
  48. 48. Notifies plugins JRebel core Spring plugin Hibernate plugin EJB plugin
  49. 49. JRebel core Spring plugin Hibernate plugin EJB plugin Refresh configurations
  50. 50. Javassist lives here Spring plugin Hibernate plugin EJB plugin JRebel core
  51. 51. Spring Hibernate OpenEJB JRebel core Spring plugin Hibernate plugin EJB plugin
  52. 52. class Framework { public void configure(){ //…
 } } CtClass framework = cp.get("com.zt.Framework"); framework.addInterface( cp.get("com.zt.jrebel.Listener")); class Framework implements Listener { public void configure(){
 } } framework.addMethod( CtNewMethod.make( "public void onEvent(){" + " configure();" + "}", framework ));
  53. 53. https://github.com/antonarhipov/jpoint HowItWorks
  54. 54. This slide is intentionally left blank
  55. 55. Your task Javassist
  56. 56. https://speakerdeck.com/antonarhipov http://www.slideshare.net/arhan @antonarhipov anton@zeroturnaround.com

×