思维导图
在JAVA语言中用来开发动态web资源的技术叫做Servlet。
HTTP请求报文格式:
请求行:请求方法(GET/POST) 请求资源 版本协议
若干请求头
空行
请求体
服务器相关知识回顾:
提供服务的机器。可以从两个层面去说:一个是硬件层面;一个是软件层面
硬件:云服务器,阿里云服务器。
软件:软件。比如tomcat,主要的功能是什么呢?监听当前机器上面的某一个端口号,如果客户端向当前机器的对应的端口号去发送请求(HTTP请求报文),那么该请求便会被接收到,进而进行处理(服务器需要处理这部分信息,是否意味着要求请求报文必须遵循指定的格式?)。随即再将响应的内容返回给客户端(HTTP响应报文)。
tomcat:服务器软件。
架构:组件组成。Connector:连接器,主要的功能是接收客户端的请求,并且提供响应
Engine:用来去接收Connector处理过后的数据。
Host:Engine处理过后会进行一步的进行传递。类似于行政区域制度。
Context:其实就是之前大家学习的应用(资源文件的集合,非常类似于村)。不仅可以部署静态资源文件,后续学习的Servlet动态资源也需要部署在应用中。Context 应用是tomcat里面的最小单位。
如何部署资源?掌握的内容
1.直接部署:就是在tomcat的webapps目录下去部署。
2.虚拟映射:不希望放置在tomcat的webapps目录下,依然是可以部署的。又可以分为两种方式: 1.conf/Catalina/localhost目录下新建一个xml文件,文件名被解析成为应用名,docBase便是应用的基准目录,后续访问的时候通过 http://localhost:端口号/{应用名}/{文件相对docBase的相对路径}
2.conf/server.xml文件,Host节点下新增Context节点,不推荐。
需要看到本质,文件在哪不重要,重要的是否是需要找到该文件的硬盘路径,然后通过IO流响应出去。
Servlet介绍
https://tomcat.apache.org/tomcat-8.5-doc/servletapi/
官方英文介绍
A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually
across HTTP, the HyperText Transfer Protocol.
Servlet是什么?Servlet是运行在服务器里面的java程序。Servlet可以接收客户端的请求并且做出响应。
1,Servlet开发流程(掌握)
1.1 实现Servlet接口
To implement this interface, you can write a generic servlet that extends javax.servlet.GenericServlet
or an HTTP servlet that extends javax.servlet.http.HttpServlet
.
import javax.servlet.*;
public class Servlet1 extends GenericServlet{
public void service(ServletRequest req,ServletResponse resp) throws ServletException,java.io.IOException{
System.out.println("servlet1");
}
}
1.2 编译java文件成class文件
为什么会出现该错误?
javax.servlet包下面的所有类没有在jdk中。
无论jdk还是我们编写的代码,运行时都要求在内存中,但是它们实际上是存在于硬盘上面的。就需要有一个过程将类加载到内存中,该过程称之为类加载,完成该过程的组件叫做类加载器。类加载器可以分为三类:
1.BootStrap类加载器:负责去加载jdk中的核心类库。比如rt.jar包里面的类库
2.Extension类加载器:负责去加载jdk中ext目录下的类库。
3.System类加载器:允许通过一个参数 -classpath 将第三方的类库加载到内存中。
javac -classpath xxxx.jar(servletjar包的路径) 待编译的java文件
1.3 运行Servlet
其中,需要特别注意,不可以使用java指令来运行Servlet需要将Servlet部署到tomcat服务器中才可以完成运行。
如果通过Servlet的地址直接去访问的话,此时会有以下的问题:
1.实际上该过程是下载,而不是运行
2.服务器的源代码被客户端下载到了本地,有没有安全风险?
针对问题2,思路是:在应用的根目录下,如果某些文件非常的重要,不希望被浏览器直接访问到,那么设置一个WEB-INF目录(需要位于应用根目录中),将我们所有需要保护的资源全部放置在该目录下,那么浏览器便无法直接访问到。
如何让Servlet运行呢?
此处tomcat服务器设置了一个映射规则。便是地址和全类名类的映射关系,当用户在浏览器地址栏输入该地址时,那么tomcat服务器便会调用该全类名类的对应的方法。
比如设置一个/name1----------------com.baidu.servlet.Servlet1映射关系,只要用户在浏览器地址栏输入xxxx/ss1,解析过后便会去调用com.baidu.servlet.Servlet1.service(req,resp)方法。
1.4 配置映射规则
操作如下:
1.在应用根目录下新建WEB-INF/classes目录,用于存放全类名的类
2.在应用根目录下新建WEB-INF/lib目录,用于存放后续可能运行的时候需要用到的jar包类库,比如后续整合数据库,那么便需要将数据库的相关依赖包放到该目录下,今天暂不需要。
3.在应用根目录下新建WEB-INF/web.xml文件,在该xml文件中用来配置上述的映射规则。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
<servlet>
<servlet-name>ss1</servlet-name>
<servlet-class>Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ss1</servlet-name>
<url-pattern>/name</url-pattern>
</servlet-mapping>
</web-app>
后续用户访问的时候,只需要去输入 http://主机:端口号/应用名/{url-pattern}
2, Servlet执行原理(了解)
通过上述实验,我们发现了tomcat服务器将用户的一次次的请求转换成了Servlet中的方法的每次调用。究竟是如何实现该功能的呢?
以输入的http://localhost:8083/app/ss1为例
1.域名解析:拿到该域名对应的ip地址
2.发起TCP三次握手,建立一个可靠的TCP连接
3.浏览器根据用户输入的地址,生成对应的HTTP请求报文(GET /app/ss1 HTTP/1.1),请求报文在网络中中转传输最终到达目标机器
4.被监听8083端口号的tomcat接收到,Connector接收到该请求报文之后,会将请求报文解析成为一个对象,该对象便是request对象。
5.随后Connector将request对象以及response对象一并传递给Engine
6.Engine进一步将这两个对象传递给Host
7.Host会去挑选一个合适的Context,将这两个对象进行进一步的传递
8.在应用内部,此时,有效的路径部分为/ss1(此时/app应用部分已经不再重要,这部分的作用就是为了在众多应用中找到合适的应用),根据web.xml文件中的配置信息,根据/ss1找到对应的映射关系为Servlet1
9.tomcat利用反射先实例化Servlet对象,进一步调用其service(req,resp)(关注参数),调用该方法需要传递进来两个参数,刚刚好之前说的request对象和response对象
10.随后Connector会读取response里面的数据,根据HTTP相应报文的格式要求,返回指定的HTTP响应报文。
3, 反射回顾
我们编写了各种各样的class文件,随后这些class文件都会被加载到内存中。那么有没有一种方式使用一个对象,可以将不同的class文件的信息表示出来呢?包含哪些信息呢?属性、构造函数
比如A class文件,里面包含了属性1、属性2、构造函数3、是否是接口等信息
比如B class文件,里面包含了属性3、构造函数4、是否是注解等信息
在内存中,其实会有一个对应的ClassA对象,里面包含了A class文件的全部信息
再内存中,还会有一个对应的ClassB对象,里面包含了B class文件的全部信息
Class持有了构造函数,那么能否调用构造函数来进行实例化对象呢?
Class aClazz = Class.forName("com.cskaoyan.A");
Constrctor cons = aClazz.getDeclaredConstructor();
Object obj = cons.newInstance();
Method method = aClazz.getDeclaredMethod(name);
//第一个参数表示的是调用哪个对象里面的该方法;第二个参数表示的是在调用该方法时需要传递进去的参数
method.invoke(obj, args)
Field field = aClazz.getDeclaredField(name);
field.setAccessible(true);
field.set(obj, arg);
A a = new A();
上述两种方式大家觉得是一样的吗?原理是一致的,均是利用了无参构造函数来进行对象的实例化操作。
只不过利用反射的代码更加具有通用性,今后你去实例化B,大概率也可以继续用,但是下面的代码如果你去实例化B,肯定不行。
4, 使用IDEA开发Servlet
先不介绍整合Maven。
1.新建项目选择下面
2.后续创建项目的步骤和之前是一致的
3.直接不编写Servlet即可,编写完毕之后,编译无需我们去处理,你只需要去在web/WEB-INF/web.xml文件中去配置映射关系即可
4.部署资源。
关于idea中按钮的一个补充说明:
如果第一次点击了某个启动按钮,比如run或者debug,第二次再次点击同样的按钮,会出现如下的对话框:
可以非常快速地进行资源更新操作,为了方便大家的记忆,建议大家每次都选择第三个即可。Redeploy
5, 开发Servlet方式二(掌握)
可以通过继承HttpServlet的方式来实现。
Provides an abstract class to be subclassed to create an HTTP servlet suitable for a Web site. A subclass of HttpServlet
must override at least one method, usually one of these:
doGet
, if the servlet supports HTTP GET requestsdoPost
, for HTTP POST requestsdoPut
, for HTTP PUT requestsdoDelete
, for HTTP DELETE requestsinit
anddestroy
, to manage resources that are held for the life of the servletgetServletInfo
, which the servlet uses to provide information about itself
言外之意:如果你继承HttpServlet的Servlet希望支持何种请求方法,那么便去重写对应的doXX方法即可。一般情况下,我们重写doGet、doPost即可
5.1 操作步骤
1.编写类继承HttpServlet,需要去重写doGet或者doPost方法,或者二者均重写
public class Servlet3 extends HttpServlet {
/**
* 如果你发送的是get请求,便会进入到当前方法中
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
System.out.println("doGet");
}
/**
* 如果你发送的是post请求,那么便会进入到当前方法中
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
System.out.println("doPost");
}
}
2.web.xml文件中去配置对应的映射关系
5.2 常见问题(掌握)
1.如何让doPost方法被调用?我仅需要向当前Servlet发起Post请求即可。
2.如何向对应的Servlet发起Post请求?利用form表单向指定地址发起Post请求即可。
需要注意的是:form表单需要先加载出来,点击提交按钮时,会往action地址发起请求,此时的请求方法便是post
<form action="http://localhost:8083/app/ss3" method="post">
<input type="text" name="username"><br>
<input type="submit">
</form>
3.在继承GenericSerlvet的Serlvet中,调用的方法是service方法,为什么在继承HttpServlet的Servlet中却又变成了doXXX方法?
无论使用的是及继承哪个Servlet的方式,最终都是统一的、一致的。
tomcat调用Servlet的程序执行入口始终是Servlet的service方法。
程序执行入口:
//进行了一次向下转型,调用了自己的service方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException("non-HTTP request or response");
}
this.service(request, response);
}
//根据当前请求方法的不同,进一步分发到不同的doXXX方法中
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取当前请求报文的请求方法
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
//调用了自身的doGet方法
this.doGet(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
}
}
6, 开发Servlet方式三(掌握)
之前,我们配置映射关系是使用web.xml方式来进行配置,除此之外,还可以使用注解的方式。
//@WebServlet(name = "s4", urlPatterns = "/ss4")
//可以进一步优化,name属性可以去掉不写
//@WebServlet(urlPatterns = "/ss4")
//还可以进一步优化,如果注解内只写了一个urlPatterns = xxx,那么可以将key=删掉,只保留value
@WebServlet("/ss4")
//还有一个补充,value属性和urlPatterns属性是一致的,都是给映射赋值,所以你设置任何一个都可以
public class Servlet4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet");
}
}
关于注解的说明回顾:
在前面,我们提及Class对象的主要作用便是记录当前类中的信息,如果当前类包含了注解,那么也会在Class对象中体现出来。Class也给我们开发者提供的对应的方法来获取里面的注解信息。
@WebServlet("/scan")
public class ScanAnnotationServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Class<?> aClass = null;
try {
aClass = Class.forName("com.cskaoyan.servlet.Servlet4");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
WebServlet annotation = aClass.getAnnotation(WebServlet.class);
String[] strings = annotation.urlPatterns();
String[] value = annotation.value();
System.out.println(Arrays.toString(strings));
System.out.println(Arrays.toString(value));
}
}
7, IDEA和Tomcat整合
在IDEA中,我们开发JavaEE项目,非常的简单。主要的原因在于很多重复性的、机械性的操作被IDEA取代了,无需我们开发者自己去处理。但是IDEA究竟是如何和tomcat进行整合的呢?清楚这个问题之后,后续我们使用idea的过程中,遇到问题,便可以很轻松的解决了。
实际上,idea和tomcat进行整合关联的方式非常的巧妙。首先先复制本地tomcat的配置文件到某一处,在此处利用这套配置文件重新开启一个新的tomcat。所以其实它并没有使用本地tomcat的原始的配置信息。
CATALINA_BASE: C:\Users\song.IntelliJIdea2019.3\system\tomcat\Unnamed_Day1Servlet
我们可以发现,idea的tomcat部署的应用信息如下:
<Context docBase="D:\cskaoyan-training-course\java54-course-materials\03_EE\code\Day1Servlet\out\artifacts\Day1Servlet_war_exploded" path="/app" />
当前应用的根目录为此docBase的位置。
为什么idea会进行同步复制呢?就需要给大家去介绍几个配置项。
最终,大家可以知道如下一个结论:
那就是web(带蓝点)并不是应用根目录,而是artifacts里面的output才是。
如果今后出现了web目录中有某个文件,但是访问的时候显示404,此时你应该怎么办?
因为应用根目录不是web目录,如何解决呢?把out目录全部删除,重新部署即可。
8, Servlet生命周期(了解)
This interface defines methods to initialize a servlet, to service requests, and to remove a servlet from the server. These are known as life-cycle methods and are called in the following sequence:
- The servlet is constructed, then initialized with the
init
method. - Any calls from clients to the
service
method are handled. - The servlet is taken out of service, then destroyed with the
destroy
method, then garbage collected and finalized.
Servlet接口重定义了一系列的方法,包含:初始化servlet的方法、给请求提供服务的方法、将servlet从服务器中移除的方法,这些方法被称之为生命周期函数,被按照如下的顺序进行调用:
1.当Servlet被创建时,会调用init方法来完成初始化工作。每个Servlet只有一个实例化对象,这个称之为单例(一个class类只有一个实例对象)。如果多个用户同时访问当前serlvet时,实际上访问的是同一个对象的service方法。如果该servlet中存在着成员变量,如果用成员变量来去接收用户所特有的数据,那么便会有问题。init方法默认情况下,是在第一次访问当前serlvet时调用,后续访问不会再调用;如果希望init方法可以提前,可以给当前servlet设置一个load-on-startup=非负数的参数,那么该init方法便会随着应用的启动而触发。
@WebServlet(value = "/life",loadOnStartup = 1)
xml
<servlet>
<servlet-name>life</servlet-name>
<servlet-class>com.cskaoyan.servlet.LifeCycleServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>life</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>
2.任何客户端的请求都会交给service方法来处理。客户端的每次访问都会调用一次serlvet的service方法。无论你继承GenericServlet还是继承HttpSerlvet,那么都是从service过来的。无论是doGet还是doPost其实都可以看作是service方法。
3.当servlet不再发挥功能,会调用destroy方法来完成销毁工作。如果你希望去做一些善后、收尾的工作,那么便可以将代码逻辑写在这个方法中。比如写入到数据库、持久化、序列化。该方法会在应用卸载、服务器关闭的时候调用。
意义:如果我们需要在特定的阶段做某些事情,那么便可以将代码写在对应的生命周期函数中。
//@WebServlet(value = "/life",loadOnStartup = 1)
public class LifeCycleServlet extends HttpServlet {
/**
* 每当有一个servlet实例对象被创建出来,那么便会一次init来进行初始化
* 根据实验,可以得知,一个Servlet其实在运行时,只有一个实例对象
* 所以,用户所特有的数据不可以使用成员变量来接收
* @throws ServletException
*/
@Override
public void init() throws ServletException {
System.out.println("init");
//希望在客户端访问serlvet之前先去做一个准备工作,比如访问数据库,读取配置,代码写在哪呢?
}
/**
* 客户端的每次请求都会调用一次service
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service");
}
/**
* servlet不再发挥功能,即将被卸载时;应用关闭、服务器关闭
*/
@Override
public void destroy() {
System.out.println("destroy");
}
}
9, 常见的注意事项(掌握)
1.Servlet的url-pattern的合法写法有哪些?
有两种,一种是/xxx,还有一种是*.xxxx.注意:这里面出现的*就表示在写的时候就这么去写,不是表示统配。
比如 /life、/servlet1、/ss1
下面便是反面教材,url-pattern必须以/开头,不能直接以一个数字、字母开头
建议大家课后自己主动去复现该bug
Caused by: java.lang.IllegalArgumentException: Invalid <url-pattern> [life] in servlet mapping
比如*.do、*.html
2.一个Servlet可不可以配置多个url-pattern呢?可以设置多个
//@WebServlet(urlPatterns = {"*.txt", "*.txt2"})
//依然是可以进一步去省略的
@WebServlet({"*.txt", "*.txt2"})
3.多个servlet可不可以映射到同一个url-pattern呢?不可以
Caused by: java.lang.IllegalArgumentException: The servlets named [com.cskaoyan.servlet.Txt2Servlet] and [com.cskaoyan.servlet.TxtServlet] are both mapped to the url-pattern [*.txt2] which is not permitted
10, url-pattern优先级(掌握)
既然url-pattern的合法写法是允许写*的,那么可能就会存在多个url-pattern之间是有相互重叠的。一个请求到来,有可能是有多个serlvet可以处理该请求,那么谁的优先级更高呢?应该优先交给哪个Servlet来进行处理呢?
举例:
/abc/a.html请求
存在着三个serlvet,一个url-pattern是/abc/*,还有一个是*.html,还有一个是/*
规则:
1./xxxx和.xxxx他们之间的优先级是前者高于后者。只要有前者存在的情况下,后者永远无法执行。*
2.如果都是/xxxx,那么满足匹配程度越高,优先级越高。
11, 缺省Servlet(掌握)
在前面课程的学习中,我们发现学习了Servlet之后和没学习之前,对待静态资源的处理有一些不同。
在tomcat中,任何一个请求都会交给Servlet来处理;如果没有配置servlet或者没有合适的Servlet,会交给tomcat提供的缺省Servlet来进行处理。如果有配置Servlet,那么便会交给一个优先级最高的Servlet来进行处理。
关于缺省Servlet还有一点说明,如果你的系统中没有配置,那么默认就是使用tomcat提供的缺省Servlet,但是如果你的应用中配置了一个缺省Servlet,那么便会将tomcat提供的缺省Servlet功能给覆盖掉。如何配置一个缺省Sservlet呢?url-pattern为/即可。
@WebServlet("/")
public class MyDefaultServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("/");
}
}
12, ServletConfig对象(了解)
如果我在web.xml文件中配置Serlvet信息时,做一些初始化参数,能否在serlvet运行期间获取该初始化参数呢?
init-param标签便是初始化参数
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>cfg</servlet-name>
<servlet-class>com.cskaoyan.servlet.ServletConfigServlet</servlet-class>
<init-param>
<param-name>configLocation</param-name>
<param-value>D:/app/app.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>cfg</servlet-name>
<url-pattern>/cfg</url-pattern>
</servlet-mapping>
public class ServletConfigServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//程序运行期间,能够获取该初始化参数,读取里面的配置文件所在的位置
//只需要利用ServletConfig对象便可以获取该初始化参数
ServletConfig servletConfig = getServletConfig();
//后续我们在学习SpringMVC时,第一个案例便是使用这种方式来入门的
String configLocation = servletConfig.getInitParameter("configLocation");
System.out.println(configLocation);
}
}
13, ServletContext对象(掌握)
该对象你可以理解为是基本和应用的生命周期保持一致。当应用被创建时,那么该对象被创建出来;当应用被销毁时,该应用被销毁。在整个应用运行期间有且只有这唯一的一个ServletContext对象。并且每个Servlet都持有该对象的引用。所以ServletContext可以用在多个Servlet之间进行数据共享。
13.1 context域
便是利用ServletContext对象来进行数据共享。可以实现多个Servlet之间的数据共享。并且这种共享和使用数据库、配置文件的区别在于,这是使用内存来进行共享,至于使用数据库、配置文件那么是使用硬盘来进行共享。速度差异比较大的。
类比:局域网的空间。大家每个同学相当于一个serlvet一样,然后均可以访问该局域网。大家都可以在这个局域网内进行存取数据,同学a分享了某个书籍,同学b下载了这个书籍。此时,大家便是利用了局域网来进行了数据的共享。
@WebServlet("/ct1")
public class ContextServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//context域可以用在哪个地方呢?当前商城商品的类目信息,在很多页面都需要进行渲染,需要每次都去查询数据库吗?
//第一次的时候,从数据库获取,随即放入context域中;后面其他页面如果也需要,则直接从context域里面获取即可
//需要在当前serlvet中持有serlvetContext对象
ServletContext servletContext = getServletContext();
//利用context域来进行数据的共享 存数据
servletContext.setAttribute("categories", "food");
}
}
@WebServlet("/ct2")
public class ContextServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如果当前页面也需要去使用类目信息,那么完全没必要再去获取,直接从context域中获取即可
//当前serlvet需要去持有serlvetContext引用
ServletContext servletContext = getServletContext();
//利用context域 取值
Object categories = servletContext.getAttribute("categories");
System.out.println(categories);
}
}
13.2 获取应用下文件的绝对路径
希望你可以将应用根目录下某个文件的绝对路径获取到,应该怎么办?
@WebServlet("/path")
public class PathServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//应用根目录下有一个1.html文件,希望你获取该文件的绝对路径
File file = new File("1.html");
//D:\apache-tomcat-8.5.37\bin\1.html
String absolutePath = file.getAbsolutePath();
System.out.println(absolutePath);
}
}
注意:不可以像之前SE一样,使用file相对路径的方式来获取文件的绝对路径,这是不可以的。因为file里面我们输入的是一个相对路径,相对的是用户的工作目录,此时用户的工作目录实际上是tomcat的bin目录。
在此情形下,如果希望获取应用根目录下某个文件的绝对路径,应该怎么办呢?
根据我们之前学习服务器的经验,tomcat实际上是知晓每个应用的应用名称以及应用根目录的
所以,如果tomcat可以将这部分路径暴露给我们开发者,那么我们也是可以获取到该路径的。这里面便是利用了SerlvetContext对象将这个路径加以暴露出来。
方案一:
可以先获取应用根目录的绝对路径,自行拼接出某个文件的绝对路径。
@WebServlet("/rp1")
public class RealPathServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先获取应用根目录的路径,后续我自行去拼接
ServletContext servletContext = getServletContext();
String realPath = servletContext.getRealPath("");
System.out.println(realPath);
}
}
方案二:
可以在方法的参数中输入一个文件相对于应用根目录的相对路径,那么会返回该文件的绝对路径。其实和方案一本质没有任何区别。千万需要注意一点:没有搜寻的功能。
@WebServlet("/rp2")
public class RealPathServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先获取应用根目录的路径,后续我自行去拼接
ServletContext servletContext = getServletContext();
//还可以输入一个参数:文件相对应用根目录的相对路径
String realPath = servletContext.getRealPath("1.html");
System.out.println(realPath);
}
}
关于路径的总结:
不仅仅是file相对路径无法使用,fileReader相对路径也无法使用。
但是如果你的file中输入的是绝对路径,那么是可以用的。