Не зная специфики вашего кода и настроек, невозможно быть уверенным, но это напоминает мне о проблеме, с которой я столкнулся, когда попробовал свои силы в пользовательских загрузчиках классов.
Сценарий таков:
- Загрузчик (собственно JVM / Tomcat / любой другой) загружает ваш код
- Ваш загрузчик классов загружает ваши добавления, недоступные в указанном выше пути к классам.
- Ваш код ссылается на эти добавления.
- Эти дополнения недоступны в том же пространстве имен, что и код, загруженный загрузчиком.
- Ваш код в пространстве имен загрузчика запускается, пытается ссылаться на код в вашем пользовательском пространстве имен, и этоне виден из пространства имен загрузчика.Таким образом, в этот момент JVM завершается с ошибкой NoClassDefFound.
Причина этого заключается в том, что иерархия загрузчика классов работает только в одном направлении: то есть код из подпространств имен (дочерние загрузчики классов) недоступен(видимый) в более широком родительском пространстве имен (родительский загрузчик классов);и нет хорошего способа взломать эту систему.
К счастью, вы можете определить интерфейсы, которые доступны в родительском пространстве имен, а затем реализовать их в коде, видимом только в подпространстве имен.Затем код, который находится только в родительском пространстве имен, может использовать это имя интерфейса для целей приведения и вызова метода и, таким образом, получить доступ к вашему компоненту подпространства имен независимо.
Чтобы это работало, вы должны убедиться, чтоВаш пользовательский загрузчик классов не только загружает компоненты, к которым родительский загрузчик не имеет доступа (т. е. находится вне его пути к классам), но также и те биты вашего кода , которые напрямую взаимодействуют с этими компонентами и явно ссылаются на эти символы (названия / методы и т. д.).В противном случае ссылки на эти компоненты оказываются в родительском пространстве имен (помните, загрузчик загрузчика классов по умолчанию загружает весь ваш собственный код), и вы возвращаетесь к исходной проблеме.
Вы можете сделать это, подорвав предполагаемую загрузку классовмодель делегации.Как правило, вы должны отложить загрузчик родительский, прежде чем пытаться загрузить классы самостоятельно.Однако теперь вы должны заранее проверить, что ваш код не касается ни одного из этих компонентов, недоступных для родительского загрузчика.Вероятно, самый простой способ - настроить путь к коду таким образом, чтобы загрузчик классов поддерживал набор имен классов, которые он должен загружать сам, а не позволять загрузчику их загружать родительский загрузчик.
Вы должны найти способ как-то сообщить своему пользовательскому загрузчику классов, для этого можно использовать аннотацию типа в объявлениях классов.Идея заключается в том, что ваш загрузчик классов анализирует классы, загруженные родителем, и если он находит конкретную пользовательскую аннотацию для имени типа, он будет вызывать методы для аннотации, чтобы получить имя символов класса, которые он не должен разрешать своему родительскому загрузчику.load.
Пример:
@MyCustomAnnotation(implementation="my.custom.package.MyImpl")
public class MyFeatureProvider {
public MyFeature getFeature() { // return an instance of MyImpl here }
}
Обратите внимание, что поскольку класс MyFeatureProvider
будет загружен раньше, чем MyImpl
, ваш загрузчик классов будет заранее знать оаннотация в MyFeatureProvider
, поэтому он сможет переопределить модель делегирования по умолчанию для MyImpl.Поскольку остальная часть вашего кода взаимодействует только с MyImpl
как экземпляр MyFeature
, родительский загрузчик никогда не должен блокировать при виде неопределенных символов - и ошибка ClassNoDefFound устранена.