刚开始做开发的时候,对于要路径前是否加斜杠“/”一直比较疑惑,即绝对路径和相对路径问题。最近写了一个demo用来测试这个问题,对这个问题做了一个总结。
这里先贴上项目的结构,方便能看到各资源的实际位置。
下面是web.xml的配置,主要是各个servlet的资源路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>pathDemoApp</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Demo1</servlet-name>
<servlet-class>com.lk.demo.Demo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Demo2</servlet-name>
<servlet-class>com.lk.demo.Demo2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo2</servlet-name>
<url-pattern>/demo2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Demo3</servlet-name>
<servlet-class>com.lk.demo.Demo3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo3</servlet-name>
<url-pattern>/test/demo3</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Demo4</servlet-name>
<servlet-class>com.lk.demo.Demo4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo4</servlet-name>
<url-pattern>/demo4</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Demo5</servlet-name>
<servlet-class>com.lk.demo.Demo5</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo5</servlet-name>
<url-pattern>/test/demo5</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Demo6</servlet-name>
<servlet-class>com.lk.demo.Demo6</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo6</servlet-name>
<url-pattern>/test/demo6</url-pattern>
</servlet-mapping>
</web-app>
需要强调,路径的问题要区别开是“服务器(或者说servlet处理的路径)”还是“客户端(即浏览器要发送的请求)”。
1、先说“客户端”:
1.1 HTML页面的请求。
下面的代码是在HTML页面中,浏览器请求图片资源的情况。
test1.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<img src="/pathDemoApp/iamge/pic.jpg">
<br/>
<img src="/iamge/pic.jpg">
<br/>
<img src="iamge/pic.jpg">
<!--
当前HTML页面的访问路径为:
http://localhost:8080/pathDemoApp/html/test1.html
总结:
1.在HTML页面中的请求路径,斜杠“/”代表服务器站点的根路径,这里是 http://localhost:8080/,
然后加上图片的URI地址,因此第一个的实际请求路径为:
http://localhost:8080/pathDemoApp/iamge/pic.jpg
第二个img标签的实际请求路径为:
http://localhost:8080/iamge/pic.jpg
因此第一个图片可以正常显示,第二个路径是错误的(404)。
2.如果路径前不加“/”,则浏览器会将URI的最后一个斜杠后的资源名称替换为请求的路径,
例如第三个img标签的src的实际请求路径为:
http://localhost:8080/pathDemoApp/html/iamge/pic.jpg
这个图片都不能正确显示(404)。
-->
</body>
</html>
结果如下图所示,只有第一个图片请求成功。
1.2 Response的重定向。
response.sendRedirect(path);
虽然response重定向资源是在servlet代码中实现的,但是,是将要重定向的路径返回给浏览器,让浏览器重新请求,因此,其规律和HTML页面请求资源是一样的,所以这里将重定向归为客户端请求这一类。
代码如下:
Demo3.java
package com.lk.demo;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 该类用于测试Response的重定向
* 将该类的访问路径特别设置为/test/demo3
*/
public class Demo3 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我是Demo3");
/*
* 总结:
* response的重定向方法,是将路径返回给浏览器,让浏览器重新请求,因此可以发现,
* 重定向的规律和浏览器在页面中发送请求的规律是一样的。
* “/”表示服务器站点的根路径,这里是 http://localhost:8080/,
* 如果参数路径前不加“/”,则浏览器会将URI的最后一个斜杠后的资源名称替换为参数请求的路径。
*/
response.sendRedirect("/pathDemoApp/demo4");//正确
// response.sendRedirect("pathDemoApp/demo4");//路径为http://localhost:8080/pathDemoApp/test/pathDemoApp/demo4
// response.sendRedirect("/demo4");//错误路径为http://localhost:8080/demo4
// response.sendRedirect("demo4");//错误路径为http://localhost:8080/pathDemoApp/test/demo4
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
HTML中,“/”表示的是服务器站点的根路径,案例中为http://localhost:8080/;
如果不加“/”,则是在当前页面的路径基础上,将请求的URI替换掉最后一个“/”后的资源名称。
2、服务器
2.1 ServletContext.getRealPath()方法获取资源的路径。
ServletContext的getRealPath方法可以获取项目下任何位置资源的路径。
Demo2.java
package com.lk.demo;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 用于测试ServletContext的getRealPath方法的结果
*
*/
public class Demo2 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* ServletContext的getRealPath方法可以获取项目下任何资源的路径
* 总结:
* 该方法都是从项目根路径(即站点+项目名称)下查找参数传递的资源路径,不论参数前是否有斜杠。
* 然后将参数代表的资源路径拼接到项目根路径之后。
*/
String path1 = getServletContext().getRealPath("pathDemoApp/iamge/pic.jpg");
System.out.println(path1);
// D:\WorkProgramFiles\apache-tomcat-7.0.35\webapps\pathDemoApp\pathDemoApp\iamge\pic.jpg
String path2 = getServletContext().getRealPath("/iamge/pic.jpg");
System.out.println(path2);
// D:\WorkProgramFiles\apache-tomcat-7.0.35\webapps\pathDemoApp\iamge\pic.jpg
String path3 = getServletContext().getRealPath("iamge/pic.jpg");
System.out.println(path3);
// D:\WorkProgramFiles\apache-tomcat-7.0.35\webapps\pathDemoApp\iamge\pic.jpg
String path4 = getServletContext().getRealPath("/pic.jpg");
System.out.println(path4);
// D:\WorkProgramFiles\apache-tomcat-7.0.35\webapps\pathDemoApp\pic.jpg
String path5 = getServletContext().getRealPath("/");
System.out.println(path5);
// D:\WorkProgramFiles\apache-tomcat-7.0.35\webapps\pathDemoApp\
String path6 = getServletContext().getRealPath("");
System.out.println(path6);
// D:\WorkProgramFiles\apache-tomcat-7.0.35\webapps\pathDemoApp
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
该方法都是从项目根路径(即站点+项目名称)下查找参数传递的资源路径,不论参数前是否有斜杠“/”, 然后将参数代表的资源路径拼接到项目根路径之后。
2.2 Request的转发请求。
request.getRequestDispatcher(path).forward(request, response);
Demo5.java
package com.lk.demo;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 用于测试Request的转发
* 将该类的访问路径特别设置为/test/demo5
*/
public class Demo5 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 总结:
* 经过测试,可以发现
* “/”表示项目的根路径,这里是 http://localhost:8080/pathDemoApp/,
* 如果参数路径前不加“/”,则会将当前资源的请求路径中,最后一个斜杠后的资源名称替换为参数请求的路径。
*/
System.out.println("我是Demo5");
// String path = "/pathDemoApp/test/demo6";//错误404:/pathDemoApp/pathDemoApp/test/demo6
// String path = "pathDemoApp/test/demo6";//错误404:/pathDemoApp/test/pathDemoApp/test/demo6
String path = "/test/demo6";//正确
// String path = "test/demo6";//错误404:/pathDemoApp/test/test/demo6
request.getRequestDispatcher(path).forward(request, response);
System.out.println("又回到了Demo5");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
该方法中,“/”表示项目的根路径,这里是 http://localhost:8080/pathDemoApp/;
如果参数路径前不加“/”,则会将当前资源的请求路径中,最后一个斜杠后的资源名称替换为参数请求的路径,不加“/”类似于重定向的请求。
总结:
上述的例子是我现在能想到的一些情况。
对于浏览器端,“/”表示的是服务器根站点,即http://localhost:8080/ 。
对于服务器端,“/”表示的是项目的根路径,即http://localhost:8080/pathDemoApp/ 。
总的来说,可以发现,在使用资源路径时,最好使用当前项目的根据路,这样不会出现什么大问题。
对于浏览器请求,比如在jsp的渲染引擎中,我们可以使用request.getContextPath的方法获取当前项目的项目名称,在后面拼接具体资源的路径,以避免在代码中将项目名称写为固定值。