17 Stimmen

Warum kann System.setProperty() den Klassenpfad nicht zur Laufzeit ändern?

Ich beziehe mich auf die Frage zur programmatischen Änderung des Klassenpfads.

Ich habe gelesen und herausgefunden, dass es eine Funktion gibt unter System Klasse als getproperties, wo wir die Eigenschaften abrufen können und dann auch mit setProperties() einstellen können.

Die Antworten, die ich erhielt, lauteten jedoch, dass es nicht funktionieren würde. Ich habe das nicht selbst ausprobiert, aber ich nehme den Anruf entgegen.

Nur um zu klären, warum diese setProperty()- und getProperty()-Methoden dann da sind, wenn sie sie zur Laufzeit nicht ändern können. Oder ist dies spezifisch für die Klassenpfad-Eigenschaft nur?

Ich wäre dankbar, wenn jemand ein Szenario vorstellen könnte, in dem sie wirklich hilfreich sind.

0 Stimmen

Informationen über die Eigenschaft classpath hinzugefügt

0 Stimmen

Hinweis: Beanshell (sowie Ant und Groovy) ist in der Lage, .jars dynamisch zu laden, aber das Problem ist, dass für bestimmte Dinge der Versuch, sie in Beanshells benutzerdefiniertem Classloader zu laden, nicht erwünscht ist. es funktioniert also nicht immer, aber meistens.

14voto

Sie können natürlich jederzeit alle gewünschten Systemeigenschaften einstellen. Die Frage ist nur, ob es irgendeine Auswirkung haben wird. Im Fall des Klassenpfads lautet die Antwort NEIN. Der Systemklassenlader wird zu einem sehr frühen Zeitpunkt in der Startsequenz initialisiert. Er kopiert den Klassenpfad in seine eigenen Datenstrukturen, und die Eigenschaft classpath wird nicht mehr gelesen. Eine Änderung der Eigenschaft hat keine Auswirkungen auf das System.

Dies kann zwei Gründe haben. Der geringere Grund ist die Leistung. Es kann sein, dass Sie eine Art Datenstruktur zum schnellen Auffinden von Ressourcen benötigen, und es kann ineffizient sein, den Klassenpfad jedes Mal neu zu analysieren. Der wichtigere Grund ist die Sicherheit. Sie wollen nicht, dass eine bösartige Klasse den Klassenpfad unter Ihnen ändert und eine kompromittierte Version einer anderen Klasse lädt.

14voto

Dave Jarvis Punkte 29378

Klassenpfad modifizieren

Auch wenn Sie den Klassenpfad nicht über die Systemeigenschaften festlegen können (weil die JVM die Systemeigenschaften einmal liest: beim Start), können Sie den Klassenpfad dennoch ändern, indem Sie zwangsweise den Befehl addURL Methode des Classloaders. Beachten Sie, dass die nachstehende Lösung den aktuellen Thread nicht berücksichtigt. Daher ist sie möglicherweise nicht in allen Situationen korrekt.

Beispiellösung

Die ursprüngliche Quelle auf der Website von Sun für den folgenden Code wurde entfernt:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;                   

import java.io.File;
import java.io.IOException;

import java.net.URL;
import java.net.URLClassLoader;

/**
 * Allows programs to modify the classpath during runtime.              
 */                                                                     
public class ClassPathUpdater {                                         
  /** Used to find the method signature. */                             
  private static final Class[] PARAMETERS = new Class[]{ URL.class };   

  /** Class containing the private addURL method. */
  private static final Class<?> CLASS_LOADER = URLClassLoader.class;

  /**
   * Adds a new path to the classloader. If the given string points to a file,
   * then that file's parent file (i.e., directory) is used as the
   * directory to add to the classpath. If the given string represents a
   * directory, then the directory is directly added to the classpath.
   *
   * @param s The directory to add to the classpath (or a file, which
   * will relegate to its directory).
   */
  public static void add( String s )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    add( new File( s ) );
  }

  /**
   * Adds a new path to the classloader. If the given file object is
   * a file, then its parent file (i.e., directory) is used as the directory
   * to add to the classpath. If the given string represents a directory,
   * then the directory it represents is added.
   *
   * @param f The directory (or enclosing directory if a file) to add to the
   * classpath.
   */
  public static void add( File f )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    f = f.isDirectory() ? f : f.getParentFile();
    add( f.toURI().toURL() );
  }

  /**
   * Adds a new path to the classloader. The class must point to a directory,
   * not a file.
   *
   * @param url The path to include when searching the classpath.
   */
  public static void add( URL url )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    Method method = CLASS_LOADER.getDeclaredMethod( "addURL", PARAMETERS );
    method.setAccessible( true );
    method.invoke( getClassLoader(), new Object[]{ url } );
  }

  private static URLClassLoader getClassLoader() {
    return (URLClassLoader)ClassLoader.getSystemClassLoader();
  }
}

Der Link funktioniert nicht mehr: http://forums.sun.com/thread.jspa?threadID=300557

Beispiel für die Verwendung

Das folgende Beispiel fügt Folgendes hinzu /home/user/dev/java/app/build/com/package zur Laufzeit in den Klassenpfad aufnehmen:

try {
  ClassPathUpdater.add( "/home/user/dev/java/app/build/com/package/Filename.class" );
}
catch( Exception e ) {
  e.printStackTrace();
}

0 Stimmen

Ihr erster Link ist down.... Und btw, dies scheint davon auszugehen, dass der Classloader der Anwendung vom Typ URLClassLoader ist, was möglicherweise nicht der Fall ist, nicht wahr?

0 Stimmen

@Pacerier: Ich danke Ihnen. Es ist möglich, einen eigenen URLClassLoader zu haben. Die Lösung könnte robuster gemacht werden, indem alle Unterklassen von ClassLoader ( docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html ) und versuchen, ihre loadClass Methode unter Verwendung eines binären Klassennamens.

0 Stimmen

Ich meine, dass der Standard-Classloader der Anwendung von der System der kein URLClassLoader sein darf. (es kann auch etwas ganz anderes sein).

10voto

VonC Punkte 1117238

System.setProperty kann verwendet werden, um einen Sicherheits- oder Protokoll-Handler am Anfang eines Programms zu setzen. Zum Beispiel:

/*
Add the URL handler to the handler property. This informs 
IBMJSSE what URL handler to use to handle the safkeyring 
support. In this case IBMJCE.
*/
System.setProperty("java.protocol.handler.pkgs", "com.ibm.crypto.provider");

oder für mit SSL :

System.setProperty("javax.net.ssl.keyStore", context.getRealPath(KEYSTORE));
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", context.getRealPath(TRUSTSTORE));
System.setProperty("javax.net.debug", "ssl");
HttpClient httpClient = new HttpClient();
GetMethod httpGet = new GetMethod("https://something.com");
httpClient.executeMethod(httpGet);
return new String(httpGet.getResponseBody());

Aber Vorsicht, denn es ändert die Umgebung zur Laufzeit für ALLE Anwendungen, die in der gleichen JVM laufen .
Wenn zum Beispiel eine Anwendung mit Saxon und die andere mit Xalan laufen muss und beide System.setProperty verwenden, um die transformerFactory zu setzen, dann werden Sie Probleme bekommen

Wie in Überwachtes System.setProperty Artikel,
System.setProperty() kann ein böser Aufruf sein.

  • Sie ist zu 100% fadenfeindlich
  • Sie enthält superglobale Variablen
  • Es ist äußerst schwierig, Fehler zu beheben, wenn sich diese Variablen zur Laufzeit auf mysteriöse Weise ändern.

Bezüglich der Klassenpfad-Eigenschaft, als Ich sagte in einer früheren Frage kann sie nicht einfach zur Laufzeit geändert werden.

Insbesondere wird die Java-System-Eigenschaft java.class.path verwendet, um einen verknüpften Link zu erstellen, wenn das JRE instanziiert wird, dann wird nicht wieder gelesen . Daher haben Änderungen, die Sie an der Eigenschaft vornehmen, keine wirklichen Auswirkungen auf die bestehende virtuelle Maschine.

0 Stimmen

Danke VonC, sehr informativ. Allerdings, warum das Verhalten ist anders für Klasse Pfad Eigenschaft. Wenn Sie sagen, am Anfang des Programms, ich nehme an, das Programm wurde ausgeführt und es ist Art der ersten Sache, die es tut. Korrekt?

0 Stimmen

Ja, die Systemeigenschaft java.class.path ist eher eine informatorische Gefälligkeit für den Entwickler. Wie bei allen Eigenschaften ist es erlaubt, sie über setProperty() zu ändern, aber das ist nicht mehr als das Ändern eines Strings in einer Map.

0 Stimmen

Ich denke, java.security.properties ist eine weitere Systemeigenschaft, die beim Start der JVM gelesen und nicht erneut referenziert wird. Ich vermute, dass die Möglichkeit, eine der beiden Eigenschaften zur Laufzeit zu ändern, mehr Probleme verursachen als lösen würde.

3voto

Zorkus Punkte 484

Es gibt auch eine Möglichkeit, java.library.path in der Laufzeit zu ändern, um das zu tun, tun Sie einfach:

System.setProperty( "java.library.path", newPath);
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null); // that's the key.

Wenn dieses private statische Feld in der ClassLoader-Klasse auf null gesetzt wird, wird ClassLoader beim nächsten Versuch, die native Bibliothek zu laden, erneut mit dem neuen Wert in java.library.path initialisiert.

0voto

Martin Probst Punkte 9167

Der Grundgedanke von getProperty() ist, dass Programme/Code von außerhalb der JVM konfiguriert werden können, indem Eigenschaften auf der Befehlszeile mit der Option java -Dfoo=bar Syntax.

Da Sie möglicherweise ein bestimmtes Verhalten in anderen Softwarekomponenten (z. B. einer Protokollierungskomponente) in Situationen konfigurieren möchten, in denen Sie keine Kontrolle über die Befehlszeile haben - denken Sie an die Bereitstellung in einem Servlet-Container -, können Sie diese Funktion nutzen. setProperty() ist eine praktische Möglichkeit, Einstellungen programmatisch zu ändern, z. B. bevor Sie Ihr Protokollierungsprogramm instanziieren.

Das Problem, das sich bei der classpath Das Problem besteht darin, dass Programme solche Systemeigenschaften in der Regel nur genau einmal lesen, nämlich bei der ersten Initialisierung. Wenn Sie also den Klassenpfad nach dem Start der JVM ändern, ändert sich für Ihre Anwendung selbst nichts mehr, da die JVM bereits initialisiert ist, und wenn Sie die Protokollierungskonfiguration ändern, nachdem Sie bereits eine Logger-Instanz (oder was auch immer) erhalten haben, hat das normalerweise auch keine Auswirkungen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X