03_JavaWeb中的Servlet(详解)+ 思维导图

思维导图

在这里插入图片描述

在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 requests
  • doPost, for HTTP POST requests
  • doPut, for HTTP PUT requests
  • doDelete, for HTTP DELETE requests
  • init and destroy, to manage resources that are held for the life of the servlet
  • getServletInfo, 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:

  1. The servlet is constructed, then initialized with the init method.
  2. Any calls from clients to the service method are handled.
  3. 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中输入的是绝对路径,那么是可以用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值