J'ai été amené lors de mes derniers développements à utiliser et à profiter des avantages des Framework Javaassist. Je le trouve très utile et facile à mettre en place, mais surtout très peu connu. Alors, je met en ligne ce poste pour en partager mon feedback.
Javassist is a sub project of JBoss, Javassist (for Java Programming Assistant)
makes Java bytecode manipulation
simple. It is a class library for editing bytecodes in Java;
it enables Java programs to define a new class at runtime and to
modify a class file when the JVM loads it. Unlike other similar
bytecode editors, Javassist provides two levels of API: source level
and bytecode level. If the users use the source-level API, they can
edit a class file without knowledge of the specifications of the Java
bytecode. The whole API is designed with only the vocabulary of the
Java language. You can even specify inserted bytecode in the form of
source text; Javassist compiles it on the fly. On the other hand, the
bytecode-level API allows the users to directly edit a class file as
other editors.
If your application is under Mavan, here is today (28/05/2013) javaassit Mavane's dependency :
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.17.1.GA</version>
</dependency>A global view of this framework is available under this tutorial . Its main content, allows :
1. Reading and writing bytecode
2. ClassPool
3. Class loader
4. Introspection and customization
5. Bytecode level API
6. Generics
7. Varargs
8. J2ME
9. Debug
I recommand you to take your time, read and explor this tutorial, one can customize and use the java code snippit according to his need.
Here is a javaassist use case example :
Let's suppose that our need is to add AspectJ annotations to a given class, once it is already copiled.
Assuming that our application is under Maven, This can be done by configuring a new Maven goal in the main pom.xml and execute it when "mvn install" or "mvn compile" command is called.
Java code snippet:
The Maven job that add the AspectJ annotation is set in ordinary java Main class. Maven launch it eachtime it generate the byte code. the required configuration in pom.xml file is specified bellow :
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>myPackage.MyMainclass</mainClass>
<!--if no argument, skip the tag bellow -->
<!--if no argument, skip the tag bellow -->
<arguments>
<argument>arg1</argument>
</arguments>
</configuration>
</plugin>
Now, this is what our concrete class looks like :
@Aspect(value="")
public class MyPointCutAspect{
@Pointcut(value="")
public void firstPointCutMeth(MyObj1 obj1){}
@Pointcut(value="")
public void secondPointCutMeth(MyObj2 obj2){}
@Pointcut(value="")
public void thirdPointCutMeth(MyObj3 obj3){}
}
Now, this is what our concrete class looks like :
@Aspect(value="")
public class MyPointCutAspect{
@Pointcut(value="")
public void firstPointCutMeth(MyObj1 obj1){}
@Pointcut(value="")
public void secondPointCutMeth(MyObj2 obj2){}
@Pointcut(value="")
public void thirdPointCutMeth(MyObj3 obj3){}
}
So, the aim of our java class package.myMainclass is to add some custum AspectJ pointcut expressions to MyPointCutAspect methods. If MyPointCutAspect class is compiled like as it is, the generated AspectJ expression is empty, and no AspectJ is bound.
With Javassist we modify and add dynamically custum AspectJ pointcut expression to the generated byte code.
Imagine that we like to add, by javaassit, thoses two AspectJ pointcut expressions :
1- "execution(* myPackage.MyClass.MyMethod1(myPackage.MyObj1)) && args(obj1)" as a value of MyPointCutAspect.firstPointCut method expression.
2- "execution(* myPackage.MyClass.MyMethod2(myPackage.MyObj2)) && args(obj2)" as a value of MyPointCutAspect.secondPintCut method expression.
All we need is to define it inside the java Main class myPackage.MyMainclass like below :
public class MyMainclass {
public static void main(String[] args) throws Exception {
MyMainclass process= new MyMainclass();
String expression1=execution(* myPackage.MyClass.MyMethod1(myPackage.MyObj1)) && args(obj1)";
String expression2="execution(* myPackage.MyClass.MyMethod2(myPackage.MyObj2)) && args(obj2)";
process.createDynamicPointCut(expression1,expression2);
}
private void createDynamicPointCut(final String expression1,final String expression2) throws ClassNotFoundException {
try {
final ClassPool pool = ClassPool.getDefault();
// Tell Javassist where to look for classes - into our ClassLoader
pool.appendClassPath(new LoaderClassPath(getClass().getClassLoader()));
final CtClass dymanicPointCutAspect = pool.get("myPackage.MyPointCutAspect");
final ClassFile ccFile = dymanicPointCutAspect.getClassFile();
final ConstPool constpool = ccFile.getConstPool();
// add @PointCut annotation to dymanicPointCutAspect's declared
// Methods.
addPointCutAnnotationToMethod(dymanicPointCutAspect.getDeclaredMethod("firstPointCutMeth"), expression1, constpool);
addPointCutAnnotationToMethod(dymanicPointCutAspect.getDeclaredMethod("secondPointCutMeth"),expression2, constpool);
addPointCutAnnotationToMethod(dymanicPointCutAspect.getDeclaredMethod("thirdPointCutMeth"),"", constpool);
dymanicPointCutAspect.writeFile("./target/classes");
} catch (NotFoundException e) {
} catch (IOException e) {
} catch (CannotCompileException e) {
}
}
private void addPointCutAnnotationToMethod(final CtMethod declaredMethod, final String pointCutExpression, final ConstPool constpool)throws ClassNotFoundException {
// create @PointCut annotation
AnnotationsAttribute attribute = new AnnotationsAttribute(constpool,AnnotationsAttribute.visibleTag);
Annotation pointCutAnnot = new Annotation("org.aspectj.lang.annotation.Pointcut", constpool);
pointCutAnnot.addMemberValue("value", new StringMemberValue(pointCutExpression, constpool));
attribute.addAnnotation(pointCutAnnot);
// add @PointCut annotation to method
declaredMethod.getMethodInfo().addAttribute(attribute);
System.out.println("--method -- " + declaredMethod.getName()+ " -- the annotation "+ Arrays.toString(declaredMethod.getAnnotations()));
}
}
Now, any mvn install command call, add automatically those expression to the given method.
Hope you enjoy.
Aucun commentaire:
Enregistrer un commentaire