загружать класс не в classpath динамически в веб-приложении - без использования пользовательского загрузчика классов - PullRequest
4 голосов
/ 19 марта 2010

Я занимаюсь разработкой веб-приложения.

  1. Веб-приложение генерирует Java-классы на лету. Например, он генерирует класс com.people.Customer.java
  2. В моем коде я динамически компилирую это, чтобы получить com.people.Customer.class и сохраняю в некотором каталоге, скажем repository/com/people/Customer.class, который не находится на пути к классам моего сервера приложений. Мой сервер приложений (я использую WebSphere Application Server / Apache Tomcat и т. Д.) Выбирает классы из каталога WEB-INF/classes. Classloader будет использовать это для загрузки классов.
  3. После компиляции мне нужно загрузить этот класс, чтобы он стал доступен другим классам, использующим его после его создания.
  4. Когда я использую Thread.currentThread().getContextClassLoader().loadClass(com.people.Customer), очевидно, что Classloader не может загрузить класс, так как его нет в пути к классам (не в WEB-INF/classes). По аналогичным причинам getResource(..) или getResourceAsStream(..) также не работает.

Мне нужен способ:

Прочитайте класс Customer.class, возможно, как поток (или любым другим способом), а затем загрузите его. Ниже приведены ограничения:

  1. Я не могу добавить папку репозитория в папку WEB-INF/classes.
  2. Я не могу создать новый Custom ClassLoader. Если я создаю новый ClassLoader и он загружает класс, он не будет доступен его родительскому ClassLoader.

Есть ли способ достичь этого?

Если нет, то в худшем случае есть способ переопределить загрузчик классов по умолчанию пользовательским загрузчиком классов для веб-приложений. Этот же загрузчик классов следует использовать для загрузки приложений на протяжении всего жизненного цикла моего веб-приложения.

Цените любое решение :)

Ответы [ 3 ]

2 голосов
/ 23 ноября 2012

Для этого нужен пользовательский загрузчик классов, и в этом загрузчике классов вам необходимо переопределить метод findClass(String name)

Пример:

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);
        }
    }
}

Тогда вы будете загружать классы из пользовательского местоположения. Например:

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

Делая это, вы сообщаете JVM, что класс с именем my.pretty.Clazz должен быть загружен вашим пользовательским загрузчиком классов, который знает как и , откуда загружать ваш пользовательский класс. Он разрешает полное имя класса (например, my.pretty.Clazz) в имя файла (в нашем случае: /your/base/path/to/directory/named/repository/my/pretty/Clazz.class), затем загружает полученный ресурс в виде байтового массива и, наконец, преобразует этот массив в экземпляр Class.

Этот пример очень прост и демонстрирует общую технику загрузки пользовательских классов, как в вашем случае. Я предлагаю вам прочитать несколько статей о загрузке классов, например this .

1 голос
/ 20 марта 2010

Конечно, вы можете сделать это. Просто получите веб-загрузчик классов и вызовите метод defineClass (), используя отражение (оно защищено, поэтому обязательно вызовите метод setAccessible (true) для метода. DefineClass () принимает байтовый массив, так что это не имеет никакого значения, где вы class from. Убедитесь, что имя класса уникально, и вы загружаете его только один раз, иначе у вас возникнут сложные проблемы с загрузкой классов.

1 голос
/ 19 марта 2010

Краткий ответ: Нет

Без пользовательского ClassLoader вы не можете динамически загружать классы.

Однако ваше предположение о том, что вы не можете использовать пользовательский ClassLoader, поскольку ваши другие объекты, загруженные WebApp ClassLoader, не смогут использовать эти недавно загруженные классы, неверно. Все, что вам нужно, это универсальный способ использования этих вновь созданных классов - например, общий интерфейс или мета-описание (Beans Introspector для доступа к свойствам бина).

Но если вы используете сторонние библиотеки, такие как Hibernate, и вы динамически загружаете сущности во время выполнения, которые должны быть сохранены, тогда у вас будет трудное время, но imho это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...