Java bytecode and classes

5,330 views
5,186 views

Published on

Published in: Technology
1 Comment
11 Likes
Statistics
Notes
No Downloads
Views
Total views
5,330
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
172
Comments
1
Likes
11
Embeds 0
No embeds

No notes for slide

Java bytecode and classes

  1. 1. Playing with Java Classes and Bytecode What is a Class? How it is loaded and used? How to write a Java program that writes itself at runtime? Yoav Abrahami Wix.com
  2. 2. Java Classloading • Why do we care? – Because if we’re gonna write code at runtime, we’d better know how to load and use it… – Because we don’t really understand classes • So… what identifies a class? – Its name – Its package – Its classloader • It means that – We can have multiple instances of a class loaded at the same time – Two instances of the same class from different classloaders are not compatible and not assignable – Static variables are static only in the context of a classloader, not globally as we’re always told
  3. 3. Java Classloading • So what is this classloader? – A Java class (subclass of java.lang.ClassLoader), responsible for loading other classes used by the JVM – Classloaders are arranged as a tree • Bootstrap classloader – Loads the Java system • jre/lib/resources.jar – series of resource files • jre/lib/rt.jar – the java.*, javax.*, etc packages • jre/lib/sunrsasign.jar • jre/lib/jsse.jar – secure socket extension • jre/lib/jce.jar – Java cryptography extension • jre/lib/charsets.jar • jre/classes – The important stuff is in rt.jar – the base Java classes Bootstrap classloader Ext classloader Application / System classloader
  4. 4. Java Classloading Commandline Java App Tomcat (6) Bootstrap classloader Ext classloader Application / System classloader Application / System classloader Common classloader WAR 1 classloader WAR 2 classloader
  5. 5. Java Classloading Commandline Java App Tomcat (6) Bootstrap classloader Ext classloader Application / System classloader Application / System classloader Common classloader WAR 1 classloader WAR 2 classloader Loads the Application Jars from the classpath Loads only bootstrap.jar and tomcat-juli.jar Loads Tomcat commons library jars Loads jars in the webapp lib directory
  6. 6. LETS TALK BUSINESS Ok, we understand classes. Where is the cool stuff?
  7. 7. What we’re gonna talk about • Aspect Oriented Programming – Java Proxy – Spring Aspects – AspectJ Aspects • Doing the really cool stuff – The bootstrap classloader – The javaagent and class instrumentation – Writing bytecode at runtime • All in context of when I’ve had to use them
  8. 8. The Java Proxy • What does it do? – Allows implementing one or more interfaces dynamically • When do we use it? – Generic implementation of an interface – such as in the case of a client calling a service. The client uses an interface with a generic implementation that marshals the method calls to whatever “on-the-wire” format. For instance, creating a SOAP client on the fly using an interface and WSDL – Simplistic AOP – to catch method calls, perform some pre/post/around logic and delegate the call to the real implementation. Can be used for transaction handling, logging, etc. • Limitations – Only supports Java interfaces – Intercepts only calls to the proxy instance. Direct calls to the delegate will not be intercepted (for instance, a call from one delegate method to another)
  9. 9. The Java Proxy • Example code public interface SomeInterface { public void doSomething(); public void doAnotherThing(String name); } Object p = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{SomeInterface.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoking method:" + method.getName()); if (args != null) for (Object arg: args) System.out.println(" arg: " + arg); return null; } }); SomeInterface s = (SomeInterface)p; s.doSomething(); s.doAnotherThing("hello");
  10. 10. Spring AOP • What does it do? – Allows intercepting method calls to Spring beans, with or without an interface – Simpler code compared to the Java Proxy – Based on the proxy model – Spring BeanFactory returns a proxy to the real bean – Supports choosing the methods to intercept using AspectJ selectors – Aspects are written using the AspectJ Java syntax • When do we use it? – AOP on Spring beans – Transaction handling, logging, security handling – anything AOP is good for • Limitations – Only supports Spring beans – Intercepts only calls to the proxy instance. Direct calls to the delegate will not be intercepted (for instance, a call from one delegate method to another) – Supports only a subset of AspectJ selectors
  11. 11. Spring AOP • Configuring Spring AOP • Example Aspect • Activating the Aspect – Using Spring component scan, by adding the @Component annotation on the aspect – Using Spring Beans XML @Aspect public class ExampleAspect { @Around("@annotation(com.experiments.RequireLogin)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { ... } } <bean class="com.experiments.ExampleAspect"/> <aop:aspectj-autoproxy/>
  12. 12. AspectJ • What does it do? – Allows intercepting any JoinPoint such as method calls, exceptions, etc. – Supports writing aspects using Java or AspectJ syntax – Modifies the actual class bytecode – Supports choosing the methods to intercept using AspectJ selectors – Modify existing classes, adding methods, members and super-interfaces to them – Weaves aspects on load time or compile time • When do we use it? – AOP on any Java class – Transaction handling, logging, security – anything AOP is good for – Introduce compiler-like coding rules • Limitations – For compile-time weaving – replaces the standard Java Compiler – For load-time weaving – requires elaborate JVM configuration
  13. 13. AspectJ • Configuration options – Compile-time weaving – Load-time weaving – Load-time weaving with Spring • Dependencies – aspectjrt.jar must be in the classpath • Configuring AspectJ load-time weaving with Spring – Simple, isn’t it? • Well, not so simple… <aop:aspectj-autoproxy/> <context:load-time-weaver/>
  14. 14. AspectJ • Need to include META-INF/aop.xml – declares the aspects for load-time weaving and the target packages to weave into <!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"> <aspectj> <weaver> <!-- only any class from the com.experiments package --> <include within="com.experiments..*"/> </weaver> <aspects> <!-- weave in just this aspect --> <aspect name="com.experiments.ExampleAspect"/> </aspects> </aspectj>
  15. 15. AspectJ • Per-platform configuration for class instrumentation – Command line & JUnit applications – using the -javaagent to load instrumentation jars • -javaagent:spring-instrument-<version>.jar -javaagent:aspectjweaver-<version>.jar – Maven running JUnit – using the maven-surefire-plugin, passing it the javaagent args – Note the two javaagent params must be at the same line – Tomcat 6 – • spring-instrument-tomcat-<version>.jar has to be copied to the Tomcat lib directory • The webapp must have a context.xml file in WEB-INFcontent.xml, with the minimum content – There are alternative locations for the context file. This is the location I find easiest to use. <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.6</version> <configuration> <forkMode>once</forkMode> <argLine> -javaagent:"${settings.localRepository}/org/springframework/spring- instrument/${org.springframework.version}/spring-instrument- ${org.springframework.version}.jar" -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/ ${org.aspectj.version}/aspectjweaver-${org.aspectj.version}.jar" </argLine> <useSystemClassLoader>true</useSystemClassLoader> </configuration> </plugin> <Context path="/"> <Loader loaderClass="org.springframework.instrument.classloading.tomcat. TomcatInstrumentableClassLoader"/> </Context>
  16. 16. ASPECTJ 101 As long as we’re talking about aspects…
  17. 17. AOP 101 – AspectJ Style • Aspect – a concern that cuts across multiple classes. Examples are logging, transaction handling, security, etc. • Join Point – a point during the execution of a program. Examples are when – A method is executed – A method is called – A constructor is executed – A constructor is called – An exception handler is executed – An advice is executed – Static initialization is executed – Initialization and pre-initialization of an object • Advice – an action taken by the aspect at a particular pointcut. Types of advice: – Before, after returning, after throwing, after finally, around • Pointcut – a selector of Join Points using predicates. An advice is associated with a pointcut expression and runs on any point matched by the pointcut
  18. 18. AOP 101 – AspectJ Style • More on pointcuts – Pointcuts are query expressions on the code – May be joined using the and (‘&&’), or (‘||’) or not (‘!’) operators • Some pointcut predicates – execution(…) – when a particular method body executes – call(…) – when a method is called – handler(….) – when a particular exception handler executes – this(…) – when the currently executing object is of a certain type – target(…) – when the target object is of type – args(…) – when the method arguments match a certain type – within(…) – when the executing code belongs to a certain class – cflow(…) – when the program flow is in a certain method call (the method is a parent in the stack) – @annotation(…) – methods annotated with a certain annotation – @target(…) – when the target executing object has a certain annotation – @args(…) – when the runtime type of an argument has a certain annotation – @within(…) – limits to pointcuts within a class that has a certain annotation – bean(…) – a spring bean name
  19. 19. AOP 101 – AspectJ Style • More on pointcuts – Pointcuts are query expressions on the code – May be joined using the and (‘&&’), or (‘||’) or not (‘!’) operators • Some pointcut predicates – execution(…) – when a particular method body executes – call(…) – when a method is called – handler(….) – when a particular exception handler executes – this(…) – when the currently executing object is of a certain type – target(…) – when the target object is of type – args(…) – when the method arguments match a certain type – within(…) – when the executing code belongs to a certain class – cflow(…) – when the program flow is in a certain method call (the method is a parent in the stack) – @annotation(…) – methods annotated with a certain annotation – @target(…) – when the target executing object has a certain annotation – @args(…) – when the runtime type of an argument has a certain annotation – @within(…) – limits to pointcuts within a class that has a certain annotation – bean(…) – a spring bean name Not Supported by Spring AOP Only Spring AOP
  20. 20. AOP 101 – AspectJ Style • Some examples – execution (int *()) • JoinPoints that are integer-returning method executions that do not take parameters – @annotation(com.example.Log) && execution(* *(..)) • JoinPoints that are executions of methods annotated with the @Log annotation, regardless of the method return type, class, name or parameters – call(public * *(..)) • Call to any public method – !this(Point) && call(int *(..)) • Any call to a method returning an integer when the executing object is of any type other than Point – cflow(P) && cflow(Q) • All JoinPoint that are both in the control flow of P and in the control flow of Q
  21. 21. AOP 101 – AspectJ Style • Coding Aspects using AspectJ Java Syntax • The AspectJ syntax @Aspect public class MyAspect { @Pointcut("within(com.springsource..*)") public void inSpring() {} @Pointcut("@Annotation(java.lang.Deprecated)") public void inDepracated() {} @Pointcut("inSpring() && inDepracated()") public void deprecatedInSpring() {} @Before("deprecatedInSpring()") public void pointcutRef() { ... } @Before("within(com.springsource..*) && @Annotation(java.lang.Deprecated)") public void pointcutInplace() { ... } } aspect A { pointcut fooPC(): execution(void Test.foo()); pointcut gooPC(): execution(void Test.goo()); pointcut printPC(): call(void java.io.PrintStream.println(String)); before(): cflow(fooPC()) && cflow(gooPC()) && printPC() && !within(A) { System.out.println("should occur"); } }
  22. 22. AOP 101 – AspectJ Style • Adding code to existing classes using aspects (Inter-type declarations) – Adding implemented interfaces – Changing the superclass of a class – Adding methods to a class – Adding members to a class – Implement Mix-ins in Java • Using Join Points to raise custom compiler errors or warnings • Privileged aspects – Can access private, package and protected members of classes, bypassing the java member visibility constraints
  23. 23. AOP 101 – AspectJ Style • Example of an aspect modifying a class aspect PointAssertions { private boolean Point.assertX(int x) { return (x <= 100 && x >= 0); } private boolean Point.assertY(int y) { return (y <= 100 && y >= 0); } before(Point p, int x): target(p) && args(x) && call(void setX(int)) { if (!p.assertX(x)) { System.out.println("Illegal value for x"); return; } } before(Point p, int y): target(p) && args(y) && call(void setY(int)) { if (!p.assertY(y)) { System.out.println("Illegal value for y"); return; } } } public class Point { int x, y; public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public static void main(String[] args) { Point p = new Point(); p.setX(3); p.setY(333); } }
  24. 24. BACK TO THE COOL STUFF
  25. 25. The bootstrap classloader • What does it do? – Allows replacing Java system classes – Bypasses all Java security policies set by SecurityManager • When do we use it? – Meddling with the core Java APIs – Terracotta uses it to replace the Java HashMap class with a distributed cache implementation • Limitations – Classes loaded by the bootstrap classloader can’t load classes that are not included in the bootstrap classpath • Usage – With the Java command line options • –bootclasspath: - list of jars to use instead of the standard list • –bootclasspath/a: - list of jars to append to the standard list • –bootclasspath/p: - list of jars to prepend to the standard list
  26. 26. Javaagent and Instrument • What does it do? – Allows instrumenting / transforming / changing a class as it is loaded • When do we use it? – AspectJ uses it for load-time weaving – To modify classes as we load them • Limitations – Works on the class file bytes – requires intimate knowledge of the class file structure – Not very useful without a framework like AspectJ, BCEL, cglib, etc. • Usage – Using the –javaagent command line option to introduce an Agent jar – The Agent jar has to have a premain method • public static void premain(String agentArguments, Instrumentation instrumentation) – The Instrumentation class allows to redefine a class, add & remove transformers, and re-transform a class. – ClassFileTransformer has one method to transform a class file bytes (as byte[]).
  27. 27. Writing Bytecode • What does it do? – Write new classes or modify existing classes at runtime – Load the new/modified classes – Instantiate them – Use them as any other Java class • When do we use it? – Implementing Proxies on any class – Spring AOP and Hibernate do just that – Hibernate uses bytecode manipulations of classes to introduce mechanisms for lazy- loading of members • My own experience – Performance – when a mapping library (in my case XML to objects) was too slow due to excessive use of reflection, coding simple mapping classes proved much faster – Adaptor for Beans – a reporting framework used reflection on beans to read the field definitions from a JavaObject datasource. When adapting a Java query interface that returns map-like objects, we had to generate wrapper classes adapting the map to the beans the framework expected.
  28. 28. Writing Bytecode • Limitations – Hard to use – Generated bytecode cannot be debugged normally – there’s no source code  • BCEL – I’ve found using BCEL to be the easiest • BCEL is included with JRE 1.6 – In package com.sun.org.apache.bcel – However, the JRE version didn’t work for me – Eventually, I’ve used the Apache version – BCEL 5.2 • Guidelines – Keep the generated code small. Use helper methods / superclasses whenever you can. Remember, you can debug helper classes or superclasses, but you can’t debug generated bytecode – Classes can be generated at build-time or runtime. I’ve found runtime generation simpler to use – Use class generation-by-example
  29. 29. Writing Bytecode • Code generation or bytecode generation? • Code generation – Performed at build-time - inputs to the generation process must be available at build time – Easier to debug – Tends to clutter the project source code – Code-generation code tends to be longer than bytecode-generation code – Hard to get the build right – separation of generated code from actual code • Bytecode generation – Performed at runtime – inputs to the generation process can be resolved at runtime – Requires a higher skill level – More complex to debug • At the philosophical level – If it can be generated, it’s not code! – We’re just configuring the JVM, not coding
  30. 30. Writing Bytecode • Class generation-by-example – Write an example class of what you want to generate – Compile it – Run org.apache.bcel.util.BCELifier. It generates a factory class that generates your example class bytecode – Modify the generated factory class so that it generates the bytecode for the classes you want – Use a bytecode verifier to verify the generated classes – Decompile generated files and verify method logic. I recommend that you set the "annotate" option so you can see the bytecode instruction as comments. – BCEL also provides a tool to generate HTML documentation for your example class bytecode • Class loading – Code a new subclass of ClassLoader – Use the defineClass (protected) method to define the new generated class – Remember to link your classloader to a parent classloader
  31. 31. Writing Bytecode • Example – mapping objects from type A to type B Using a superclass helper public abstract class BaseMapper { protected Object a; protected Object b; public BaseMapper(Object a, Object b) { this.a = a; this.b = b; } public abstract Object mapToB(Object a); public abstract Object mapToA(Object b); } public class ExampleMappedClass extends BaseMapper{ public ExampleMappedClass(Person person, PersonDTO personDTO) { super(person, personDTO); } @Override public PersonDTO mapToB(Object a) { Person person = (Person)a; PersonDTO personDTO = new PersonDTO(); personDTO.name = person.name; personDTO.lastName = person.lastName; personDTO.id = Integer.toString(person.id); return personDTO; } @Override public Person mapToA(Object b) { PersonDTO personDTO = (PersonDTO)b; Person person = new Person(); person.name = personDTO.name; person.lastName = personDTO.lastName; person.id = Integer.parseInt(personDTO.id); return person; } } The example class
  32. 32. Writing Bytecode • Running BCELifier on the class – BCELifier has a main method, accepting one parameter – the class name • The created factory class – Defining the class and the class members public class ExampleMappedClassCreator implements Constants { private InstructionFactory _factory; private ConstantPoolGen _cp; private ClassGen _cg; public ExampleMappedClassCreator() { _cg = new ClassGen("com.experiments.ExampleMappedClass", "com.experiments.BaseMapper", "ExampleMappedClass.java", ACC_PUBLIC | ACC_SUPER, new String[] { }); _cp = _cg.getConstantPool(); _factory = new InstructionFactory(_cg, _cp); } public void create(OutputStream out) throws IOException { createMethod_0(); createMethod_1(); createMethod_2(); createMethod_3(); createMethod_4(); _cg.getJavaClass().dump(out); } ...
  33. 33. Writing Bytecode • Creating the Factory class (continued) – Creating the constructor – Which generates bytecode for the constructor private void createMethod_0() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, new Type[] { new ObjectType("com.experiments.Person"), new ObjectType("com.experiments.PersonDTO") }, new String[] { "arg0", "arg1" }, "<init>", "com.experiments.ExampleMappedClass", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0)); il.append(_factory.createLoad(Type.OBJECT, 1)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createInvoke("com.experiments.BaseMapper", "<init>", Type.VOID, new Type[] { Type.OBJECT, Type.OBJECT }, Constants.INVOKESPECIAL)); InstructionHandle ih_6 = il.append(_factory.createReturn(Type.VOID)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } public ExampleMappedClass(Person person, PersonDTO personDTO) { super(person, personDTO); }
  34. 34. public PersonDTO mapToB(Object a) { Person person = (Person)a; PersonDTO personDTO = new PersonDTO(); personDTO.name = person.name; personDTO.lastName = person.lastName; personDTO.id = Integer.toString(person.id); return personDTO; }private void createMethod_1() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_1PUBLIC, new ObjectType("com.experiments.PersonDTO"), new Type[] { Type.OBJECT }, new String[] { "arg0" }, "mapToB", "com.experiments.ExampleMappedClass", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 1)); il.append(_factory.createCheckCast(new ObjectType("com.experiments.Person"))); il.append(_factory.createStore(Type.OBJECT, 2)); InstructionHandle ih_5 = il.append(_factory.createNew("com.experiments.PersonDTO")); il.append(InstructionConstants.DUP); il.append(_factory.createInvoke("com.experiments.PersonDTO", "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); il.append(_factory.createStore(Type.OBJECT, 3)); InstructionHandle ih_13 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "name", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "name", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_21 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "lastName", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "lastName", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_29 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "id", Type.INT, Constants.GETFIELD)); il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "id", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_40 = il.append(_factory.createLoad(Type.OBJECT, 3)); InstructionHandle ih_41 = il.append(_factory.createReturn(Type.OBJECT)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } Writing Bytecode • Creating the Factory class (continued) – Creating the mapToB method
  35. 35. public PersonDTO mapToB(Object a) { Person person = (Person)a; PersonDTO personDTO = new PersonDTO(); personDTO.name = person.name; personDTO.lastName = person.lastName; personDTO.id = Integer.toString(person.id); return personDTO; }private void createMethod_1() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_1PUBLIC, new ObjectType("com.experiments.PersonDTO"), new Type[] { Type.OBJECT }, new String[] { "arg0" }, "mapToB", "com.experiments.ExampleMappedClass", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 1)); il.append(_factory.createCheckCast(new ObjectType("com.experiments.Person"))); il.append(_factory.createStore(Type.OBJECT, 2)); InstructionHandle ih_5 = il.append(_factory.createNew("com.experiments.PersonDTO")); il.append(InstructionConstants.DUP); il.append(_factory.createInvoke("com.experiments.PersonDTO", "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); il.append(_factory.createStore(Type.OBJECT, 3)); InstructionHandle ih_13 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "name", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "name", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_21 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "lastName", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "lastName", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_29 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "id", Type.INT, Constants.GETFIELD)); il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "id", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_40 = il.append(_factory.createLoad(Type.OBJECT, 3)); InstructionHandle ih_41 = il.append(_factory.createReturn(Type.OBJECT)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } Writing Bytecode • Creating the Factory class (continued) – Creating the mapToB method public Return type Input arguments Class name Method name Input argument names
  36. 36. public PersonDTO mapToB(Object a) { Person person = (Person)a; PersonDTO personDTO = new PersonDTO(); personDTO.name = person.name; personDTO.lastName = person.lastName; personDTO.id = Integer.toString(person.id); return personDTO; }private void createMethod_1() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_1PUBLIC, new ObjectType("com.experiments.PersonDTO"), new Type[] { Type.OBJECT }, new String[] { "arg0" }, "mapToB", "com.experiments.ExampleMappedClass", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 1)); il.append(_factory.createCheckCast(new ObjectType("com.experiments.Person"))); il.append(_factory.createStore(Type.OBJECT, 2)); InstructionHandle ih_5 = il.append(_factory.createNew("com.experiments.PersonDTO")); il.append(InstructionConstants.DUP); il.append(_factory.createInvoke("com.experiments.PersonDTO", "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); il.append(_factory.createStore(Type.OBJECT, 3)); InstructionHandle ih_13 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "name", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "name", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_21 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "lastName", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "lastName", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_29 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "id", Type.INT, Constants.GETFIELD)); il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "id", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_40 = il.append(_factory.createLoad(Type.OBJECT, 3)); InstructionHandle ih_41 = il.append(_factory.createReturn(Type.OBJECT)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } Writing Bytecode • Creating the Factory class (continued) – Creating the mapToB method Store the result as Object 2 Take Object 1 – the a parameter (Object 0 is this) Cast it to Person
  37. 37. public PersonDTO mapToB(Object a) { Person person = (Person)a; PersonDTO personDTO = new PersonDTO(); personDTO.name = person.name; personDTO.lastName = person.lastName; personDTO.id = Integer.toString(person.id); return personDTO; }private void createMethod_1() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_1PUBLIC, new ObjectType("com.experiments.PersonDTO"), new Type[] { Type.OBJECT }, new String[] { "arg0" }, "mapToB", "com.experiments.ExampleMappedClass", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 1)); il.append(_factory.createCheckCast(new ObjectType("com.experiments.Person"))); il.append(_factory.createStore(Type.OBJECT, 2)); InstructionHandle ih_5 = il.append(_factory.createNew("com.experiments.PersonDTO")); il.append(InstructionConstants.DUP); il.append(_factory.createInvoke("com.experiments.PersonDTO", "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); il.append(_factory.createStore(Type.OBJECT, 3)); InstructionHandle ih_13 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "name", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "name", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_21 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "lastName", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "lastName", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_29 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "id", Type.INT, Constants.GETFIELD)); il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "id", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_40 = il.append(_factory.createLoad(Type.OBJECT, 3)); InstructionHandle ih_41 = il.append(_factory.createReturn(Type.OBJECT)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } Writing Bytecode • Creating the Factory class (continued) – Creating the mapToB method Store the result as Object 3 Create new PersonDTO Invoke the personDTO constructor
  38. 38. public PersonDTO mapToB(Object a) { Person person = (Person)a; PersonDTO personDTO = new PersonDTO(); personDTO.name = person.name; personDTO.lastName = person.lastName; personDTO.id = Integer.toString(person.id); return personDTO; }private void createMethod_1() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_1PUBLIC, new ObjectType("com.experiments.PersonDTO"), new Type[] { Type.OBJECT }, new String[] { "arg0" }, "mapToB", "com.experiments.ExampleMappedClass", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 1)); il.append(_factory.createCheckCast(new ObjectType("com.experiments.Person"))); il.append(_factory.createStore(Type.OBJECT, 2)); InstructionHandle ih_5 = il.append(_factory.createNew("com.experiments.PersonDTO")); il.append(InstructionConstants.DUP); il.append(_factory.createInvoke("com.experiments.PersonDTO", "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL)); il.append(_factory.createStore(Type.OBJECT, 3)); InstructionHandle ih_13 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "name", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "name", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_21 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "lastName", Type.STRING, Constants.GETFIELD)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "lastName", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_29 = il.append(_factory.createLoad(Type.OBJECT, 3)); il.append(_factory.createLoad(Type.OBJECT, 2)); il.append(_factory.createFieldAccess("com.experiments.Person", "id", Type.INT, Constants.GETFIELD)); il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(_factory.createFieldAccess("com.experiments.PersonDTO", "id", Type.STRING, Constants.PUTFIELD)); InstructionHandle ih_40 = il.append(_factory.createLoad(Type.OBJECT, 3)); InstructionHandle ih_41 = il.append(_factory.createReturn(Type.OBJECT)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); } Writing Bytecode • Creating the Factory class (continued) – Creating the mapToB method Pop the stack, writing the value to the name property of object 3 Load object 3 to the stack - Person Read the name field from object 2 to the stack Load object 2 to the stack - PersonDTO
  39. 39. Writing Bytecode • Creating the Factory class (continued) – Creating the mapToB method – we got a second mapToB method – The first has signature PersonDTO mapToB(Object) – The second has signature Object mapToB(Object) – The second overrides the superclass mapToB method and calls the first – Narrowing the return type of mapToB was apparently not a good idea, given that generated classes loaded at runtime do not benefit from the narrowed type constraint private void createMethod_4() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC, Type.OBJECT, new Type[] { Type.OBJECT }, new String[] { "arg0" }, "mapToB", "com.experiments.ExampleMappedClass", il, _cp); InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0)); il.append(_factory.createLoad(Type.OBJECT, 1)); il.append(_factory.createInvoke("com.experiments.ExampleMappedClass", "mapToB", new ObjectType("com.experiments.PersonDTO"), new Type[] { Type.OBJECT }, Constants.INVOKEVIRTUAL)); InstructionHandle ih_5 = il.append(_factory.createReturn(Type.OBJECT)); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); }
  40. 40. class MyClassLoader extends ClassLoader { MyClassLoader(ClassLoader parent) { super(parent); } public void defineClass(JavaClass javaClass) { byte[] classBytes = javaClass.getBytes(); defineClass(javaClass.getClassName(), classBytes, 0, classBytes.length); } } public JavaClass create() { createMethod_0(); createMethod_1(); createMethod_2(); createMethod_3(); createMethod_4(); return _cg.getJavaClass(); } MyClassLoader myClassLoader = new MyClassLoader(this.getClass().getClassLoader()); ExampleMappedClassCreator creator = new ExampleMappedClassCreator(); JavaClass javaClass = creator.create(); myClassLoader.defineClass(javaClass); Class<?> clazz = myClassLoader.loadClass(javaClass.getClassName()); Constructor<?> constructor = clazz.getConstructor(Person.class, PersonDTO.class); BaseMapper mapper = (BaseMapper)constructor.newInstance(new Person(), new PersonDTO()); Writing Bytecode• Using the generated class – The Class loader – Creating and loading the new class – Add a method returning JavaClass to the factory class – And use it
  41. 41. class MyClassLoader extends ClassLoader { MyClassLoader(ClassLoader parent) { super(parent); } public void defineClass(JavaClass javaClass) { byte[] classBytes = javaClass.getBytes(); defineClass(javaClass.getClassName(), classBytes, 0, classBytes.length); } } public JavaClass create() { createMethod_0(); createMethod_1(); createMethod_2(); createMethod_3(); createMethod_4(); return _cg.getJavaClass(); } MyClassLoader myClassLoader = new MyClassLoader(this.getClass().getClassLoader()); ExampleMappedClassCreator creator = new ExampleMappedClassCreator(); JavaClass javaClass = creator.create(); myClassLoader.defineClass(javaClass); Class<?> clazz = myClassLoader.loadClass(javaClass.getClassName()); Constructor<?> constructor = clazz.getConstructor(Person.class, PersonDTO.class); BaseMapper mapper = (BaseMapper)constructor.newInstance(new Person(), new PersonDTO()); Writing Bytecode• Using the generated class – The Class loader – Creating and loading the new class – Add a method returning JavaClass to the factory class – And use it Create the classloader Create the class factory Create the class bytecode Define the new class Load the new class Get the new class constructor Call the constructor
  42. 42. IT IS THAT SIMPLE 

×