Shine and Poverty of
Java Proxies
Yegor Bondar
Purposes
- Revisit Java code build and work cycle
- An attempt to understand Java proxy mechanism
- Dig deeper into frameworks architecture tricks
outline
- Long Story Short: Bytecode, ClassLoader, Java Agent, Bytecode manipulation
- Java Proxy world: Dynamic Proxy, Javassist, CGLib
- Proxied Frameworks: Hibernate, Spring, Mocking
J(XX)
What is java bytecode?
Java Class Loading quizz
class Parent{
{
System.out.println("Parent init block before ctor");
}
String toString = this.toString();
public static String var = "static variable";
static {
System.out.println("Parent static block");
}
public Parent(){
System.out.println("Parent ctor");
}
{
System.out.println("Parent init block after ctor");
}
@Override
public String toString(){
System.out.println("Parent toString call");
return "toString()";
}
}
class Child extends Parent{
static {
System.out.println("Child static block");
}
{
System.out.println("Child init block before ctor");
}
int hash = this.hashCode();
public Child(){
System.out.println("Child ctor");
}
{
System.out.println("Child init block after ctor");
}
@Override
public int hashCode(){
System.out.println("Child hashCode call");
return new Random().nextInt();
}
}
Java Class Loading quizz
public static void main(String[] args) {
new Child();
new Child();
}
1. Parent static block
3. Parent init block before ctor
4. Parent toString call
5. Parent init block after ctor
6. Parent ctor
7. Child init block before ctor
8. Child hashCode call
9. Child init block after ctor
10. Child ctor
2. Child static block
What is java classloader?
Responsibilities:
- Dynamically load classes
- Perform class cache actuality
- Verify correctness of bytecode
What is java classloader?
How java classloader works
java.lang.ClassCastException
public abstract class ClassLoader {
}
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
Class<?> result = cache.get(name);
if(result == null)
result = super.findSystemClass(name);
if(result == null)
result = defineClass(name, b, 0, b.length);
return result;
}
private byte[] loadClassData(String name) {
// load the class data from the connection
}
}
What is java classloader?
What is java Agent?
DIg deeper into java agent - Entry point
public class DemoAgent {
public static void premain(String agentArguments,
Instrumentation instrumentation){
System.out.println("Demo JavaAgent Entry point. Args: " + agentArguments);
instrumentation.addTransformer(new DemoClassTransformer());
}
}
Exec command:
java -javaagent:/to/agent.jar to/java/program/EntryPoint
Added ability to modify application code w/o modifying source code.
public class DemoClassTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("Transformation candidate: " + className);
return classfileBuffer;
}
}
DIg deeper into java agent - Class Transformer
Why Should I know All these Strange stuff?
Program analysis
- find bugs in code
- examine code complexity generation
Class generation
- proxies
- remove access to certain APIs
- compiler for another language like Scala
Transform classes without Java source code
- profilers
- optimization and obfuscation
- additional logging
Frameworks usage
- Spring : AOP, Java Config
- Hibernate : Lazy Proxies
- Mocking frameworks : Mocks & Stubs
- JRebel, XRebel, NewRelic, Profiling tools : Statistics & HotSwap
- Groovy, Clojure, Scala : compilation mixtures
Proxy concept
Java Proxy - Problems solved
- Event publishing - on method x(), transparently call y() or send message z.
- Transaction management (for db connections or other transactional ops)
- Thread management - thread out expensive operations transparently.
- Performance tracking - timing operations checked by a CountdownLatch, for example.
- Connection management - improve API that require some operations before actual action performed
- Changing method parameters - in case you want to pass default values for nulls, if that's your sort of thing.
Libraries overview
Complexity and barrier to entry:
Libraries overview
LIBRARY OVERVIEW - java Dynamic proxy
- They are based on runtime implementations of interfaces
- They are public, final and not abstract
- They extend java.lang.reflect.Proxy
- All method invocations go through java.lang.reflect.InvocationHandler
Java Dynamic Proxies are:
LIBRARY OVERVIEW - java Dynamic proxy
public class NoOpAddInvocationHandler implements InvocationHandler {
private final List proxied;
public NoOpAddInvocationHandler(List proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().startsWith("add")) {
return false;
}
return method.invoke(proxied, args);
}
}
List proxy = (List) Proxy.newProxyInstance(
NoOpAddInvocationHandlerTest.class.getClassLoader(),
new Class[] { List.class },
new NoOpAddInvocationHandler(list));
Library overview - Javassist
- Subproject of JBoss
- Javassist version 3.19.0-GA is available (Jan. 6, 2015).
- MPL, LGPL and Apache License
Library overview - Javassist bytecode manipulation
Input transformed = (Input) BytecodeTransformer
.wrapAround("org.edu.proxy.Input", "method")
.getDeclaredConstructor(String.class)
.newInstance("Hello world!");
public static Class wrapAround(String className, String methodName){
Class result = null;
ClassPool cp = ClassPool.getDefault();
try {
CtClass targetClass = cp.get(className);
CtMethod targetMethod = targetClass.getDeclaredMethod(methodName);
targetMethod.insertBefore("{System.out.println(field);};");
result = targetClass.toClass();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
Library overview - Javassist Proxy
public static <T> T create(Class<T> classs) throws Exception {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(classs);
Class clazz = factory.createClass();
MethodHandler handler = new MethodHandler() {
@Override
public Object invoke(Object self, Method overridden, Method forwarder,
Object[] args) throws Throwable {
System.out.println("do something "+overridden.getName());
return forwarder.invoke(self, args);
}
};
Object instance = clazz.newInstance();
((ProxyObject) instance).setHandler(handler);
return (T) instance;
}
Library overview - Byte Buddy
Byte Buddy is a code generation library for creating Java classes during the runtime of a Java application and without the help of a compiler.
library overview - byte buddy
Class<? extends MyService> serviceClass =
buddy.subclass(MyService.class)
.method(MethodMatchers.named("sayFoo")
.or(MethodMatchers.named("sayBar")))
.intercept(MethodDelegation.to(new MyServiceInterceptor()))
.make()
.load(Main.class.getClassLoader(),
ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
MyService service = serviceClass.newInstance();
library overview - byte buddy hotswap
ByteBuddyAgent.installOnOpenJDK();
Foo foo = new Foo();
new ByteBuddy()
.redefine(Bar.class)
.name(Foo.class.getName())
.make()
.load(Foo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
assertThat(foo.m(), is("bar"));
Frameworks Usage - hibernate
- Javaassist for all proxy stuff
- Hibernate provides some BuildTime tasks
Why do we need a proxy?
- Is the entry dirty? Have we changed any data?
- What fields of the entity have been loaded?
Frameworks Usage - hibernate
LazyInitializationException
Main class: org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer
Enhancement Task
Frameworks Usage - hibernate
EntityEntry entry = (ManagedEntity)entity.$$_hibernate_getEntityEntry();
- Implement ManagedEntity Interface
- Build-time instrument
- Runtime instrument
Frameworks Usage - Spring
- CGLib & JDK Proxy for all proxy stuff
- ASM for working with Java Config approach
Why do we need a proxy & bytecode manipulation?
- AOP
- Java Config approach
Frameworks Usage - Spring aop
- If target class has an interface then JDK Dynamic Proxy
- If target class does not have an interface then CGLib Proxy (proxy-target-class="true")
- If we enable AspectJ weaver in the config then AspectJ LTW(load-time weaver) or CTW(compile-time weaver)
public class ExampleRunner implements Runnable {
@Transactional
public void run(){
//Some transactional stuff
}
public String internalRun(){
run();
}
}
<tx:annotation-driven mode="aspectj"/>
AspectJ weaving requires spring-aspects.jar on the classpath, as well as load-time weaving (or compile-time weaving) enabled.
Frameworks Usage - Spring aop
Frameworks usage - Spring Java config
@Configuration
@ComponentScan("org.edu")
class Application {
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
AsyncRestTemplate asyncRestTemplate(){
return new AsyncRestTemplate();
}
}
- Scan all annotations
- Build Spring Bean definitions
- Define Spring Beans in the Context
Frameworks usage - Spring Java config
package org.springframework.context.annotation;
/**
* A component provider that scans the classpath from a base package.
* It then applies exclude and include filters to the resulting
* classes to find candidates.
*
* <p>This implementation is based on Spring's
* {@link org.springframework.core.type.classreading.MetadataReader
* MetadataReader}
* facility, backed by an ASM
* {@linkorg.springframework.asm.ClassReader ClassReader}.
*/
public class ClassPathScanningCandidateComponentProvider implements
EnvironmentCapable, ResourceLoaderAware {
Frameworks usage - Spring Java config
ASM Stuff Inside
Frameworks Usage - Mockito & Co.
- CGLib for all Mock, Stub and Spy stuff
- Spock uses JDK Proxy if an interface, CGLib if Class
- PowerMock uses Bytecode Instrumentation for mocking Static methods
Conlusions
- Bytecode manipulation is very powerful instrument
- Proxies are everywhere
- It is good to know your framework under the hood
javap -v /to/my/best/class/MyBestClass
Usefull Resources
- RebelLabs Articles: http://zeroturnaround.com/rebellabs/reloading-objects-classes-classloaders/
- Bytecode Stuff: http://zeroturnaround.com/rebellabs/10-reasons-why-java-rocks-more-than-ever-part-6-bytecode/
- CGLib : http://java.dzone.com/articles/cglib-missing-manual
- Demo code: https://bitbucket.org/yegorbondar/proxy-talk.git
Thank you!
Java Proxy
By Yegor Bondar
Java Proxy
java proxy, bytecode and manipulation
- 2,461