JVM Bytecode manipulation
When reflection is simply not enough
Piotr Lewandowski
Not an expert
not even a newbie...
Manipulation
is the skillful handling, controlling or using of someone
Bytecode manipulation
- Modify method body
- Replace statements
- Generate classes
instrumentation
It's everywhere
we make Java more like JavaScript
Hot Reload
Java Debugger API since 2002
Works good when
- Modify method body
- No new classes
- No annotation changes
Not good for
- Method definition change
- New fields declarations
- Spring beans changes
Author: Anton Arhipov @ JRebel
class Framework {
public void configure() {
// Configure beans, set-up endpoints
}
}
class Framework implements Listener {
public void configure() {
// ...
}
}
CtClass framework
= cp.get("com.zt.Framework");
framework.addInterface(
cp.get("com.zt.jrebel.Listener"));
framework.addMethod(
CtNewMethod.make(
"public void onEvent() {" +
" configure();" +
"}",
framework
));
class Framework implements Listener {
public void configure() {
// ...
}
public void onEvent() {
configure();
}
}
Javassist
Result
It's useful
and necessary
Monitoring and profiling
Generating classes from metadata
Caching
Obfuscation
Mocking
java.lang.NullPointerException: null
at net.piotrl.NewsService.get(NewsService.java:50)
at net.piotrl.NewsService$$FastClassBySpringCGLIB$$a842897a.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at net.piotrl.NewsService$$EnhancerBySpringCGLIB$$b6cab172.get(<generated>)
at net.piotrl.NewsController.get(NewsController.java:46)
Proxy Class
Reflection
/* Handle how proxy behaves */
InvocationHandler handler = (proxy, method, methodArgs) -> {
if (method.getName().equals("get")) {
return 42;
} else {
throw new UnsupportedOperationException(
"Unsupported method: " + method.getName());
}
/* Create proxy for Map */
Map mapProxy = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { Map.class },
handler
);
/* Result */
proxyInstance.get("hello"); // 42
proxyInstance.put("hello", "world"); // exception
Proxy Class
Reflection
/* Create proxy */
YourEntity mapProxy = (YourEntity) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(),
new Class[] { YourEntity.class },
handler
);
/* Result */
java.lang.IllegalArgumentException:
DynamicProxyTest$1YourEntity is not an interface
@Entity
class YourEntity {
// getters ...
}
public class JavassistLazyInitializer extends BasicLazyInitializer
implements MethodHandler {
final JavassistLazyInitializer instance = new JavassistLazyInitializer(...);
ProxyFactory factory = new ProxyFactory();
// factory set-up class metadata
Class cl = factory.createClass();
final HibernateProxy proxy = ( HibernateProxy ) cl.newInstance();
( (Proxy) proxy ).setHandler( instance );
return proxy;
InvocationHandler handler = (proxy, method, methodArgs) -> {
if (method.getName().equals("get")) {
return 42;
} else {
throw new UnsupportedOperationException(
"Unsupported method: " + method.getName()
);
}
Proxy Class
Javassist
Libraries
Javassist
- Write code in strings
- What You See Is What You Get
- API similar to reflection
Javassist
Demo
Further reading
Bytecode manipulation with Javassist
By Piotr Lewandowski
Bytecode manipulation with Javassist
Na prezentacji opowiem o tym gdzie i w jaki sposób się manipuluje bytecodem oraz pokażę możliwości biblioteki Javassist która dostarcza API pozwalające na modyfikację bytecode bez narzutu znajomości bytecode samego w sobie. O czym nie będzie prezentacji: To nie będzie deep dive, nie będziemy hackować JVM ani szczegółowo omawiać fragmentów bytecode.
- 483