servlet
简介
Servlet 本质还是一个Java 类,只是它是被 tomcat 调用的。
1. Servlet 类是单例的,服务器只会创建一个 Servlet 实例对象,常驻内存。
2. Tomcat 每收到一次 http 请求,就会产生一个新的线程去处理;创建一个用于封装 HTTP 请求消息的 ServletRequest 对象和一个 代表 HTTP 响应消息的 ServletResponse 对象;然后调用 Servlet 的 Service() 方法并将请求和响应对象作为参数传递进去。
3. Servlet 是一个供其他 Java 程序来调用的 Java 类,不能独立运行。
4. 在 Servlet 的整个生命周期中,init方法只被调用一次,而每次请求都导致 Servlet 引擎调用一次 Servlet 的 Service 方法
5. 对于每次访问请求,Servlet 引擎都会创建一个新的 HttpServletRequest 请求对象和一个新的 HttpServletRequest 响应对象,然后将这两个对象作为参数传递给他调用的 Servlet 的 service 方法
导入 servlet-api.jar 包

servlet 类编写
HelloServlet 必须实现 Servlet 接口并重写方法
package com.servlet;
import javax.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet { // 开发sevlet需要实现servlrt接口
// 初始化servlet,当创建HelloServlet 实例时,会调用init方法,该方法只会被调用一次
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init()被调用");
}
// 返回Servlet的配置
@Override
public ServletConfig getServletConfig() {
return null;
}
// service 方法处理浏览器的请求,包括get和post
// 当浏览器每次请求servlet时,就会调用一次service
// 当tomcat调用该方法时,会把http请求的数据封装成实现了 ServletRequest 接口的 request 对象
// 通过 ServletRequest 对象,得到用户提交的数据
// 通过 ServletResponse 对象,返回数据给客户
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
// 返回servlet 信息
@Override
public String getServletInfo() {
return null;
}
// 该方法在servlet 被销毁时调用,只会被调用一次
@Override
public void destroy() {
}
}
web.xml 编写
web.xml 主要用来配置web 应用使用到的 ServletResponse

在web.xml里配置servlet
<servlet>
<servlet-name>HelloServlet</servlet-name>
<!--servlet类的全路径,Tomcat在反射生成该Servlet时需要使用-->
<servlet-class>com.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<!-- 该sservlet访问url的路径,取名由程序员决定-->
<!--http://loclahost:8080/servlet/hello-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
load-on-startup
Servlet 容器即 Tomcat启动时自动装载某些 Servlet ,实现这个需要在 web.xml 中配置
<load-on-startup>1</load-on-startup> 标签,数字越小优先级越高

浏览器发送请求,到最终服务器调用 Servlet 中的方法,过程描述
1、用户在浏览器中输入 URL:http://localhost:8080/servlet/hello
2、Tomcat 服务器接收到请求,截取路径/hello
3、Tomcat 服务器找到HelloServlet 项目
4、Tomcat 服务器在 web .xml 文件中查找路径对应的 Servlet :com.servlet.HelloServlet(类完整包名)
5、Tomcat 通过反射机制,创建 com.servlet.HelloServlet 对象
6、Tomcat 服务器调用com.servlet.HelloServlet 对象的 service 方法
Servlet 对象的生命周期
默认情况下,启动服务器Servlet并不会被实例化。
用户发送第一次http请求的时候,Servlet对象被实例化了;
对象被创建出来之后,Tomcat服务器马上调用了Servlet对象的init方法。(init方法在调用的时候Servlet对象已经被创建出来了);
用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用Servlet对象的service 犯法;
用户第二次、第三次发送http请求时,Servlet 对象并没有被新建,还是之前创建的Servlet对象,并直接调用了该 Servlet 对象的 service 方法。说明 Servlet 对象是单例的。
构造方法只执行一次;
init 方法只执行一次;
service 方法客户端发送多少次请求执行多少次;
destroy 方法只执行一次;
GET和POST请求的分发处理
实现需求:servlet分别处理前端传来的 GET 和 POST 请求的逻辑
前端页面register.html
<!DOCTYPE html> <html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/hello"
method="post">
u: <input type="text" name="username"/><br><br> <input type="submit" value="注册用户"/>
</form>
</body>
</html>

HelloServlet 类中 Service 方法逻辑编写
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 从 ServletRequest 中获取请求方式 get/post
// ServletRequest 中没有getMethod 方法,用快捷键 command+alt+B 快捷键,找到 其子类 HttpServletRequest 找到getMethod 方法
// 向下转型
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod(); // 调用 getMethod 方法,获得前端的请求类型
System.out.println("method="+method); // 访问 http://localhost:8080/servlet/hello 返回 GET
// 实现doGet和doPost的逻辑
if("GET".equals(method)){
this.doGet();
}else{
this.doPost();
}
}
public void doGet(){
System.out.println("doGet()方法被调用");
}
public void doPost(){
System.out.println("doPost方法被调用");
}

通过继承 HttpServlet 开发 Servlet
HttpServlet 实现了 Servlet 接口里的方法,通过继承 HttpServlet 开发 Servlet 更加方便。
已经写好了 doPost、doGet 方法。


servlet 类编写
public class HiServlet extends HttpServlet {
// 重写 HttpServlet 的 doGet 和doPost 方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HiServlet doGet() 方法被调用");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HiServlet doPost() 方法被调用");
}
}
web.xml 编写
<!--配置 HiServlet-->
<servlet>
<servlet-name>HiServlet</servlet-name>
<servlet-class>com.servlet.HiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HiServlet</servlet-name>
<url-pattern>/hi</url-pattern>
</servlet-mapping>
html 页面
<!DOCTYPE html> <html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/hi"
method="post">
<!-- action 指明 url; method 定义 html 请求类型-->
u: <input type="text" name="username"/><br><br> <input type="submit" value="注册用户"/>
</form>
</body>
</html>

提交form 表单,控制台输出

HttpServlet 源码
service 方法
实际运行过程中,Tomcat 先在 HiService 中找 service 方法,每找到到父类 HttpService 方法中找到 service 方法。req.getMethod() 获取请求类型,this.doPost() 根据动态绑定机制调用 HiServlet 的 doPost() 方法。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
idea 快捷创建 Servlet


类创建自动继承 HttpServlet 并且重写 doGet 和 doPost 方法

运行流程图

Servlet 注解(annotation)方式开发
注解方式配置和 web.xml 配置本质上是一样的。
首先对 src 里的所有包进行扫描,发现有java 类被 WebServlet 进行修饰,就说明该类是 Servlet。读取 urlPatterns 。
创建 Servlet 类,不需要进行 web.xml 配置
// 用注解的方式来配置
// @WebServlet 注解
// urlPatterns 对应 web.xml 的 <url-pattern></url-pattern>; {"/ok1","/ok2"} 可以给 OkServlet 配置多个 url
@WebServlet(urlPatterns = {"/ok1","/ok2"})
public class OkServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("注解方式配置的Servlet doGet被调用");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("注解方式配置的Servlet doPost被调用");
}
}
访问 http://localhost:8080/servlet/ok1 控制台打印

ServletConfig
ServletConfig 类是为Servlet 程序配置信息的类。
Servlet 程序和 SewrvletConfig 对象都是由 Tomcat 负责创建。
Servlet 程序默认是第一次方位的时候常见,ServletConfig 在 Servlet 程序创建时,就创建一个对应的 ServletComfig 对象。
ServletConfig 类可以获得Servlet 程序的 servlet-name,para-init初始化参数
HttpServletRequest 类
HttpServletRequest 对象代表客户端请求,HTTP 请求头中的所有信息都封装在这个对象中,通过这个对象的方法,可以获得客户的这些信息。

HttpServletRequest 常用方法
需求说明:从一个前端表单中提交数据给Servlet,然后在Servlet中 通过 HttpSErvlertRequest 对象获取相关数据。
getRequestURI方法:获取请求资源路径URI
getRequestURL方法:获取请求资源路径URL
getRemoteAddr方法:获得请求客户端的ip地址
getHeader("请求的相关字段")方法:可以获取希望请求的头的相关信息
servlet 类编写
@WebServlet(urlPatterns = {"/request"})
public class HttpServlerRequestMethods extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// HttpServletRequest 对象代表客户端请求,HTTP 请求头中的所有信息都封装在这个对象中,通过这个对象的方法,可以获得客户的这些信息。
System.out.println("获取前端表单传来的各种信息");
// HttpServletRequest对象的getRequest方法:获取请求资源路径URI
System.out.println("获取请求资源路径URI:" + req.getRequestURI());
// HttpServletRequest对象的getRequestURL方法:获取请求资源路径URL
System.out.println("获取请求资源路径URL:" + req.getRequestURL());
// HttpServletRequest对象的getRemoteAddr方法:获得请求客户端的ip地址
System.out.println("获得请求客户端的ip地址:" + req.getRemoteAddr());
// HttpServletRequest对象的getHeader("请求的相关字段")方法:可以获取希望请求的头的相关信息
System.out.println("获取http请求头的Host:" + req.getHeader("Host"));
System.out.println("获取该http请求的发起地址Referer:" + req.getHeader("Referer"));
System.out.println("获取该http请求发起的浏览器的信息User-Agent:" + req.getHeader("User-Agent"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
前端页面提交表单后,控制台打印

getParameter 方法获取表单提交数据
// 获取表单提交的数据 getParameter 方法
// username=xx&pwd=Yy&hobby=hsp
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
System.out.println("username="+username);
System.out.println("pwd="+pwd);
控制台打印

注意:
解决接收参数的中文乱码问题 setCharacterEncoding("utf-8")
使用 ServletResponse 接口从服务器向浏览器发送信息
// 解决中文乱码问题
// 设置返回http文件头的 ContentType
// text/html 响应文本的类型,浏览器会根据这个类型来解析数据
resp.setContentType("text/html;charset=utf-8");
// 从服务器向浏览器发送信息
PrintWriter writer = resp.getWriter();
// 浏览器可以识别html代码
writer.print("<h1>提交的用户名为:"+username+"</h1>");
writer.print("<h1>提交的密码为:"+pwd+"</h1>");
浏览器显示
