servletcontext類中有這么四個方法:
- getrealpath(string path)
- getresource(string path)
- getresourceasstream(string path)
- getresourcepaths(string path)
這四個方法都使用web工程下某個web資源路徑的字符串表現形式作為參數,而每個方法返回不同的類型,我們通過這四個方法之一可以獲取某個資源,并對其進行讀取和修改操作。
假設我們的【myservlet】web工程中有一個數據庫的配置文件:database.properties,在這個數據庫中已經有了一些參數,而我們在web工程中希望讀取這個配置文件中的有關信息:
先來看看servletcontext中的getresourceasstream()
方法,這個方法返回inputstream對象。由于我們的配置文件為properties文件,所以可以用properties對象來裝載這個輸入流,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { servletcontext context = this .getservletcontext(); inputstream in = context.getresourceasstream( "/database.properties" ); properties prop = new properties(); prop.load(in); string url = prop.getproperty( "url" ); string username = prop.getproperty( "username" ); string password = prop.getproperty( "password" ); system.out.println(url); system.out.println(username); system.out.println(password); } |
最后在瀏覽器中訪問這個servlet,那么在myeclipse的控制臺上能看到的數據正好是database.properties中我們配置的信息:
接下來看看servletcontext中的getrealpath()
方法,這個方法返回string對象。由于我們的配置文件為properties文件,所以可以用properties對象來裝載這個輸入流,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
servletcontext context = this .getservletcontext(); string filepath = context.getrealpath( "/database.properties" ); fileinputstream fis = new fileinputstream(filepath); properties prop = new properties(); prop.load(fis); string url = prop.getproperty( "url" ); string username = prop.getproperty( "username" ); string password = prop.getproperty( "password" ); system.out.println(url); system.out.println(username); system.out.println(password); |
最后在瀏覽器中訪問這個servlet,那么在myeclipse的控制臺上能看到的數據正好是database.properties中我們配置的信息:
使用getrealpath()方法的好處在于這個方法還可以獲取文件名,而getresourceasstream()方法就只能獲取文件流了。例如獲取文件名:
1
2
3
4
5
6
7
8
9
|
servletcontext context = this .getservletcontext(); string filepath = context.getrealpath( "/web-inf/web.xml" ); system.out.println(filepath); if (filepath == null ) { system.out.println( "所找文件不存在!" ); } string filename = filepath.substring(filepath.lastindexof( "\\" )); system.out.println( "文件為:" +filename); |
接著來看看servletcontext中的getresource()
方法,這個方法返回url對象。而url對象具有打開到此 url 的連接并返回一個用于從該連接讀入的 inputstream的openstream()方法。由于我們的配置文件為properties文件,所以可以用properties對象來裝載這個輸入流,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
servletcontext context = this .getservletcontext(); url fileurl = context.getresource( "/database.properties" ); inputstream in = fileurl.openstream(); properties prop = new properties(); prop.load(in); string url = prop.getproperty( "url" ); string username = prop.getproperty( "username" ); string password = prop.getproperty( "password" ); system.out.println(url); system.out.println(username); system.out.println(password); |
最后在瀏覽器中訪問這個servlet,那么在myeclipse的控制臺上能看到的數據正好是database.properties中我們配置的信息:
以上說完了幾種通過servletcontext對象來讀取web應用下的某個資源文件,只要通過讀取的方法,并將資源相對于web工程的路徑作為參數傳入其中便可。我們上述的例子都是直接在web工程中,或者web工程的某個目錄下,而如果我們把某個web資源放置在myeclipse中的【src】目錄中,那么該如何讀取呢:
我們說過,這個web應用在發布時,會將【src】目錄下的.java文件編譯成為.class字節碼文件,由服務器自動將這些字節碼文件放置在該web應用中的【web-inf】下的【classes】目錄里,如果沒有【classes】目錄,服務器會自動幫我們創建,因此,只要是放置在【src】目錄中的資源,最后也會被服務器自動放置在【classes】目錄中,這樣我們可以繼續通過servletcontext對象來獲取:
1
2
3
4
5
6
7
8
9
10
11
12
|
servletcontext context = this .getservletcontext(); inputstream in = context.getresourceasstream( "/web-inf/classes/database.properties" ); properties prop = new properties(); prop.load(in); string url = prop.getproperty( "url" ); string username = prop.getproperty( "username" ); string password = prop.getproperty( "password" ); system.out.println(url); system.out.println(username); system.out.println(password); |
關于web工程下某個web資源在不同位置下的問題:
問題一:我們為什么不能用傳統方式,如fileinputstream或者file對象來直接獲取web工程中的資源呢?其實也是可以的,但是有個路徑的問題,servlet中方法所需要的路徑都是相對于web應用的路徑,而傳統的fileinputstream等等中方法所需的路徑參數都是相對于虛擬機的路徑。而又因為我這個web應用是從myeclipse中的tomcat里啟動的,所以這時候的虛擬機目錄其實是tomcat中的【bin】目錄。所以如果想用傳統方式讀取文件必須每次都將文件放置在tomcat的【bin】目錄下, 這是多么麻煩的事,因此我們開發web工程就應該使用web工程中的方法來讀取文件!但是,這卻又引出了問題二。。。
問題二:當我們web工程中有別的非servlet的類時,比如javabean,當javabean需要連接數據庫時,這就是非servlet對象讀取web工程中的資源文件了,不能用servletcontext來讀取,問題一種也說過不能用傳統方式如fileinputstream來讀取,那么該如何讀取呢?
答案是:類加載器!由于在【src】目錄下的java程序經過編譯成字節碼class文件,如果要用到這些類,java虛擬機需要先將這些字節碼文件加載到內存中才可以使用,而這個過程就是由類加載器來完成。因此這就有一個知識點,如果我們將某個web資源放置在【src】目錄下,因為這是個web工程,服務器會自動將各個字節碼文件重新放置在【classes】目錄下, 而這個web資源也會重新被服務器放置在【classes】目錄下,那么類加載器能加載【classes】目錄下所有的字節碼文件,同時,同處在這個目錄下的web資源也會被類加載器加載進內存,這時我們就可以使用類加載器讀取該web資源了。
例:在【myservlet】的dao包中創建一個student的javabean對象,并在src【目錄下】創建一個student的配置文件student.properties,而這個配置文件內容如下圖所示:
在student類中,我們需要通過類加載器來獲取輸入流來讀取這個文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class student { public void getstudent() throws ioexception { classloader loader = student. class .getclassloader(); inputstream in = loader.getresourceasstream( "student.properties" ); properties prop = new properties(); prop.load(in); string studentname = prop.getproperty( "name" ); string studentage = prop.getproperty( "age" ); system.out.println(studentname+ ":" +studentage); } } |
另外創建一個servlet作為可以供瀏覽器訪問的對象,在該servlet中創建student的示例來獲取配置文件中的內容,這樣就達到了從非servlet對象讀取web資源內容并向servlet對象傳遞數據:
1
2
3
4
5
6
7
8
|
public class servletdemo extends httpservlet { public void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { student student = new student(); student.getstudent(); } } |
從瀏覽器中訪問該servlet,可以看到通過類加載器讀取的配置文件中的內容:
注意,這種方法只能是web資源放置在【src】目錄中才可以使用,如果要讀取的web資源是放置在web工程的目錄下,使用類加載器也還是無法讀取,因為類加載器只能讀取類目錄下的文件,這時候非servlet類就無法讀取資源文件,只能使用servletcontext來讀取了。
方立勛老師說:“類加載器只能加載【classes】目錄下的所有文件一次,這樣在服務器運行web工程的過程中,如果我們修改【classes】目錄下的student.properties配置文件,則由于類加載器不再加載,因此使用類加載器的方式不能讀取修改后的內容”
但是我修改后,還是可以使用類加載器的方式讀取classes】目錄下修改后的student.properties配置文件,難道是因為jdk7的原因嗎?
不過不管是什么原因,方立勛老師針對他的問題所采取的解決方案還是值得學習的,他采用先用類加載器獲取該配置文件的路徑,然后再采用傳統方式獲取這個文件的輸入流。所以在student中的getstudent()方法代碼改為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class student { public void getstudent() throws ioexception { classloader loader = student. class .getclassloader(); url fileurl = loader.getresource( "student.properties" ); string filepath = fileurl.getpath(); fileinputstream fis = new fileinputstream(filepath); properties prop = new properties(); prop.load(fis); string studentname = prop.getproperty( "name" ); string studentage = prop.getproperty( "age" ); system.out.println(studentname+ ":" +studentage); } } |
這種方式還有一種好處就是,如果要讀取的文件過大,而之前通過類加載器將大文件加載進內存就容易導致內存溢出,所以還是采用這種方式比較好。
最后再說明一點,如果是在非servlet類中采用類加載器獲取【classes】目錄中的資源,方法參數的路徑只需要是相對于【src】目錄即可。
補充:使用類加載器加載【classes】目錄中的資源,得到的路徑取決是哪個虛擬機(或服務器)調用,例如上面的代碼getstudent()方法,如果是在非servlet的類的方法中被調用,那么就是使用jvm虛擬機,那么得到的資源路徑并不是tomcat的應用【webapps】目錄的路徑。因此如果是要為servlet中提供資源,那么非servlet類中獲取資源的方法,請一定要使用servlet來調用,這樣才能保證得到的資源路徑是在tomcat服務器下的自己的web應用所在目錄中的正確位置。
例如下面的例子,我的myeclipse工作空間在【e】盤,而tomcat服務器所在路徑為【f】盤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class resourceutils { public static void main(string[] args) throws ioexception { getresource(); } @test public static void getresource() throws ioexception { classloader loader = resourceutils. class .getclassloader(); url url = loader.getresource( "student.properties" ); string path = url.getpath(); system.out.println(path); } } |
而資源為student.properties配置文件,放置的位置為【src】目錄下:
這個是在我的一個web應用中定義的一個非servlet的普通java類,這個類無論是用junit測試還是使用main函數,亦或是使用別的非servlet類來調用getresource方法獲取在web應用下【src】目錄中的student.properties資源,顯示的路徑為myeclipse的工作空間,而不是tomcat服務器:
而如果是使用servlet來調用的話,才是真正顯示在tomcat中web應用所在的地方:
1
2
3
4
5
6
7
8
|
public class servletdemo extends httpservlet { public void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { resourceutils.getresource(); } } |
因此在使用web工程中,如果使用非servlet類來獲取資源,請一定注意這個資源路徑問題!!!