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类来获取资源,请一定注意这个资源路径问题!!!