Creating classes at runtime java

Prabhath’s Blog

When we write programs usual method is to write all the code compile it and run. But when we try to complex things some times we may want to generate a new class and use it at runtime. As an example I wanted to create a class which parse a xml message and create a new Java object from that data. This is used in web server. So that result object varies according to the web service which is deployed. The solution was to inspect the service at deployment time and create a new parser class according to that in the service deployment time. It has to be noted that this parser had to be generated as a .class file and dynamically added to the classpath. So the result was to use a bytecode manupulation library. There are few bytecode manuplulation libraries available as ASM and JavaAssist.
I used JavaAssist for this. The reason for using JavaAssist is it is simple to create a new class from the scratch using JavaAssit. When we create new class files using JavaAssit we have to create all the methods in the new class as Strings and the pass them to Java Assist. As a example if we have to add amethod called sayHellow() to new class we can create it as a String like this.

String s=»public void sayHello()

/** * @param c Any class which can be used to get the class loader (c.getClassLoarder()) can be passed using object.getClass() methood * @param name * Name of the to be generated class * @param methods * Methods to be created as Strings * @param interfaces * interfaces to be implemented as Strings * @param directory * the directory generated class files has to be written */ public static void createClass(Class c, String name, List methods, List interfaces, String directory) < String temp = null; try < ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(c)); CtClass cc = pool.makeClass(name); if (interfaces != null) < for (String s : interfaces) < CtClass anInterface = pool.get(s); cc.addInterface(anInterface); >> for (String s : methods) < temp = s; CtMethod m = CtNewMethod.make(s, cc); cc.addMethod(m); >cc.writeFile(directory); > catch (Exception e) < // TODO throw System.out.println(temp); e.printStackTrace(); >>
//Here directory is the path which class files were written public static Class loadClass(String className,String directory, ClassLoader loader) throws Exception < File f = new File(directory); java.net.URL[] urls = new java.net.URL[] < f.toURI().toURL() >; ClassLoader cl = new URLClassLoader(urls, loader); Class cls = cl.loadClass(className); return cls; >

Note — JavaAssist does not support generics. If we use genarics java assist throws an exception. We have to use fully qualified names for types. As an example we have to use java.util.List for List type.

Читайте также:  Все файлы html заражены вирусом

Источник

Generating classes at runtime and invoking their methods with and without the use of Reflection in Java

The generation of classes at runtime is an advanced topic that requires a lot of knowledge that can be reduced if you use particular libraries that perform the most complex functions to accomplish this task.
So, for this purpose, we can use the ClassFactory component and the sources generating components of the Burningwave Core library by adding to our pom.xml the following dependency:

  • see a complete example about source code generators
  • read this guide where you also can find a link to an example about generating classes by using libraries located outside the runtime class paths
  • go here for more examples
  • ask for assistance on GitHub
  • ask Stack Overflow for assistance

Once the classes have been compiled and loaded, it is possible to invoke their methods in severals ways as shown at the end of the example below.

UnitSourceGenerator unitSG = UnitSourceGenerator.create("packagename").addClass( ClassSourceGenerator.create( TypeDeclarationSourceGenerator.create("MyExtendedClass") ).addModifier( Modifier.PUBLIC //generating new method that override MyInterface.convert(LocalDateTime) ).addMethod( FunctionSourceGenerator.create("convert") .setReturnType( TypeDeclarationSourceGenerator.create(Comparable.class) .addGeneric(GenericSourceGenerator.create(Date.class)) ).addParameter(VariableSourceGenerator.create(LocalDateTime.class, "localDateTime")) .addModifier(Modifier.PUBLIC) .addAnnotation(AnnotationSourceGenerator.create(Override.class)) .addBodyCodeLine("return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());") .useType(ZoneId.class) ).addConcretizedType( MyInterface.class ).expands(ToBeExtended.class) ); System.out.println("\nGenerated code:\n" + unitSG.make()); //With this we store the generated source to a path unitSG.storeToClassPath(System.getProperty("user.home") + "/Desktop/bw-tests"); ComponentSupplier componentSupplier = ComponentContainer.getInstance(); ClassFactory classFactory = componentSupplier.getClassFactory(); //this method compile all compilation units and upload the generated classes to default //class loader declared with property "class-factory.default-class-loader" in //burningwave.properties file (see "Overview and configuration"). //If you need to upload the class to another class loader use //loadOrBuildAndDefine(LoadOrBuildAndDefineConfig) method ClassFactory.ClassRetriever classRetriever = classFactory.loadOrBuildAndDefine( unitSG ); Class generatedClass = classRetriever.get( "packagename.MyExtendedClass" ); ToBeExtended generatedClassObject = Constructors.newInstanceOf(generatedClass); generatedClassObject.printSomeThing(); System.out.println( ((MyInterface)generatedClassObject).convert(LocalDateTime.now()).toString() ); //You can also invoke methods by casting to Virtual (an interface offered by the //library for faciliate use of runtime generated classes) Virtual virtualObject = (Virtual)generatedClassObject; //Invoke by using reflection virtualObject.invoke("printSomeThing"); //Invoke by using MethodHandle virtualObject.invokeDirect("printSomeThing"); System.out.println( ((Date)virtualObject.invokeDirect("convert", LocalDateTime.now())).toString() ); classRetriever.close(); 

In this short article we have learned how to generate classes at runtime in Java, how to load them and create instances; the complete source is available at this at this page.

Источник

Generating classes at runtime and invoking their methods with and without the use of reflection

For this purpose is necessary the use of ClassFactory component and of the sources generating components but, first of all, we must add to our pom.xml the following dependency:

 org.burningwave core 12.62.7 

… And to use Burningwave Core as a Java module, add the following to your module-info.java :

requires org.burningwave.core;

Once the sources have been set in UnitSourceGenerator objects, they must be passed to loadOrBuildAndDefine method of ClassFactory with the ClassLoader where you want to define new generated classes. This method performs the following operations: tries to load all the classes present in the UnitSourceGenerator through the class loader, if at least one of these is not found it proceeds to compiling all the UnitSourceGenerators and uploading their classes on class loader: in this case, keep in mind that if a class with the same name was previously loaded by the class loader, the compiled class will not be uploaded. If you need more information you can:

  • see a complete example about source code generators
  • read this guide where you also can find a link to an example about generating classes by using libraries located outside the runtime class paths
  • go here for more examples

Once the classes have been compiled and loaded, it is possible to invoke their methods in severals ways as shown at the end of the example below.

package org.burningwave.core.examples.classfactory; import static org.burningwave.core.assembler.StaticComponentContainer.Constructors; import java.lang.reflect.Modifier; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; import org.burningwave.core.Virtual; import org.burningwave.core.assembler.ComponentContainer; import org.burningwave.core.assembler.ComponentSupplier; import org.burningwave.core.classes.AnnotationSourceGenerator; import org.burningwave.core.classes.ClassFactory; import org.burningwave.core.classes.ClassSourceGenerator; import org.burningwave.core.classes.FunctionSourceGenerator; import org.burningwave.core.classes.GenericSourceGenerator; import org.burningwave.core.classes.TypeDeclarationSourceGenerator; import org.burningwave.core.classes.UnitSourceGenerator; import org.burningwave.core.classes.VariableSourceGenerator; public class RuntimeClassExtender < @SuppressWarnings("resource") public static void execute() throws Throwable < UnitSourceGenerator unitSG = UnitSourceGenerator.create("packagename").addClass( ClassSourceGenerator.create( TypeDeclarationSourceGenerator.create("MyExtendedClass") ).addModifier( Modifier.PUBLIC //generating new method that override MyInterface.convert(LocalDateTime) ).addMethod( FunctionSourceGenerator.create("convert") .setReturnType( TypeDeclarationSourceGenerator.create(Comparable.class) .addGeneric(GenericSourceGenerator.create(Date.class)) ).addParameter(VariableSourceGenerator.create(LocalDateTime.class, "localDateTime")) .addModifier(Modifier.PUBLIC) .addAnnotation(AnnotationSourceGenerator.create(Override.class)) .addBodyCodeLine("return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());") .useType(ZoneId.class) ).addConcretizedType( MyInterface.class ).expands(ToBeExtended.class) ); System.out.println("\nGenerated code:\n" + unitSG.make()); //With this we store the generated source to a path unitSG.storeToClassPath(System.getProperty("user.home") + "/Desktop/bw-tests"); ComponentSupplier componentSupplier = ComponentContainer.getInstance(); ClassFactory classFactory = componentSupplier.getClassFactory(); //this method compile all compilation units and upload the generated classes to default //class loader declared with property "class-factory.default-class-loader" in //burningwave.properties file (see "Overview and configuration"). //If you need to upload the class to another class loader use //loadOrBuildAndDefine(LoadOrBuildAndDefineConfig) method ClassFactory.ClassRetriever classRetriever = classFactory.loadOrBuildAndDefine( unitSG ); ClassgeneratedClass = classRetriever.get( "packagename.MyExtendedClass" ); ToBeExtended generatedClassObject = Constructors.newInstanceOf(generatedClass); generatedClassObject.printSomeThing(); System.out.println( ((MyInterface)generatedClassObject).convert(LocalDateTime.now()).toString() ); //You can also invoke methods by casting to Virtual (an interface offered by the //library for faciliate use of runtime generated classes) Virtual virtualObject = (Virtual)generatedClassObject; //Invoke by using reflection virtualObject.invoke("printSomeThing"); //Invoke by using MethodHandle virtualObject.invokeDirect("printSomeThing"); System.out.println( ((Date)virtualObject.invokeDirect("convert", LocalDateTime.now())).toString() ); classRetriever.close(); > public static class ToBeExtended < public void printSomeThing() < System.out.println("Called method printSomeThing"); >> public static interface MyInterface < public Comparableconvert(LocalDateTime localDateTime); > public static void main(String[] args) throws Throwable < execute(); >>

Источник

Оцените статью