Можно ли «добавить» к classpath динамически в Java? - PullRequest
22 голосов
/ 31 декабря 2008
java -classpath ../classes;../jar;. parserTester

Как получить программную функциональность в приведенной выше команде? Мол, можно ли запустить как:

java parserTester

и получить тот же результат? Я пытался использовать URLClassLoader, но он изменяет путь к классам и не добавляет к нему.

Thanx!


Спасибо за ответ, Милхоус. Но это то, что я пытаюсь сделать. Как можно сначала поставить банку в путь к классу? Я тоже пытался использовать собственный загрузчик классов: (

Это работает .. Но извините, что мне нужно запустить его только как: java parserTester Хотелось бы узнать, возможно ли такое? ??? 1012 *

Это должно быть так, потому что у меня есть parserTester.java и .class в отдельной папке. Мне нужно сохранить структуру файла. ParserTester использует банку в отдельной папке.

Ответы [ 7 ]

22 голосов
/ 31 декабря 2008

Вы можете использовать java.net.URLClassLoader для загрузки классов с любым программно-определенным списком URL, которые вы хотите:

открытый класс URLClassLoader расширяет SecureClassLoader

Этот загрузчик классов используется для загрузки классы и ресурсы из поиска путь URL-адресов, относящихся к обоим JAR файлы и каталоги. Любой URL, который заканчивается знаком «/», как предполагается, относится к каталог. В противном случае URL Предполагается, что он ссылается на файл JAR, который будет открыт при необходимости.

AccessControlContext потока который создал экземпляр URLClassLoader будет использоваться, когда впоследствии загрузка классов и ресурсы.

Загруженные классы по умолчанию предоставлено разрешение только получить доступ к URL-адресам, указанным при URLClassLoader создан.

С: 1.2

И небольшая сложная работа может расширить его для поддержки использования имен путей с подстановочными символами для выбора целых каталогов JAR (этот код имеет некоторые ссылки на служебные методы, но их реализация должна быть очевидна в контексте):

/**
 * Add classPath to this loader's classpath.
 * <p>
 * The classpath may contain elements that include a generic file base name.  A generic basename
 * is a filename without the extension that may begin and/or end with an asterisk.  Use of the
 * asterisk denotes a partial match. Any files with an extension of ".jar" whose base name match
 * the specified basename will be added to this class loaders classpath.  The case of the filename is ignored.
 * For example "/somedir/*abc" means all files in somedir that end with "abc.jar", "/somedir/abc*"
 * means all files that start with "abc" and end with ".jar", and "/somedir/*abc*" means all files
 * that contain "abc" and end with ".jar".
 *
 */
public void addClassPath(String cp) {
    String                              seps=File.pathSeparator;                // separators

    if(!File.pathSeparator.equals(";")) { seps+=";"; }                          // want to accept both system separator and ';'
    for(StringTokenizer st=new StringTokenizer(cp,seps,false); st.hasMoreTokens(); ) {
        String pe=st.nextToken();
        File   fe;
        String bn=null;

        if(pe.length()==0) { continue; }

        fe=new File(pe);
        if(fe.getName().indexOf('*')!=-1) {
            bn=fe.getName();
            fe=fe.getParentFile();
            }

        if(!fe.isAbsolute() && pe.charAt(0)!='/' && pe.charAt(0)!='\\') { fe=new File(rootPath,fe.getPath()); }
        try { fe=fe.getCanonicalFile(); }
        catch(IOException thr) {
            log.diagln("Skipping non-existent classpath element '"+fe+"' ("+thr+").");
            continue;
            }
        if(!GenUtil.isBlank(bn)) {
            fe=new File(fe,bn);
            }
        if(classPathElements.contains(fe.getPath())) {
            log.diagln("Skipping duplicate classpath element '"+fe+"'.");
            continue;
            }
        else {
            classPathElements.add(fe.getPath());
            }

        if(!GenUtil.isBlank(bn)) {
            addJars(fe.getParentFile(),bn);
            }
        else if(!fe.exists()) {                                                 // s/never be due getCanonicalFile() above
            log.diagln("Could not find classpath element '"+fe+"'");
            }
        else if(fe.isDirectory()) {
            addURL(createUrl(fe));
            }
        else if(fe.getName().toLowerCase().endsWith(".zip") || fe.getName().toLowerCase().endsWith(".jar")) {
            addURL(createUrl(fe));
            }
        else {
            log.diagln("ClassPath element '"+fe+"' is not an existing directory and is not a file ending with '.zip' or '.jar'");
            }
        }
    log.diagln("Class loader is using classpath: \""+classPath+"\".");
    }

/**
 * Adds a set of JAR files using a generic base name to this loader's classpath.  See @link:addClassPath(String) for
 * details of the generic base name.
 */
public void addJars(File dir, String nam) {
    String[]                            jars;                                   // matching jar files

    if(nam.endsWith(".jar")) { nam=nam.substring(0,(nam.length()-4)); }

    if(!dir.exists()) {
        log.diagln("Could not find directory for Class Path element '"+dir+File.separator+nam+".jar'");
        return;
        }
    if(!dir.canRead()) {
        log.error("Could not read directory for Class Path element '"+dir+File.separator+nam+".jar'");
        return;
        }

    FileSelector fs=new FileSelector(true).add("BaseName","EG",nam,true).add("Name","EW",".jar",true);
    if((jars=dir.list(fs))==null) {
        log.error("Error accessing directory for Class Path element '"+dir+File.separator+nam+".jar'");
        }
    else if(jars.length==0) {
        log.diagln("No JAR files match specification '"+new File(dir,nam)+".jar'");
        }
    else {
        log.diagln("Adding files matching specification '"+dir+File.separator+nam+".jar'");
        Arrays.sort(jars,String.CASE_INSENSITIVE_ORDER);
        for(int xa=0; xa<jars.length; xa++) { addURL(createUrl(new File(dir,jars[xa]))); }
        }
    }

private URL createUrl(File fe) {
    try {
        URL url=fe.toURI().toURL();
        log.diagln("Added URL: '"+url.toString()+"'");
        if(classPath.length()>0) { classPath+=File.pathSeparator; }
        this.classPath+=fe.getPath();
        return url;
        }
    catch(MalformedURLException thr) {
        log.diagln("Classpath element '"+fe+"' could not be used to create a valid file system URL");
        return null;
        }
    }
1 голос
/ 31 декабря 2008

Я должен согласиться с двумя другими постерами, похоже, вы слишком усложняете тестовый класс. Нет ничего необычного в том, чтобы файлы .java и .class находились в отдельных папках, хотя в зависимости от jar-файлов находится еще одна треть без программного изменения пути к классу. Если вы делаете это, потому что не хотите каждый раз вводить classpath в командной строке, я бы предложил сценарий оболочки или командный файл. Еще лучше, IDE. Вопрос, который у меня действительно возникает, заключается в том, почему вы пытаетесь управлять classpath в коде?

0 голосов
/ 27 июня 2011

Я думаю, что вам нужен "Execution Wrapper" или специфичный для платформы "Launcher" ... обычно этот компонент используется для определения вашей ОС, архитектуры и зависимостей, а затем вносит коррективы перед запуском вашего приложения. Это старая модель проектирования (речь идет о 80-х и более ранних), но она все еще используется сегодня. Идея состоит в том, что ваша программа может быть независимой от системы и среды, а программа запуска подготовит программу и расскажет программному обеспечению все, что ему нужно знать. Многие современные программы с открытым исходным кодом делают это с помощью сценариев оболочки и пакетных файлов и т. Д. Apache Tomcat, например. Вы также можете легко создать оболочку в java и запустить ее с помощью командной строки exec (обязательно добавьте «&» в конце вашей команды exec в * NIX, чтобы ваша оболочка могла выйти, оставив только работающее программное обеспечение). .. также позволяет закрыть окно оболочки, не прерывая процесс)

0 голосов
/ 31 марта 2011

Отлично хороший пост, в моем случае я сделал это, чтобы хорошо работать (примечание: специфично для Windows):

set classpath=%classpath%;../lib/*
java -cp %classpath% com.test.MyClass 
0 голосов
/ 31 декабря 2008

Вы можете написать командный файл или файл сценария оболочки для экспорта пути к классам и запуска Java-программы. В Windows

set classpath =% classpath%; ../ classes; ../ jars / * java ParserTester

В Unix, экспорт classpath =% classpath%: ../ classes: ../ jars / * java ParserTester

Если вы называете имя файла как parser.bat или parser.sh, вы можете просто запустить его, вызвав parser в соответствующей ОС.

В Java 1.6 вы можете включить все jar-файлы из каталога в classpath, просто сказав / *

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

0 голосов
/ 31 декабря 2008

Я правильно понял ?! Единственная причина, по которой у вас есть это, что вы хотите запустить свой класс без указания пути к классу и загрузить его во время выполнения? ...

Java ParserTester

вместо

java -classpath ../classes;../jar ;. parserTester

Возможно, я не понял вашу причину. Но если "это" то, что вы хотите, вы можете сделать следующее (хотя это не имеет большого смысла для меня)

  • Запустить класс
  • Из основного метода запускается другой класс, программно устанавливающий там путь к классам.
  • Конец истории.

Что-то вроде следующего "java -pseudo code"

public static void main( String [] args ) {
    String classpath = "classes;../jar";
    Runtime.getRuntime().execute("java + classpath + " parserTester ");
}

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

0 голосов
/ 31 декабря 2008

Вы можете реализовать свой собственный загрузчик классов , но этот класс / jar должен находиться в пути к классам, чтобы его можно было выполнить.

попробовать

java -cp *.jar:. myClass

или

export CLASSPATH=./lib/tool.jar:.
java myClass

или

java -jar file.jar
...