playing with java classes and bytecode

36
Playing with Java Classes and Bytecode What is a Class? How it is loaded and used? How can I write a code at runtime, using that code?

Upload: yoav-avrahami

Post on 27-Jun-2015

665 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Playing with Java Classes and Bytecode

Playing with Java Classes and Bytecode

What is a Class?How it is loaded and used?

How can I write a code at runtime, using that code?

Page 2: Playing with Java Classes and Bytecode

Java Classloading

• Why do we care?– Because if we gonna write code at runtime, we better know how

to load it and use it…– Because we do not really understand classes

• So… what identifies a class?– It’s name– It’s package– It’s classloader

• It means that– We can have multiple instances of a class loaded.– 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 are

always told.

Page 3: Playing with Java Classes and Bytecode

Java Classloading

• So what is this classloader?– It is a class written in Java (subclass of java.lang.ClassLoader), responsible to load other

classes for use by the JVM– Arranged as a tree of classloaders

• 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

Page 4: Playing with Java Classes and Bytecode

Java Classloading

Commandline Java App Tomcat (6)

Bootstrap classloader

Ext classloader

Application/ System

classloader

Application/ System

classloader

Common classloader

WAR 1classloader

WAR 2classloader

Loads the Application Jars from the classpath

Loads just the bootstrap.jar and tomcat-juli.jar

Loads the tomcat commons library jars

Loads the jars in the webapp lib directory

Page 5: Playing with Java Classes and Bytecode

LETS TALK BUSINESSOk, we understand classes. Where is the cool stuff?

Page 6: Playing with Java Classes and Bytecode

What we gonna talk about

• Aspect Oriented Programming– Java Proxy– Spring Aspects – AspectJ Aspects

• Doing the real cool stuff– The bootstrap classloader– The javaagent and class instrumentation– Writing bytecode at runtime

Page 7: Playing with Java Classes and Bytecode

The Java Proxy

• What it does– It allows to implement one or more interfaces dynamically

• When to use it– Generic implementation of an interface – such as in the case of client code calling a

service. The generic implementation can marshal the client method call to whatever “on-the-wire” format, saving the developer from coding stubs.

– Simplistic aspect oriented programming – 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– Can only function on Java interfaces– Intercepts only calls done on the proxy instance. When used on

the delegate model, calls directly on the delegate will not be intercepted.

Page 8: Playing with Java Classes and Bytecode

The Java Proxy

• Example code

• The output

Page 9: Playing with Java Classes and Bytecode

Spring AOP

• What it does– Allows to intercept method calls to Spring beans, without the requirement for an

interface– Simplifies the code compared to the Java Proxy– Works in the proxy model – spring beans factory returns a proxy to the bean we

intercept methods of– Allows using AspectJ selectors to select the methods to intercept– Aspects are written using the AspectJ Java syntax

• When to use it– Aspect programming on Spring beans.– Transaction handling, logging, security handling – anything AOP is good for

• Limitations– Can only function on Spring Beans– Intercepts only calls done on the proxy instance. Inner bean calls will

not be intercepted– Supports only a small subset of AspectJ selectors – only the method

execution join points are supported.

Page 10: Playing with Java Classes and Bytecode

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

Page 11: Playing with Java Classes and Bytecode

AspectJ

• What it does– Allows to intercept any Join Point such as method calls, exceptions, etc– Allows writing aspects using Java or AspectJ syntax– Changes the actual class bytecode– Allows using AspectJ selectors to select the methods to intercept– Change existing classes, adding methods, members and super-interfaces to them– Weaves aspects on load time or compile time

• When to use it– Aspect programming on any Java object– Transaction handling, logging, security handling – anything AOP is good for– Introduce compiler like coding rules

• Limitations– Requires custom Java Compiler (AspectJ compiler)– Or requires custom definitions for load time weaving

(depending on run environment)

Page 12: Playing with Java Classes and Bytecode

AspectJ

• Configuration options– Compile time weaving– Load time weaving– Load time weaving with Spring

• Configuring AspectJ Load time weaving with Spring

– Simple, isn’t it? • Well, there are additional requirements • Dependencies

– aspectjrt.jar must be in the classpath• (continues on next slide)

Page 13: Playing with Java Classes and Bytecode

AspectJ

• META-INF/aop.xml– declares the aspects for load time weaving and the target packages to weave into

• Class instrumentation– Commandline & 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 – • The jar spring-instrument-tomcat-<version>.jar has to be copied to tomcats lib directory• The webapp has to have a context.xml file in WEB-INF\content.xml, with the minimum content

– Note there are alternative locations for the context file. This is the location I find easiest to use.

Page 14: Playing with Java Classes and Bytecode

SHORT INTRO TO ASPECTJIf we are talking about aspects…

Page 15: Playing with Java Classes and Bytecode

Intro to AOP (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 – action taken by the aspect at a particular pointcut. Types of advice– Before, after returning, after throwing, after finally, around

• Pointcut – a selector of pointcuts using predicates. An advice is associated with a pointcut expression and runs on any point matched by the pointcut

Page 16: Playing with Java Classes and Bytecode

Intro to AOP (AspectJ Style)

• More on pointcuts– Pointcuts are query expressions on the code.– Can 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(…) – then 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

Page 17: Playing with Java Classes and Bytecode

Intro to AOP (AspectJ Style)

• Some examples– execution (int *())

• joinpoints who are method executions for methods who return an int and do not take parameters

– @annotation(com.example.Log) && execution(* *(..))• joinpoints who are method executions for 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 method call to an int method when the executing object is any type except Point.– cflow(P) && cflow(Q)

• each join point that is in both the control flow of P and in the control flow of Q

Page 18: Playing with Java Classes and Bytecode

Intro to AOP (AspectJ Style)

• Coding Aspects using AspectJ Java Syntax

• AspectJ AspectJ syntax

Page 19: Playing with Java Classes and Bytecode

Intro to AOP (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 variable to a class– Turning java into multiple inheritance system – change interfaces to include method

implementation and variables• Using join points as compiler errors or warnings• SoftExceptions

– catching an exception at a join point and replacing it with SoftException (subclass of RuntimeException)

• Privileged aspects– Can access private, package and protected members of classes, bypassing the java

member visibility constraints

Page 20: Playing with Java Classes and Bytecode

Intro to AOP (AspectJ Style)

• Example of an aspect modifying a class

Page 21: Playing with Java Classes and Bytecode

BACK TO THE COOL STUFF

Page 22: Playing with Java Classes and Bytecode

Bootstrap classloader

• What it does– Allows to replace java system classes– Bypasses all java security policy set by SecurityManager

• When to use it– Snuff inside the core Java APIs– Change core Java API functionality– Terracotta uses it to replace the Java HashMap class with a distributed cache

implementation• Limitations

– Classes loaded by the bootstrap classloader are not able to load classes not included in the bootstrap classloader jar list

• Usage– With the java commandline 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

Page 23: Playing with Java Classes and Bytecode

Javaagent and Instrument

• What it does– Allows to instrument / transform / change a class definition

as it is loaded• When to use it

– AspectJ uses it for load time weaving– To modify classes as we load them

• Limitations– It 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 java commandline parameter 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 retransform a class.

– ClassFileTransformer has one method to transform a class file bytes (as a byte[]).

Page 24: Playing with Java Classes and Bytecode

Writing Bytecode

• What it does– Allows to write new classes or modify existing classes at runtime– Load the new/modified classes– Instantiate them– Use them as any other Java class

• When to use it– Performance – when a mapping library (in my case XML to objects) was too slow

because of excessive use of reflections, coding simple mapping classes proved match faster

– Implementing Proxies on any class – Spring AOP and Hibernate do just that– Hibernate uses bytecode modification of classes to introduce mechanisms for lazy

loading of members– Adaptor for Beans – a reporting framework used reflection on beans to read the field

definitions from in it’s Java Object 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.

Page 25: Playing with Java Classes and Bytecode

Writing Bytecode

• Limitations– Hard to use– Generated bytecode cannot be debugged normally - we do

not have the source code • BCEL – I have found using BCEL to be easiest for me• BCEL is included with JRE 1.6 under the package

com.sun.org.apache.bcel– However, the JRE version did not work for me.– I have used the apache version – 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 cannot debug generated bytecode.

– Classes can be generated at build time or runtime. I have found runtime generation simpler to use

– Use class generation by example• Good resource

– http://java.sys-con.com/node/49088

Page 26: Playing with Java Classes and Bytecode

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 will create java code, that when running it, will

create a factory class that generates your example class.– You can also use org.apache.bcel.util.Class2-HTML to get HTML documentation of your

example class bytecode.– Modify the generated factory class to generate the bytecode for the classes you want– Use org.apache.bcel.verifier.Verifier or

com.pavelvlasov.codegen.ClassGeneratorBase.verify() to verify that the generated classes are valid

– Use Jad, Jadclipse or DJ Java Decompiler to decompile generated files and verify method logic. I recommend that you set the "annotate" option so you can see the bytecode instruction as comments.

• Class loading– Code a new subclass of ClassLoader– Use the defineClass (protected) method to define the new

generated class– Remember to link your class loader to a parent class loader

Page 27: Playing with Java Classes and Bytecode

Writing Bytecode

• Example – mapping objects from type A to type BUsing a superclass helper

– – The example class

Page 28: Playing with Java Classes and Bytecode

Writing Bytecode

• Running BCELifier on the class– BCELifier has a main method, getting one parameter – the class name

• The created factory class– Defining the class and the class members

Page 29: Playing with Java Classes and Bytecode

Writing Bytecode

• Creating the Factory class (continued)– Creating the constructor

– Which creates the constructor

Page 30: Playing with Java Classes and Bytecode

Writing Bytecode

• Creating the Factory class (continued)– Creating the mapToB method

publicReturn type

Input arguments

Input argument names

Method name

Class name

Page 31: Playing with Java Classes and Bytecode

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

Page 32: Playing with Java Classes and Bytecode

Writing Bytecode

• Creating the Factory class (continued)– Creating the mapToB method

Store the result as Object 3

Create new PersonDTO

Invoke the personDTO constructor

Page 33: Playing with Java Classes and Bytecode

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

Page 34: Playing with Java Classes and Bytecode

Writing Bytecode

• Creating the Factory class (continued)– Creating the mapToB method – we got another 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.– Guess narrowing the return type of mapToB was not a really good idea, given generated

class loaded at runtime does not really benefit from the narrowed type constraint –

Page 35: Playing with Java Classes and Bytecode

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

Page 36: Playing with Java Classes and Bytecode

IT IS THAT SIMPLE