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();
}
}
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
Responsibilities:
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
}
}
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;
}
}
Program analysis
Class generation
Transform classes without Java source code
Complexity and barrier to entry:
Java Dynamic Proxies are:
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));
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;
}
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;
}
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.
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();
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"));
Why do we need a proxy?
LazyInitializationException
Main class: org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer
Enhancement Task
EntityEntry entry = (ManagedEntity)entity.$$_hibernate_getEntityEntry();
Why do we need a proxy & bytecode manipulation?
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.
@Configuration
@ComponentScan("org.edu")
class Application {
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
AsyncRestTemplate asyncRestTemplate(){
return new AsyncRestTemplate();
}
}
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 {
ASM Stuff Inside
javap -v /to/my/best/class/MyBestClass