Java dynamically load class

Java Dynamically Loading a class

I am attempting to load classes dynamically into a component. I am using a file chooser to select the .JAR file that will be loaded and then a option pane to get the name of the class. I have trawled the internet looking for how to convert a java file to a URL in order to load it in URLClassLoader and I have come up with:

File myFile = filechooser.getSelectedFile(); String className = JOptionPane.showInputDialog( this, "Class Name:", "Class Name", JOptionPane.QUESTION_MESSAGE); URL myUrl= null; try < myUrl = myFile.toURL(); >catch (MalformedURLException e) < >URLClassLoader loader = new URLClassLoader(myUrl); loader.loadClass(className); 

Is ‘traulled’ a word? The only thing google suggests is ‘trolled’ 🙂 urbandictionary.com/define.php?term=trolled

5 Answers 5

I like the ClassPathHacker class mentioned in the answer by Zellus, but it’s full of deprecated calls and bad practices, so here’s a rewritten version that also caches the Classloader and the addUrl method:

import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.io.IOException; import java.io.File; public class ClassPathHacker < private static final ClassURLCLASSLOADER = URLClassLoader.class; private static final Class[] PARAMS = new Class[] < URL.class >; public static void addFile(final String s) throws IOException < addFile(new File(s)); >public static void addFile(final File f) throws IOException < addURL(f.toURI().toURL()); >public static void addURL(final URL u) throws IOException< final URLClassLoader urlClassLoader = getUrlClassLoader(); try< final Method method = getAddUrlMethod(); method.setAccessible(true); method.invoke(urlClassLoader, new Object[] < u >); > catch(final Exception e) < throw new IOException( "Error, could not add URL to system classloader"); >> private static Method getAddUrlMethod() throws NoSuchMethodException < if(addUrlMethod == null)< addUrlMethod = URLCLASSLOADER.getDeclaredMethod("addURL", PARAMS); >return addUrlMethod; > private static URLClassLoader urlClassLoader; private static Method addUrlMethod; private static URLClassLoader getUrlClassLoader() < if(urlClassLoader == null)< final ClassLoader sysloader = ClassLoader.getSystemClassLoader(); if(sysloader instanceof URLClassLoader)< urlClassLoader = (URLClassLoader) sysloader; >else < throw new IllegalStateException( "Not an UrlClassLoader: " + sysloader); >> return urlClassLoader; > > 

Источник

Java Reflection — Dynamic Class Loading and Reloading

It is possible to load and reload classes at runtime in Java, though it is not as straightforward as one might have hoped. This text will explain when and how you can load and reload classes in Java.

You can argue whether Java’s dynamic class loading features are really part of Java Reflection, or a part of the core Java platform. Anyways, the article has been put in the Java Reflection trail in lack of a better place to put it.

The ClassLoader

All classes in a Java application are loaded using some subclass of java.lang.ClassLoader . Loading classes dynamically must therefore also be done using a java.lang.ClassLoader subclass.

When a class is loaded, all classes it references are loaded too. This class loading pattern happens recursively, until all classes needed are loaded. This may not be all classes in the application. Unreferenced classes are not loaded until the time they are referenced.

The ClassLoader Hierarchy

Class loaders in Java are organized into a hierarchy. When you create a new standard Java ClassLoader you must provide it with a parent ClassLoader . If a ClassLoader is asked to load a class, it will ask its parent class loader to load it. If the parent class loader can’t find the class, the child class loader then tries to load it itself.

Class Loading

The steps a given class loader uses when loading classes are:

  1. Check if the class was already loaded.
  2. If not loaded, ask parent class loader to load the class.
  3. If parent class loader cannot load class, attempt to load it in this class loader.
Читайте также:  What is char in python

When you implement a class loader that is capable of reloading classes you will need to deviate a bit from this sequence. The classes to reload should not be requested loaded by the parent class loader. More on that later.

Dynamic Class Loading

Loading a class dynamically is easy. All you need to do is to obtain a ClassLoader and call its loadClass() method. Here is an example:

public class MainClass 

Dynamic Class Reloading

Dynamic class reloading is a bit more challenging. Java's builtin Class loaders always checks if a class is already loaded before loading it. Reloading the class is therefore not possible using Java's builtin class loaders. To reload a class you will have to implement your own ClassLoader subclass.

Even with a custom subclass of ClassLoader you have a challenge. Every loaded class needs to be linked. This is done using the ClassLoader.resolve() method. This method is final, and thus cannot be overridden in your ClassLoader subclass. The resolve() method will not allow any given ClassLoader instance to link the same class twice. Therefore, everytime you want to reload a class you must use a new instance of your ClassLoader subclass. This is not impossible, but necessary to know when designing for class reloading.

Designing your Code for Class Reloading

As stated earlier you cannot reload a class using a ClassLoader that has already loaded that class once. Therefore you will have to reload the class using a different ClassLoader instance. But this poses som new challenges.

Every class loaded in a Java application is identified by its fully qualified name (package name + class name), and the ClassLoader instance that loaded it. That means, that a class MyObject loaded by class loader A, is not the same class as the MyObject class loaded with class loader B. Look at this code:

MyObject object = (MyObject) myClassReloadingFactory.newInstance("com.jenkov.MyObject");

Notice how the MyObject class is referenced in the code, as the type of the object variable. This causes the MyObject class to be loaded by the same class loader that loaded the class this code is residing in.

If the myClassReloadingFactory object factory reloads the MyObject class using a different class loader than the class the above code resides in, you cannot cast the instance of the reloaded MyObject class to the MyObject type of the object variable. Since the two MyObject classes were loaded with different class loaders, the are regarded as different classes, even if they have the same fully qualified class name. Trying to cast an object of the one class to a reference of the other will result in a ClassCastException .

It is possible to work around this limitation but you will have to change your code in either of two ways:

  1. Use an interface as the variable type, and just reload the implementing class.
  2. Use a superclass as the variable type, and just reload a subclass.

Here are two coresponding code examples:

MyObjectInterface object = (MyObjectInterface) myClassReloadingFactory.newInstance("com.jenkov.MyObject");
MyObjectSuperclass object = (MyObjectSuperclass) myClassReloadingFactory.newInstance("com.jenkov.MyObject");

Either of these two methods will work if the type of the variable, the interface or superclass, is not reloaded when the implementing class or subclass is reloaded.

To make this work you will of course need to implement your class loader to let the interface or superclass be loaded by its parent. When your class loader is asked to load the MyObject class, it will also be asked to load the MyObjectInterface class, or the MyObjectSuperclass class, since these are referenced from within the MyObject class. Your class loader must delegate the loading of those classes to the same class loader that loaded the class containing the interface or superclass typed variables.

ClassLoader Load / Reload Example

The text above has contained a lot of talk. Let's look at a simple example. Below is an example of a simple ClassLoader subclass. Notice how it delegates class loading to its parent except for the one class it is intended to be able to reload. If the loading of this class is delegated to the parent class loader, it cannot be reloaded later. Remember, a class can only be loaded once by the same ClassLoader instance.

As said earlier, this is just an example that serves to show you the basics of a ClassLoader 's behaviour. It is not a production ready template for your own class loaders. Your own class loaders should probably not be limited to a single class, but a collection of classes that you know you will need to reload. In addition, you should probably not hardcode the class paths either.

public class MyClassLoader extends ClassLoader < public MyClassLoader(ClassLoader parent) < super(parent); >public Class loadClass(String name) throws ClassNotFoundException < if(!"reflection.MyObject".equals(name)) return super.loadClass(name); try < String url = "file:C:/data/projects/tutorials/web/WEB-INF/" + "classes/reflection/MyObject.class"; URL myUrl = new URL(url); URLConnection connection = myUrl.openConnection(); InputStream input = connection.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int data = input.read(); while(data != -1)< buffer.write(data); data = input.read(); >input.close(); byte[] classData = buffer.toByteArray(); return defineClass("reflection.MyObject", classData, 0, classData.length); > catch (MalformedURLException e) < e.printStackTrace(); >catch (IOException e) < e.printStackTrace(); >return null; > >

Below is an example use of the MyClassLoader .

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException < ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader(); MyClassLoader classLoader = new MyClassLoader(parentClassLoader); Class myObjectClass = classLoader.loadClass("reflection.MyObject"); AnInterface2 object1 = (AnInterface2) myObjectClass.newInstance(); MyObjectSuperClass object2 = (MyObjectSuperClass) myObjectClass.newInstance(); //create new class loader so classes can be reloaded. classLoader = new MyClassLoader(parentClassLoader); myObjectClass = classLoader.loadClass("reflection.MyObject"); object1 = (AnInterface2) myObjectClass.newInstance(); object2 = (MyObjectSuperClass) myObjectClass.newInstance(); >

Here is the reflection.MyObject class that is loaded using the class loader. Notice how it both extends a superclass and implements an interface. This is just for the sake of the example. In your own code you would only have to one of the two - extend or implement.

public class MyObject extends MyObjectSuperClass implements AnInterface2 < //. body of class . override superclass methods // or implement interface methods >

Источник

How do you dynamically compile and load external java classes? [duplicate]

(This question is similar to many questions I have seen but most are not specific enough for what I am doing) Background: The purpose of my program is to make it easy for people who use my program to make custom "plugins" so to speak, then compile and load them into the program for use (vs having an incomplete, slow parser implemented in my program). My program allows users to input code into a predefined class extending a compiled class packaged with my program. They input the code into text panes then my program copies the code into the methods being overridden. It then saves this as a .java file (nearly) ready for the compiler. The program runs javac (java compiler) with the saved .java file as its input. My question is, how do I get it so that the client can (using my compiled program) save this java file (which extends my InterfaceExample) anywhere on their computer, have my program compile it (without saying "cannot find symbol: InterfaceExample") then load it and call the doSomething() method? I keep seeing Q&A's using reflection or ClassLoader and one that almost described how to compile it, but none are detailed enough for me/I do not understand them completely.

check out JANINO. i use it to create classes from java source at runtime. i prefer to have them compile direct to memory and into a classloader. no need to even save the .class files. docs.codehaus.org/display/JANINO/Home

I would use that, but the way my program works loading straight into memory might not be what users want, and the compiled classes need to be easily found and shared between users. This might be a useful answer for some people though.

cool. Janino can output to files as well, but if that's what you want, i recommend the JavaCompiler as per the accepted answer.

2 Answers 2

The following is based on the example given in the JavaDocs

This will save a File in the testcompile directory (based on the package name requirements) and the compile the File to a Java class.

package inlinecompiler; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class InlineCompiler < public static void main(String[] args) < StringBuilder sb = new StringBuilder(64); sb.append("package testcompile;\n"); sb.append("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff <\n"); sb.append(" public void doStuff() <\n"); sb.append(" System.out.println(\"Hello world\");\n"); sb.append(" >\n"); sb.append(">\n"); File helloWorldJava = new File("testcompile/HelloWorld.java"); if (helloWorldJava.getParentFile().exists() || helloWorldJava.getParentFile().mkdirs()) < try < Writer writer = null; try < writer = new FileWriter(helloWorldJava); writer.write(sb.toString()); writer.flush(); >finally < try < writer.close(); >catch (Exception e) < >> /** Compilation Requirements *********************************************************************************************/ DiagnosticCollector diagnostics = new DiagnosticCollector(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null); // This sets up the class path that the compiler will use. // I've added the .jar file that contains the DoStuff interface within in it. List optionList = new ArrayList(); optionList.add("-classpath"); optionList.add(System.getProperty("java.class.path") + File.pathSeparator + "dist/InlineCompiler.jar"); Iterable compilationUnit = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(helloWorldJava)); JavaCompiler.CompilationTask task = compiler.getTask( null, fileManager, diagnostics, optionList, null, compilationUnit); /********************************************************************************************* Compilation Requirements **/ if (task.call()) < /** Load and execute *************************************************************************************************/ System.out.println("Yipe"); // Create a new custom class loader, pointing to the directory that contains the compiled // classes, this should point to the top of the package structure! URLClassLoader classLoader = new URLClassLoader(new URL[]); // Load the class from the classloader by name. Class loadedClass = classLoader.loadClass("testcompile.HelloWorld"); // Create a new instance. Object obj = loadedClass.newInstance(); // Santity check if (obj instanceof DoStuff) < // Cast to the DoStuff interface DoStuff stuffToDo = (DoStuff)obj; // Run it baby stuffToDo.doStuff(); >/************************************************************************************************* Load and execute **/ > else < for (Diagnosticdiagnostic : diagnostics.getDiagnostics()) < System.out.format("Error on line %d in %s%n", diagnostic.getLineNumber(), diagnostic.getSource().toUri()); >> fileManager.close(); > catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) < exp.printStackTrace(); >> > public static interface DoStuff < public void doStuff(); >> 

Now updated to include suppling a classpath for the compiler and loading and execution of the compiled class!

Источник

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