load class not in classpath dynamically in web application - without using custom classloader

本秂侑毒 提交于 2021-02-07 08:13:14

问题


I am developing a web application.

  1. The web application generates java classes on the fly. For example it generates class com.people.Customer.java
  2. In my code, I dynamically compile this to get com.people.Customer.class and store in some directory say repository/com/people/Customer.class which is not on the classpath of my application server.My application server(I am using WebSphere Application Server/Apache Tomcat etc) picks up the classes from the WEB-INF/classes directory. The Classloader would use this to load the classes.
  3. After compilation I need to load this class so that it becomes accessible to other classes using it after its creation.
  4. When I use Thread.currentThread().getContextClassLoader().loadClass(com.people.Customer) obviously the Classloader is not able to load the class, since its not on the classpath(not in WEB-INF/classes). Due to similar reasons, getResource(..) or getResourceAsStream(..) also does not work.

I need a way to :

Read the class Customer.class maybe as a stream (or any other way would do) and then load it. Following are the constraints:

  1. I cannot add the repository folder to the WEB-INF/classes folder.
  2. I cannot create a new Custom ClassLoader. If I create a new ClassLoader and this loads the class, it will not be accessible to its parent ClassLoader.

Is there any way of achieving this?

If not this, in the worse case, is there a way of overriding the default class loader with a custom class loader for web applications the same classloader should be used to load applications throughout entire lifecycle of my web application.

Appreciate any solution :)


回答1:


You need a custom class loader to do this, and in this classloader you need to re-define a method findClass(String name)

An example:

public class CustomClassLoader extends ClassLoader {

    final String basePath = "/your/base/path/to/directory/named/repository/";

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        String fullName = name.replace('.', '/');
        fullName += ".class";

        String path = basePath + fullName ;
        try {
            FileInputStream fis = new FileInputStream(path);
            byte[] data = new byte[fis.available()];
            fis.read(data);
            Class<?> res = defineClass(name, data, 0, data.length);
            fis.close();

            return res;
        } catch(Exception e) {
            return super.findClass(name);
        }
    }
}

Then, you'll be load classes from custom location. For example:

Class<?> clazz = Class.forName("my.pretty.Clazz", true, new CustomClassLoader());
Object obj = clazz.newInstance();

Doing this, you tell JVM that class named my.pretty.Clazz should be loaded by your custom class loader, which knows how and where from to load your custom class. It resolves full class name (like my.pretty.Clazz) to file name (in our case: /your/base/path/to/directory/named/repository/my/pretty/Clazz.class), then loads obtained resource as a byte array, and finally converts this array to a Class instance.

This example is very simple and demonstrates a general technique about how to load custom classes as in your case. I suggest you to read some articles about class loading, for example this one.




回答2:


Short answer: No

Without a custom ClassLoader, you cannot dynamically load classes.

However, your assumption that you cannot use a custom ClassLoader because your other objects loaded by the WebApp ClassLoader would be unable to use these newly loaded classes is incorrect. All you need is a generic way to use these newly created classes - like a common interface or a meta-description (Beans Introspector for accessing bean properties).

But if you are using third-party libraries like Hibernate and you are dynamically loading entities at runtime which are to be persisted, then you will have a hard time, but imho it is possible.




回答3:


Sure you can do this. Just get the web classloader and call the defineClass() method using reflection (it is protected, so be sure to call setAccessible(true) on the method. defineClass() takes a byte array, so it doesn't make any difference where you class is from. Make sure that the class name is unique and you're loading it only once, or you'll have complicated classloading problems.



来源:https://stackoverflow.com/questions/2473645/load-class-not-in-classpath-dynamically-in-web-application-without-using-custo

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!