【JavaWeb】Servlet

Servlet

Servlet是Sun公司开发动态web的一门技术。Sun在这些API中提供了一个接口:Servlet。开发Servlet程序需要完成两个步骤:

  • 编写一个Java类,实现Servlet接口
  • 把开发好的Java类部署到web服务器中

把实现了Servlet接口的Java程序叫做Servlet

HelloServlet

Sevlet接口Sun公司提供有两个默认的实现类:HttpServlt,GenericServlet

  1. 构建一个普通的Maven项目(不带模板),删掉里面的src目录,这个空的工程就是Maven的主工程。之后在这个项目里建立Module,新建的Module均为Maven父项目的子项目。

  2. 关于Maven父子工程的理解:在父项目中会有

    1
    2
    3
    <modules>
    <module>servlet-01</module>
    </modules>

    父项目中的Maven依赖环境Jar包子项目可以直接使用

  3. Maven环境优化:修改web.xml(与本地Tomcat中的内容一致)

1
2
3
4
5
6
7
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_4_0.xsd"
version="4.0"
metadata-complete="true">

</web-app>
  1. 编写一个Servlet程序
    • 编写一个普通类
    • 实现Servlet接口,这里继承HttpServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.zhao.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
// 由于get或者post只是请求实现的不同方式,可以互相调用,业务逻辑都一样
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.print("Hello Servlet");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
  1. 编写Servlet映射:写的Java程序要通过浏览器访问,浏览器需要连接web服务器,所以需要在web服务中注册我们写的Servlet,还需要给他一个浏览器能访问到的路径。
1
2
3
4
5
6
7
8
9
10
<!-- 注册Servlet -->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.zhao.servlet.HelloServlet</servlet-class>
</servlet>
<!-- Servlet的请求路径 -->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 配置Tomcat,注意配置项目发布的路径
  2. 启动测试

Servlet原理

Servlet是由web服务器调用,web服务器在收到浏览器请求后会调用service()方法,该方法会根据请求的类型GETPOST分发处理,执行相应的doGet()doPost()方法。

image-20210501200459226

Mapping问题

  1. 一个Servlet可以指定一个映射路径
1
2
3
4
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定多个映射路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定通用映射路径
1
2
3
4
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
  1. 默认请求路径
1
2
3
4
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

优先级问题:

指定了固有的映射路径优先级最高,如果找不到匹配的固有映射路径,则就会走默认路径(*)

1
2
3
4
5
6
7
8
9
10
<!-- 注册Error Servlet -->
<servlet>
<servlet-name>ErrorServlet</servlet-name>
<servlet-class>com.zhao.servlet.ErrorServlet</servlet-class>
</servlet>
<!-- ErrorServlet的请求路径 -->
<servlet-mapping>
<servlet-name>ErrorServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

ServletContext

是一个接口,代表Servlet上下文对象,一个工程只有一个ServletContext对象,是一个域对象(这里的域指的是整个web工程)。

web容器在启动时,他会为每个web程序都创建一个ServletContext对象,他代表了当前的web应用。

作用1:共享数据,即在某个Servlet中保存的数据可以在另一个Servlet中获得。

存入数据的Servlet类,用于保存数据到ServletContext对象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello");
// this.getInitParameter(); 初始化参数
// this.getServletConfig(); Servlet配置
// this.getServletContext(); Servlet上下文

ServletContext context = this.getServletContext();
String username = "zhangsan"; // 数据

// 将一个数据以键值对形式保存在了ServletContext中。
context.setAttribute("username", username);
}
}

读入数据的Servlet类,用于从ServletContext对象中读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class GetServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String)context.getAttribute("username");

resp.getWriter().print(username);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}

作用2:获取初始化参数。

1
2
3
4
5
<!-- 是上下文参数(属于整个web工程) -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
1
2
3
4
ServletContext context = this.getServletContext();

String url = context.getInitParameter("url");
resp.getWriter().print(url);

作用3:请求转发。

访问当前url时,将消息转发给指定的其他url(当前url不会发生变化,重定向会变化)。当前url只充当转发功能。请求转发不需要添加项目名,只需要/+映射路径

1
2
3
// 转发的请求参数
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/servlet01");
requestDispatcher.forward(req, resp); // 调用forward实现请求转发

请求转发的特点:

  • 浏览器地址栏没有变化
  • 是一次请求
  • 共享Request域中的数据
  • 可以转发到WEB-INF目录下
  • 无法访问项目以外的其他资源(如百度)

作用4:读取资源文件

  • 在java目录下新建properties
  • 在resources目录下新建properties

发现都被打包到了同一个路径下:/WEB-INF/classes,将这个路径称为classpath。

1
2
username=root
password=123456
1
2
3
4
5
6
7
8
InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");

Properties prop = new Properties();
prop.load(resourceAsStream);
String user = prop.getProperty("username");
String password = prop.getProperty("password");

resp.getWriter().print(url + ':' + password);

HttpServletResponse

web服务器接收到客户端的HTTP请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse对象。

  • 如果要获取客户端请求过来的参数,使用HttpServletRequest
  • 如果要给客户端响应一些信息,使用HttpServletResponse

负责向浏览器发送数据的方法:

  • getOutputStream()
  • getWriter()

常见应用:

  1. 向浏览器输出消息
  2. 下载文件
1
2
// 设置让浏览器能够支持附件下载
respones.setHeader("Content-Disposition", "attachment;filename="+fileName);
  1. 实现重定向
1
response.sendRedirect("/projectName/url"); // 重定向到其他url

重定向和转发的区别

  • 请求转发时,url不会发生变化。(转发在服务器内部完成,不需要加项目名路径,如"/url")
  • 重定向时,url会发生变化。(需要加项目名路径,如"/projectName/url")

在前端文件中写跳转链接时,因其不能得知服务器内部的项目结构,因此需要人为指定contextPath(在Servlet程序中不需要再指定当前项目在服务器内的路径)

细节:当用户提交完请求,浏览器会记录下最后一次请求的全部信息。当用户按下功能键F5,就会发起浏览器记录的最后一次请求。在此情况下如果使用请求转发的方式跳转页面,用户按下F5后会再次发起请求,因此这种情况应该使用重定向。

HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过HTTP协议访问服务器,HTTP协议中的所有消息信息会被封装到HttpServletRequest,通过该类的方法可以获得客户端传来的请求信息。

  1. 获取传递的参数
1
2
String username = request.getParameter("username");
String[] hobbies = request.getParameterValues("hobbies");
  1. 请求转发
1
request.getRequestDispatcher("/success.jsp").forward(requset, response);

image-20210501221519475

  1. 获取请求头中Referer信息(浏览器发起请求时的url),可用于重定向回原地址
1
String url = req.getHeader("Referer");

Web中 / 斜杠的不同意义

在web中,/ 是一种绝对路径:

  • / 如果被浏览器解析,得到的地址是:http://ip:port/ (指写在静态html代码中,无法被服务器解析,只能被浏览器解析)
1
<a href="/">斜杠</a>
  • / 如果被服务器解析,得到的地址是:http://ip:port/工程路径
1
2
3
4
5
6
7
8
// 映射
<url-pattern>/servlet1<url-pattern>

// 获取绝对路径
servletContext.getRealPath("/");

// 请求转发
request.getRequestDispacther("/");
  • 特殊情况:response.senRedirect("/"); 会将斜杠发送给浏览器解析,得到http://ip:port/

/WEB-INF/目录下的资源文件,客户端无法直接访问(即不能在浏览器中输入url直接跳转),而只能在servlet程序中跳转