Προγραμματισμός

Συμβουλή Java 105: Εξασφάλιση του classpath με JWhich

Κάποια στιγμή, οι προγραμματιστές αντιμετωπίζουν απογοήτευση όταν αντιμετωπίζουν το Java classpath. Δεν είναι πάντα σαφές ποια κλάση θα φορτώσει ο φορτωτής τάξης, ειδικά όταν το classpath της εφαρμογής σας κατακλύζεται από καταλόγους και αρχεία. Σε αυτό το άρθρο, θα παρουσιάσω ένα εργαλείο που μπορεί να εμφανίσει το απόλυτο όνομα διαδρομής του φορτωμένου αρχείου κλάσης.

Βασικά στοιχεία του Classpath

Η εικονική μηχανή Java (JVM) χρησιμοποιεί έναν φορτωτή κλάσης για να φορτώνει τάξεις που χρησιμοποιούνται από μια εφαρμογή ανάλογα με τις ανάγκες. ο CLASSPATH η μεταβλητή περιβάλλοντος λέει στον φορτωτή κλάσης πού να βρει τάξεις τρίτων και καθορισμένων από τον χρήστη. Μπορείτε επίσης να καθορίσετε το classpath σε βάση ανά εφαρμογή με το -κατάπαθο Όρισμα γραμμής εντολών JVM, το οποίο παρακάμπτει το classpath που καθορίζεται στο CLASSPATH μεταβλητή περιβάλλοντος.

Οι καταχωρίσεις του Classpath μπορούν να είναι καταλόγοι που περιέχουν αρχεία κλάσης για κλάσεις που δεν βρίσκονται σε πακέτο, ο ριζικός κατάλογος πακέτων για τάξεις σε ένα πακέτο ή αρχεία αρχειοθέτησης (όπως αρχεία .zip ή .jar) που περιέχουν κλάσεις. Οι καταχωρήσεις Classpath διαχωρίζονται με άνω και κάτω τελεία σε συστήματα τύπου Unix και διαχωρισμένες με ερωτηματικά σε συστήματα MS Windows.

Οι φορτωτές τάξης οργανώνονται σε μια ιεραρχία αντιπροσωπείας, με κάθε φορτωτή τάξης να έχει έναν γονικό φορτωτή τάξης. Όταν ζητείται από έναν φορτωτή κλάσης να βρει μια τάξη, αναθέτει πρώτα το αίτημα στον μητρικό φορτωτή κλάσης πριν επιχειρήσει να βρει την ίδια την τάξη. Ο φορτωτής κλάσης συστήματος, ο προεπιλεγμένος φορτωτής κλάσης που παρέχεται από το JDK ή JRE που είναι εγκατεστημένος στο σύστημά σας, φορτώνει τάξεις τρίτων και καθορισμένων από τον χρήστη χρησιμοποιώντας το CLASSPATH μεταβλητή περιβάλλοντος ή το -κατάπαθο Όρισμα γραμμής εντολών JVM. Ο φορτωτής κλάσης συστήματος μεταβιβάζει την κλάση επέκτασης για να φορτώσει κλάσεις που χρησιμοποιούν τον μηχανισμό επέκτασης Java. Ο φορτωτής κλάσης επέκτασης μεταβιβάζει στον φορτωτή κλάσης bootstrap (το buck σταματά εδώ!) Για να φορτώσει τις βασικές κλάσεις JDK.

Μπορείτε να αναπτύξετε εξειδικευμένους φορτωτές τάξεων για να προσαρμόσετε τον τρόπο με τον οποίο το JVM φορτώνει δυναμικά τις κλάσεις. Για παράδειγμα, οι περισσότεροι servlet κινητήρες χρησιμοποιούν ένα προσαρμοσμένο class loader για να φορτώσουν δυναμικά servlet class που έχουν αλλάξει σε καταλόγους που καθορίζονται σε ένα custom classpath.

Ιδιαίτερης σημασίας και μεγάλη ανησυχία, ο φορτωτής τάξης θα φορτώνει τάξεις με τη σειρά που εμφανίζονται στο classpath. Ξεκινώντας με την πρώτη καταχώρηση classpath, ο φορτωτής κλάσης επισκέπτεται κάθε καθορισμένο κατάλογο ή αρχείο αρχειοθέτησης προσπαθώντας να βρει την κλάση για φόρτωση. Φορτώνεται η πρώτη τάξη που βρίσκει με το σωστό όνομα και αγνοούνται οι υπόλοιπες καταχωρήσεις του classpath.

Ακούγεται απλό, έτσι;

Εξαπάτηση του Classpath

Είτε θα το παραδέχονταν είτε όχι, τόσο οι αρχάριοι όσο και οι βετεράνοι προγραμματιστές της Java έχουν κάποια στιγμή (συνήθως στη χειρότερη δυνατή στιγμή!) Εξαπατήθηκαν από το επαχθές classpath. Καθώς ο αριθμός εξαρτημένων τάξεων τρίτου μέρους και καθορισμένων από τον χρήστη αυξάνεται για μια εφαρμογή και το classpath γίνεται έδαφος ντάμπινγκ για κάθε πιθανό αρχείο καταλόγου και αρχείου, δεν είναι πάντα προφανές ποια κλάση θα φορτώσει πρώτα ο φορτωτής κλάσης. Αυτό ισχύει ιδιαίτερα στην ατυχή περίπτωση που το classpath περιέχει διπλές καταχωρήσεις κλάσης. Θυμηθείτε, ο φορτωτής κλάσης φορτώνει την πρώτη σωστά ονομαζόμενη κλάση που βρίσκει στο classpath και "κρύβει" αποτελεσματικά όλες τις άλλες κατηγορίες χαμηλότερης προτεραιότητας.

Είναι πολύ εύκολο να πέσεις θύματα αυτής της απάτης στο classpath. Μετά από μια κουραστική μέρα δουλείας πάνω από ένα καυτό πληκτρολόγιο, προσαρτάτε έναν κατάλογο στο classpath σε μια προσπάθεια να φορτωθεί η τελευταία και μεγαλύτερη έκδοση μιας κλάσης στην εφαρμογή, ενώ δεν γνωρίζουμε ότι μια άλλη έκδοση της τάξης βρίσκεται σε έναν κατάλογο υψηλότερη προτεραιότητα στο classpath. Ωχ!

JWhich: Ένα απλό εργαλείο classpath

Το πρόβλημα προτεραιότητας που είναι εγγενές σε μια δήλωση επίπεδης διαδρομής δεν είναι μοναδικό για το Java classpath. Η εξεύρεση λύσης στο πρόβλημα απαιτεί μόνο να σταθείτε στους ώμους των θρυλικών γίγαντων λογισμικού. Το λειτουργικό σύστημα Unix οι οποίες Η εντολή παίρνει ένα όνομα και εμφανίζει το όνομα διαδρομής του αρχείου που θα εκτελεστεί αν το όνομα είχε εκδοθεί ως εντολή. Διασχίζει ουσιαστικά το ΜΟΝΟΠΑΤΙ μεταβλητή περιβάλλοντος για να εντοπίσετε την πρώτη εμφάνιση της εντολής. Αυτό ακούγεται σαν ένα ισχυρό εργαλείο για τη διαχείριση του Java classpath, επίσης. Εμπνευσμένη από αυτήν την έννοια, ξεκίνησα να γράφω ένα βοηθητικό πρόγραμμα Java που θα μπορούσε να πάρει ένα όνομα κλάσης Java και να εμφανίσει το απόλυτο όνομα διαδρομής του αρχείου κλάσης που θα φορτώνει ο φορτωτής κλάσης, όπως ορίζεται από το classpath.

Το ακόλουθο παράδειγμα χρήσης του JWhich εμφανίζει το απόλυτο όνομα διαδρομής της πρώτης εμφάνισης του com.clarkware.ejb.ShoppingCartBean κλάση που θα φορτωθεί από τον φορτωτή κλάσης, η οποία τυχαίνει να βρίσκεται σε έναν κατάλογο:

 > java JWhich com.clarkware.ejb.ShoppingCartBean Class 'com.clarkware.ejb.ShoppingCartBean' βρέθηκε στο '/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class' 

Το ακόλουθο παράδειγμα χρήσης του JWhich εμφανίζει το απόλυτο όνομα διαδρομής της πρώτης εμφάνισης του javax.servlet.http.HttpServlet class που θα φορτωθεί από το class loader, το οποίο τυχαίνει να είναι συσκευασμένο σε αρχείο αρχειοθέτησης:

 > java JWhich javax.servlet.http.HttpServlet Class 'javax.servlet.http.HttpServlet' found in 'file: /home/mclark/lib/servlet.jar! /javax/servlet/http/HttpServlet.class' 

Πώς λειτουργεί το JWhich

Για να προσδιορίσετε ξεκάθαρα ποια κλάση θα φορτωθεί πρώτα στο classpath, πρέπει να μπείτε στο μυαλό του φορτωτή τάξης. Αυτό δεν είναι τόσο δύσκολο όσο ακούγεται - το ρωτάτε! Ο σχετικός πηγαίος κώδικας για JWhich ακολουθεί. Για τον πλήρη πηγαίο κώδικα, ανατρέξτε στην ενότητα Πόροι.

1: δημόσια κλάση JWhich {2: 3: / ** 4: * Εκτυπώνει το απόλυτο όνομα διαδρομής του αρχείου κλάσης 5: * που περιέχει το καθορισμένο όνομα κλάσης, όπως καθορίζεται 6: * από το τρέχον classpath. 7: * 8: * @param className Όνομα της τάξης. 9: * / 10: δημόσιο στατικό κενό που (String className) {11: 12: if (! ClassName.startsWith ("/")) {13: className = "/" + className; 14:} 15: className = className.replace ('.', '/'); 16: className = className + ".class"; 17: 18: java.net.URL classUrl = 19: νέο JWhich (). GetClass (). GetResource (className); 20: 21: if (classUrl! = Null) {22: System.out.println ("\ nClass '" + className + 23: "' found in \ n '" + classUrl.getFile () + "'"); 24:} αλλιώς {25: System.out.println ("\ nClass '" + className + 26: "' not found in \ n '" + 27: System.getProperty ("java.class.path") + "' "); 28:} 29:} 30: 31: δημόσιο κενό στατικού κενού (String args []) {32: if (args.length> 0) {33: JWhich.which (args [0]); 34:} αλλιώς {35: System.err.println ("Χρήση: java JWhich"); 36:} 37:} 38:} 

Πρώτον, πρέπει να κάνετε λίγο μασάζ στο όνομα της τάξης για να κερδίσετε την αποδοχή του φορτωτή τάξης (γραμμές 12-16). Προετοιμασία ενός "/" στο όνομα της τάξης δίνει εντολή στον φορτωτή κλάσης να ταιριάζει με το όνομα της κλάσης κατά λέξη στο classpath, αντί να προσπαθεί να προκαλέσει σιωπηρά το όνομα του πακέτου της κλάσης που καλεί. Μετατροπή κάθε εμφάνισης του "." to "/" μορφοποιεί το όνομα κλάσης ως έγκυρο όνομα πόρου διεύθυνσης URL που απαιτείται από τον φορτωτή κλάσης.

Στη συνέχεια, ο φορτωτής κλάσης ερωτείται (γραμμές 18-19) για τον πόρο που ταιριάζει με το σωστά μορφοποιημένο όνομα κλάσης. Κάθε Τάξη το αντικείμενο διατηρεί μια αναφορά στο ClassLoader αντικείμενο που το φόρτωσε, έτσι ο φορτωτής κλάσης που φόρτωσε το JWhich η ίδια η τάξη ανακρίνεται εδώ. ο Class.getResource () Η μέθοδος εκχωρεί πράγματι στον φορτωτή κλάσης που φόρτωσε την κλάση, επιστρέφοντας μια διεύθυνση URL για ανάγνωση του πόρου του αρχείου τάξης ή μηδενικό εάν δεν ήταν δυνατή η εύρεση πόρου αρχείου τάξης με το καθορισμένο όνομα κλάσης στο τρέχον classpath.

Τέλος, εμφανίζεται το απόλυτο όνομα διαδρομής του αρχείου κλάσης που περιέχει το καθορισμένο όνομα κλάσης, εάν βρέθηκε στο τρέχον classpath (γραμμές 21-24). Ως βοήθημα εντοπισμού σφαλμάτων, εάν το αρχείο κλάσης δεν βρέθηκε στο τρέχον classpath, λαμβάνετε την τιμή του java.class.path ιδιότητα συστήματος για την εμφάνιση του τρέχοντος classpath (γραμμές 24-28).

Είναι εύκολο να φανταστεί κανείς πώς αυτό το απλό κομμάτι κώδικα θα μπορούσε να χρησιμοποιηθεί σε ένα servlet Java χρησιμοποιώντας το classpath του servlet engine ή ένα Enterprise JavaBean (EJB) χρησιμοποιώντας το classpath του διακομιστή EJB. Εάν το JWhich η τάξη φορτώθηκε από τον προσαρμοσμένο φορτωτή κλάσης σε έναν servlet κινητήρα, για παράδειγμα, τότε ο φορτωτής κλάσης του servlet engine θα χρησιμοποιηθεί για την εύρεση τάξεων. Εάν ο φορτωτής κλάσης του servlet κινητήρα δεν είναι σε θέση να εντοπίσει μια κλάση, θα εκχωρήσει στον μητρικό φορτωτή κλάσης. Σε γενικές γραμμές, όταν JWhich φορτώνεται από έναν φορτωτή κλάσης, είναι σε θέση να βρει όλες τις τάξεις που φορτώνονται από τον φορτωτή κλάσης ή από οποιονδήποτε μητρικό φορτωτή κλάσης.

συμπέρασμα

Εάν η αναγκαιότητα είναι η μητέρα κάθε εφεύρεσης, τότε ένα εργαλείο που βοηθά στη διαχείριση του Java classpath έχει καθυστερήσει πολύ. Οι ομάδες συνομιλιών και οι λίστες αλληλογραφίας που σχετίζονται με Java είναι γεμάτες από ερωτήσεις που σχετίζονται με το classpath. Πρέπει να μειώσουμε το εμπόδιο για την είσοδο νέων προγραμματιστών, ώστε όλοι να συνεχίσουμε να εργαζόμαστε σε υψηλότερα επίπεδα αφαίρεσης. JWhich είναι ένα απλό, αλλά ισχυρό, εργαλείο που θα σας βοηθήσει να αποκτήσετε το Java classpath σε οποιοδήποτε περιβάλλον.

Ο Mike Clark είναι ανεξάρτητος σύμβουλος της Clarkware Consulting, που ειδικεύεται στην αρχιτεκτονική, το σχεδιασμό και την ανάπτυξη που βασίζεται σε Java χρησιμοποιώντας τεχνολογίες J2EE. Πρόσφατα ολοκλήρωσε την ανάπτυξη και ανάπτυξη ενός διακομιστή ανταλλαγής XML από επιχείρηση σε επιχείρηση (B2B) και είναι επί του παρόντος σύμβουλος για ένα έργο κατασκευής ενός προϊόντος διαχείρισης επιδόσεων J2EE.

Μάθετε περισσότερα σχετικά με αυτό το θέμα

  • Λάβετε τον πλήρη πηγαίο κώδικα για αυτό το άρθρο

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/12/jwhich.zip

  • Μια πλήρης έκδοση του JWhich, συμπεριλαμβανομένου ενός επικυρωτή classpath, είναι διαθέσιμη στο

    //www.clarkware.com/software/jwhich.zip

  • Επίσημη τεκμηρίωση για το Sun JDK και πώς ασχολείται με το classpath για τις διάφορες επίσημα υποστηριζόμενες πλατφόρμες είναι διαθέσιμη στη διεύθυνση

    //java.sun.com/j2se/1.3/docs/tooldocs/findingclasses.html

  • Για λεπτομέρειες σχετικά με τον τρόπο ρύθμισης του classpath σε πλατφόρμες Unix και Windows, ανατρέξτε στην ενότητα "Ρύθμιση του classpath" στο:
  • Unix

    //java.sun.com/j2se/1.3/docs/tooldocs/solaris/classpath.html

  • Παράθυρα

    //java.sun.com/j2se/1.3/docs/tooldocs/win32/classpath.html

  • Δείτε όλα τα προηγούμενα Συμβουλές Java και υποβάλετε το δικό σας

    //www.javaworld.com/javatips/jw-javatips.index.html

  • Για περισσότερα κόλπα Java, εγγραφείτε στο ITworld.com δωρεάν Εκπαιδευτής Java ενημερωτικό δελτίο

    //www.itworld.com/cgi-bin/subcontent12.cgi

  • Μιλήστε στη συζήτηση Java Beginner, υπό την εποπτεία του JavaWorld συγγραφέας Geoff Friesen

    //www.itworld.com/jump/jw-javatip105/forums.itworld.com/[email protected]@.ee6b804/1195!skip=1125

Αυτή η ιστορία, "Java Tip 105: Mastering the classpath with JWhich" δημοσιεύτηκε αρχικά από το JavaWorld.