2016-08-23 4 views
0

У меня есть веб-приложение, которое есть в functionnality загрузки, которые состоят из загрузки пакет, содержащий приложение Java (он может содержит несколько зависимостей)Tomcat пользовательский загрузчик классов не удалось загрузить некоторые банки

Для этого для каждого загружаемого приложения, Я создаю пользовательский загрузчик классов для динамической загрузки пути класса приложения. Решения работает отлично, пока я не получаю эту ошибку при загрузке нового приложения:

javax.xml.stream.FactoryConfigurationError: Error creating stream factory: java.lang.ClassNotFoundException: de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl 

Я проверил, что загруженное пакет содержит staxon, а также подтвержден, что мой заказ загрузчик классов загрузить класс:

Object a=Class.forName("de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl",true , classLoader).newInstance(); 

Итак, почему это исключение и специально для этой банки?

+0

Что такое 'classLoader'? Это пользовательский 'ClassLoader', который вы написали? Какие ресурсы вы добавили в этот класс ClassLoader, чтобы он мог загружать классы? Где собственно библиотека JSON, и какой класс ClassLoader вы ожидаете загрузить? –

+0

Я использую URLClassLoader, и я добавляю баночки в мой загруженный пакет для этого загрузчика классов. Библиотека json является частью этих баннеров (мой загруженный пакет содержит в основном список баннеров) – Zizou

+0

Я попытался отладить мой пользовательский загрузчик классов см классов, пытаемся загрузить: 'общественного класса CustomClassLoader расширяет URLClassLoader { \t общественного CustomClassLoader (URL [] URLs, ClassLoader родительского) { \t \t супер (URLs, родительский); \t} \t @Override \t защищенный класс loadClass (имя String, логическое решительность) бросает ClassNotFoundException { \t \t System.out.println ("пытаетесь загрузить:" + имя); \t \t return super.loadClass (имя, разрешение); \t} \t } ' Но я обнаружил, что мой загрузчик классы не пытаются даже загрузить ** de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl ** – Zizou

ответ

0

Когда вы вызываете Class.forName(className, initialize, classLoader), вы просите JVM загрузить класс, используя этот пользовательский загрузчик классов. Если вы не укажете ClassLoader - например, просто позвонив Class.forName(className), тогда JVM будет использовать ClassLoader, известный как «контекстный классLoader» для потока. Каждый поток имеет один из них, и ваш код может изменить код.

Если у вас есть код, который вызывает класс должен быть загружен - что-то вроде этого:

MyClass foo = new MyClass(); 

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

Кроме того, если вы вызываете какой-либо код, который вы не контролируете, существует много способов, которыми этот код может привести к загрузке других классов, и поскольку у вас нет контроля над этим кодом, контекст потока ClassLoader также будет использоваться.

Итак, как вы устанавливаете контекст нити ClassLoader? Легко:

ClassLoader cl = Thread.currentThread().getContextClassLoader(); 
ClassLoader myClassLoader = ...; // You figure this out 
try 
{ 
    Thread.currentThread().setContextClassLoader(myClassLoader); 

    // Do work that requires your ClassLoader to work 
} 
finally 
{ 
    // Always restore the previous CCL after work is done 
    Thread.currentThread().setContextClassLoader(cl); 
} 

Еще одна вещь, которую вы хотите сделать, это убедиться, что ваш заказ ClassLoader делегатов любые просьбы о классовой нагрузки на родителей ClassLoader. Этот родитель ClassLoader должен, вероятно, быть ClassLoader, который, естественно, будет использоваться, если вы не пытаетесь использовать свои собственные: контекст потока ClassLoader.

Таким образом, вы, вероятно, хотите что-то вроде следующего:

ClassLoader cl = Thread.currentThread().getContextClassLoader(); 
ClassLoader myClassLoader = new MyClassLoader(cl); // Try 'cl' before your custom class loading 

try 
{ 
    Thread.currentThread().setContextClassLoader(myClassLoader); 

    // Do work that requires your ClassLoader to work 
} 
finally 
{ 
    // Always restore the previous CCL after work is done 
    Thread.currentThread().setContextClassLoader(cl); 
} 

Вы можете найти более подробную информацию о ClassLoader с, и, в частности, TCCL, в эти несколько ссылок:

+0

Спасибо agin Christopher, для загрузки, я использую 'Class.forName (className, true, myUrlClassLoader)' И еще одно важное замечание: проблема исчезает, когда я добавлял staxon jar в мое веб-приложение lib. Но мне это не нравится, потому что я хочу иметь возможность загружать новое приложение во время выполнения без перезагрузки моего сервера. – Zizou

+0

Если проблема исчезнет при добавлении библиотеки в каталог 'lib' вашего собственного приложения, тогда проблема определенно загрузчик классов контекста потока. Установите его, как указано выше, и он должен работать. Не забудьте убедиться, что ваш пользовательский класс ClassLoader делегирует другому классу ClassLoader. Если вы этого не сделаете, многое изменится, если вы измените TCCL на свой собственный. –

+0

Есть еще одна часть, запутанная для меня: Когда вы видите Кристофера **, существует много способов, которыми этот код может привести к загрузке других классов, и поскольку у вас нет контроля над этим кодом ** Но когда я загружаю class ** cl1 ** using ** Class.forname (myClass, true, myCl) **, я ожидаю, что все классы, используемые ** cl1 **, будут загружены с использованием того же загрузчика классов ** myCl **. Это еще не все верно? – Zizou

Смежные вопросы