Områdesoberoende tillgång till resurser
- Översikt
- Resurser, namn ochkontexter
- Systemresurser
- Resurser som inte är systemresurser
- Resursnamn
- Användning av metoder i java.lang.Class
- Användningsmetoder för java.lang.ClassLoader
- Säkerhet
- Exempel
Översikt
En resurs är data (bilder, ljud, text och så vidare) som ett program behöver få tillgång till på ett sätt som är oberoende av programkodens placering. Java-program kan använda två mekanismer för att få tillgång till resurser: Applets använder Applet.getCodeBase()
för att få fram bas-URL för appletkoden och utökar sedan bas-URL med en relativ sökväg för att ladda den önskade resursen, till exempel med Applet.getAudioClip(url)
. Program använder ”välkända platser” somSystem.getProperty("user.home")
ellerSystem.getProperty("java.home")
, lägger sedan till”/lib/resource” och öppnar den filen.
Metoder i klasserna Class
ochClassLoader
ger ett platsoberoende sätt att lokalisera resurser. De gör det till exempel möjligt att lokalisera resurser för:
- En applet som laddas från Internet med hjälp av flera HTTP-anslutningar.
- En applet som laddas med hjälp av JAR-filer.
- En Java Bean som laddas eller installeras i CLASSPATH.
- En ”biblioteksfil” som installeras i CLASSPATH.
De här metoderna ger inget specifikt stöd för att lokalisera lokaliserade resurser. Lokaliserade resurser stöds av internationaliseringsmöjligheterna.
Resurser, namn och sammanhang
En resurs identifieras av en sträng som består av en sekvens av understrängar, avgränsade av snedstreck (/), följt av ett resursnamn.Varje understräng måste vara en giltig Java-identifierare. Resursnamnet är av formen shortName
ellershortName.extension
. Både shortName
och extension
måste vara Java-identifierare.
Namnet på en resurs är oberoende av Javaimplementationen; i synnerhet är sökvägsseparatorn alltid ett snedstreck (/). Java-implementationen kontrollerar dock detaljerna för hur innehållet i resursen mappas till en fil, en databas eller ett annat objekt som innehåller den faktiska resursen.
Tolkningen av ett resursnamn är relativ till en klassinläsningsinstans. Metoder som implementeras av klassenClassLoader
gör denna tolkning.
Systemresurser
En systemresurs är en resurs som antingen är inbyggd i systemet eller hålls av värdimplementationen i till exempel ett lokalt filsystem. Program har tillgång till systemresurser genom metoderna ClassLoader
getSystemResource
och getSystemResourceAsStream
.
För att hitta en systemresurs i en viss implementering kan det till exempel handla om att söka i posterna i CLASSPATH. MetodernaClassLoader
söker i varje katalog, ZIP-fil eller JAR-fil i CLASSPATH efter resursfilen och, om den hittas, returnerar de antingen en InputStream
eller resursnamnet. Om den inte hittas returnerar metoderna noll. En resurs kan hittas i en annan post i CLASSPATH än den plats där klassfilen laddades.
Non-System Resources
Implementationen av getResource
på en klass som laddas beror på detaljerna i ClassLoader
-klassen. Exempel: AppletClassLoader
:
- Försöker först hitta resursen som en systemresurs, sedan, om den inte hittas,
- söker bland resurserna i arkiv (JAR-filer) som redan har laddats i den här CODEBASE, och sedan, om den inte hittas,
- använder CODEBASE och försöker hitta resursen (vilket kan innebära att man kontaktar en avlägsen plats).
Alla klassinläsare söker först efter en resurs som systemresurs, på samma sätt som när man söker efter klassfiler. Denna sökregel tillåter att alla resurser skrivs över lokalt. Klienter bör välja ett resursnamn som är unikt (till exempel genom att använda företags- eller paketnamnet som prefix).
Resursnamn
En vanlig konvention för namnet på en resurs som används av en klass är att använda det fullt kvalificerade namnet på paketet för klassen, men att konvertera alla punkter (.) till snedstreck (/) och lägga till ett resursnamn av formen name.extension
. För att stödja detta, och för att förenkla hanteringen av detaljerna i systemklasser (för vilkagetClassLoader
returnerar null), tillhandahåller klassenClass
två bekvämlighetsmetoder som anropar de lämpliga metoderna i ClassLoader
.
Resursnamnet som ges till en Class
-metod kan ha en initial start ”/” som identifierar det som ett ”absolut” namn.Resursnamn som inte börjar med ett ”/” är ”relativa”.
Absoluta namn saknar sitt inledande ”/” och skickas, utan ytterligare ändringar, till lämplig ClassLoader
-metod för att lokalisera resursen. Relativa namn modifieras enligt den konvention som beskrivits tidigare och skickas sedan till en ClassLoader
metod.
Användning av metoder i java.lang.Class
Klassen Class
implementerar flera metoder för att ladda resurser.
Metoden getResource()
returnerar en URL förresursen. URL:n (och dess representation) är specifik för implementationen och JVM:n (dvs. URL:n som erhålls i en runtimeinstans kanske inte fungerar i en annan). Dess protokoll är vanligtvis specifikt för ClassLoader
som laddar resursen. Om resursen inte finns eller inte är synlig av säkerhetsskäl returnerar metoderna null.
Om klientkoden vill läsa innehållet i resursen som en InputStream
kan den tillämpa openStream()
-metoden på URL:en. Detta är tillräckligt vanligt för att motivera att getResourceAsStream()
läggs till iClass
och ClassLoader
.getResourceAsStream()
är samma sak som att anropagetResource().openStream()
, förutom attgetResourceAsStream()
fångar upp IO-undantag och returnerar en null InputStream
.
Klientkoden kan också begära resurskällans innehåll som ett objekt genom att tillämpa java.net.URL.getContent()
metoden på URL:en. Detta är till exempel användbart när resursen innehåller data för en bild. När det gäller en bild är resultatet ett awt.image.ImageProducer
objekt, inte ettImage
objekt.
Metoderna getResource
och getResourceAsStream
hittar en resurs med ett givet namn. De returnerar null om de inte hittar en resurs med det angivna namnet. Reglerna för att söka efter resurser som är associerade med en viss klass implementeras av klassens ClassLoader. Class
-metoderna delegerar tillClassLoader
-metoder efter att ha tillämpat en namngivningskonvention: om resursnamnet börjar med ”/” används det som det är.I annat fall läggs paketets namn till, efter att ha konverterat alla punkter (.) till snedstreck (/).
public InputStream getResourceAsStream(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResourceAsStream(name); // A system class. } return cl.getResourceAsStream(name);}public java.net.URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResource(name); // A system class. } return cl.getResource(name);}
Metoden resolveName
lägger till ett prefix för paketnamnet om namnet inte är absolut, och tar bort alla inledande ”/” om namnet är absolut. Det är möjligt, om än ovanligt, att ha klasser i olika paket som delar samma resurs.
private String resolveName(String name) { if (name == null) { return name; } if (!name.startsWith("/")) { Class c = this; while (c.isArray()) { c = c.getComponentType(); } String baseName = c.getName(); int index = baseName.lastIndexOf('.'); if (index != -1) { name = baseName.substring(0, index).replace('.', '/') + "/" + name; } } else { name = name.substring(1); } return name;}
Användning av metoder i java.lang.ClassLoader
Klassen ClassLoader
har två uppsättningar metoder för att komma åt resurser. Den ena uppsättningen returnerar en InputStream
för resursen. Den andra uppsättningen returnerar en URL. Metoderna som returnerar en InputStream
är lättare att använda och uppfyller många behov, medan metoderna som returnerar URL:er ger tillgång till mer komplex information, till exempel en bild och ett ljudklipp.
ClassLoader
Förändrar resurser på samma sätt som den hanterar klasser. En ClassLoader
styr hur namnet på en resurs ska kopplas till dess innehåll. ClassLoader
tillhandahåller också metoder för åtkomst till systemresurser,analogt med systemklasserna. Class
klassen tillhandahåller några bekvämlighetsmetoder som delegerar funktionalitet till ClassLoader
metoderna.
Många Javaprogram kommer att få tillgång till dessa metoder indirekt genom I18N (lokalisering) API:erna. Andra kommer att få tillgång till dem genom metoder i Class
. Några få kommer att åberopa metoderna i ClassLoader
direkt.
Metoderna i ClassLoader
använder den givna strängen som namnet på resursen utan att tillämpa någon absolut/relativ omvandling (se metoderna i Class). Namnet bör inte ha ett inledande ”/”.
Systemresurser är sådana som hanteras direkt av värdimplementationen. De kan till exempel finnas iCLASSPATH.
Namnet på en resurs är en ”/”-separerad sekvens avidentifierare. Klassen Class
tillhandahåller bekvämlighetsmetoder för åtkomst till resurser; metoderna implementerar en konvention där paketnamnet föregås av det korta namnet påresursen.
Resurser kan nås som en InputStream
, eller enURL.
Metoden getSystemResourceAsStream
returnerar enInputStream för den specificerade systemresursen eller noll om den inte hittar resursen. Resursnamnet kan vara vilken systemresurs som helst.
Metoden getSystemResource
hittar en systemresurs med det angivna namnet. Den returnerar en URL till resursen eller noll om den inte hittar resursen. Om du anropar java.net.URL.getContent()
med URL:en returneras ett objekt som ImageProducer
, AudioClip
eller InputStream
.
Metoden getResourceAsStream
returnerar en InputStream
för den angivna resursen eller noll om den inte hittar resursen.
Metoden getResource
hittar en resurs med det angivna namnet. Den returnerar en URL till resursen eller noll om den inte hittar resursen. Om du anroparjava.net.URL.getContent()
med URL:en returneras ettobjekt som ImageProducer
, AudioClip
eller InputStream
.
Säkerhet
Då getResource()
ger tillgång till information måste den ha väldefinierade och välgrundade säkerhetsregler. Om säkerhetsöverväganden inte tillåter att en resurs är synlig i något säkerhetssammanhang, kommer getResource()
-metoden att misslyckas (returnera null) som om resursen inte fanns alls, detta för att hantera existensattacker.
Klassinläsare får inte ge tillgång till innehållet i en .classfile av både säkerhets- och prestandaskäl. Huruvida det är möjligt att erhålla en URL för en .class-fil beror på de specifika omständigheterna, vilket visas nedan.
Det finns inga specificerade säkerhetsproblem eller begränsningar när det gällerresurser som hittas av en klassinläsare som inte tillhör systemet.AppletClassLoader
ger tillgång till information som laddas in från en källplats, antingen individuellt eller i grupp genom en JAR-fil; därför bör AppletClassLoader
tillämpa samma checkConnect()
regler när det gäller URL:er genom getResource()
.
Systemet ClassLoader
ger tillgång till information i CLASSPATH. En CLASSPATH kan innehålla kataloger och JAR-filer. Eftersom en JAR-fil skapas avsiktligt har den en annan betydelse än en katalog där saker och ting kan hamna på ett mer tillfälligt sätt. Vi är särskilt strängare när det gäller att få information från en katalog än från en JAR-fil.
Om en resurs finns i en katalog:
-
getResource()
kommer anropen att användaFile.exists()
för att avgöra om motsvarande fil skall göras synlig för användaren. Kom ihåg attFile.exists()
användercheckRead()
-metoden i säkerhetshanteraren. - Det samma gäller
getResourceAsStream()
.
Om resursen finns i en JAR-fil:
-
getResource()
kommer anrop att lyckas för alla filer, oavsett om anropet görs från en system- eller en icke-systemklass. -
getResourceAsStream()
anrop kommer att lyckas för resurser som inte är .class-resurser, och så även förjava.net.URL.getContent()
på motsvarande URL:er.
Exempel
Detta avsnitt innehåller två exempel på klientkod. I det första exemplet används ”absoluta resursnamn” och traditionella mekanismer för att hämta ett Class
-objekt.
package pkg;import java.io.IOException;import java.io.InputStream;import java.io.PrintStream;class Test { private static final String absName = "/pkg/mumble.baf"; public static void test1() { Class c=null; try { c = Class.forName("pkg.Test"); } catch (Exception ex) { // This should not happen. } InputStream s = c.getResourceAsStream(absName); // do something with it. } public void test2() { InputStream s = this.getClass().getResourceAsStream(absName); // do something with it. }}
Detta exempel använder ”relativa resursnamn” och den mekanism som finns tillgänglig från kompilatorn genom -experimental
flaggan, för att hämta ett Class
-objekt.
package pkg;import java.io.IOException;import java.io.InputStream;import java.io.PrintStream;class Test { private static final String relName = "mumble.baf"; public static void test1() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it.} public void test2() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it. }