Использование javax.tools.ToolProvider из пользовательского загрузчика классов?
Кажется, невозможно использовать javax.tools.ToolProvider
из пользовательского загрузчика классов в соответствии с требованиями Ant или Webstart: http://bugs.sun.com/view_bug.do?bug_id=6548428
javax.tools.ToolProvider.getSystemJavaCompiler()
грузы javax.tools.JavaCompiler
в URLClassLoader, чьим родителем является системный загрузчик классов. Похоже, API не позволяет пользователям указывать родительский загрузчик классов.
Как можно использовать javax.tools.JavaCompiler
из пользовательского загрузчика классов?
Например:
- Муравьиные грузы
MyParserTask
MyParserTask
разбирает исходный код JavaMyParserTask
загружаетсяAntClassLoader
который делегирует системному загрузчику классовjavax.tools.JavaCompiler
загружаетсяURLClassLoader
это делегаты системному загрузчику классов
Позже, MyParserTask
вызывает:
javax.tools.CompilationTask task = compiler.getTask(...);
com.sun.source.util.JavacTask javacTask = (com.sun.source.util.JavacTask) task;
javacTask.parse().next().accept(visitor, unused); // parsing happens here
- Видя, как эти два класса находятся на разных загрузчиках классов, похоже, нет возможности
MyParserTask
взаимодействовать сJavacTask
не получаяClassCastException
ошибки.
Есть идеи?
4 ответа
У меня была точно такая же проблема. Я использую пользовательскую задачу ant для сканирования AST для определенных видов вызовов методов. Мое решение, которое может не подходить для вас, состояло в том, чтобы создать экземпляр самого компилятора вместо использования ToolProvider.
Я заменил
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
с
JavaCompiler compiler = (JavaCompiler)Class.forName("com.sun.tools.javac.api.JavacTool").newInstance();
Это, конечно, не будущее и не безопасно во всех средах, но это вариант в зависимости от ваших потребностей. Если у кого-то есть лучший способ использовать ToolProvider в пользовательских задачах Ant, пожалуйста, поделитесь.
Простой ответ заключается в том, что один и тот же класс, загружаемый двумя разными загрузчиками классов, является другим типом, и никогда не допускается перекрестное присвоение двух типов. Вот и все. Вам нужно, чтобы оба класса использовали один и тот же загрузчик классов, чтобы получить общий класс.
Обычно это происходит в результате нарушения преимущественной отсрочки загрузки класса для родителя ClassLoader. Проще говоря, любой загрузчик классов должен сначала попросить своего родителя загрузить класс, прежде чем он попытается загрузить его сам. В противном случае возникают всевозможные "интересные" проблемы.
В вашем конкретном примере, так как A вызвал B, это был загрузчик класса B, который не смог делегировать своему родителю, так как если A может видеть целевой класс, загрузчик класса B не должен был загружать его, учитывая, что A вызвал B и, следовательно, A загрузчик классов или какой-то его предок загружен B.
Эта проблема часто возникает с OSGi. Некоторые люди придумали "загрузчики классов моста", см., Например, эту статью (которая, вероятно, только соединяет интерфейсы, а не подклассы, поэтому, возможно, вы не можете использовать его напрямую).
Если есть только несколько методов, которые вы хотите вызвать для "чужого" объекта, вы также можете избежать размышлений:
javax.tools.CompilationTask task;
task.getClass().getMethod("someMethodInTheSubclassThatICannotSee").invoke("a");
Основываясь на идее рефлексии, возможно, язык скриптов тоже будет полезен (Groovy, Beanshell, JavaScript).
У меня были похожие проблемы, я должен был загрузить tools.jar Когда я обнаружил, что подкласс не загружается. подробности в этой ссылке https://stackru.com/questions/14619179/webappclassloader-loadclass-cannot-find-class-at-runtime Поскольку я упоминаю, что это может быть не очень хорошим решением, так как мы пытаемся возиться с загрузкой классов через программа. нашел некоторые заметки здесь также полезными http://easternlights-wisdomtree.blogspot.in/2013/02/classloading-blues-part1.html