目录
一、Servlet简介
Servlet是一种可以与用户进行交互的技术,它能够处理用户提交的HTTP请求并做出响应。Servlet程序可以完成JavaWeb应用程序中处理请求并发送响应的过程。通过Servlet技术,可以收集来自页面表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。Servlet是基于Java的、与平台无关的服务器端组件。
- Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术
- 使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容
- Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由Web服务器运行Servlet
- 什么是servlet?
简单来说servlet是运行在服务器上的java程序,servlet由servlet容器管理,servlet容器也叫servlet引擎,是servlet的运行环境,给发送的请求和相应之上提供网络服务
- servlet的作用
通俗来讲servlet专门用来接收客户端的请求,专门接收客户端的请求数据,然后调用底层service处理数据并生成结果
浏览器http请求------> tomcat服务器-------> 到达servlet-----> 执行doget,dopost方法------> 返回数据
<1>客户端发送请求到服务器端
<2>服务器将请求信息发送至Servlet
<3>Servlet生成响应内容并将其传给服务器
<4>服务器将响应返回给客户端
- servlet里的三大作用域
request(请求):它的作用范围是一次请求和响应,是三个作用域中最小的。 session(会话):它的作用比request要大一点,一次会话过程中,它的作用域就一直存在,(默认是30分钟) servletcontext:它作用范围最大,作用于整个服务器中。(Application)
概念:运行在服务器端的小程序
- Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则
- 将来我们定义了一个类,实现Servlet接口,复写方法
1.1 快速入门
需求分析: 编写一个Servlet类,并使用IDEA中Tomcat插件进行部署,最终通过浏览器访问所编写的Servlet程序
具体的实现步骤为:
1. 创建Web项目web-demo,导入Servlet依赖坐标
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <!-- 此处为什么需要添加该标签? provided指的是在编译和测试过程中有效,最后生成的war包时不会加入 因为Tomcat的lib目录中已经有servlet-api这个jar包,如果在生成war包的时候生效就会和Tomcat中的jar包冲突,导致报错 --> <scope>provided</scope> </dependency>
2. 创建:定义一个类,实现Servlet接口,并重写接口中所有方法,并在service方法中输入一句话
package com.learn.web; import javax.jws.WebService; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; /** * @author 咕咕猫 * @version 1.0 */ @WebServlet("/demo1") public class ServletDemo1 implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("Servlet,Hello world~"); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
3. 配置:在类上使用@WebServlet注解,配置该Servlet的访问路径
@WebServlet("/demo1")
4. 访问:启动Tomcat,浏览器中输入URL地址访问该Servlet
http://localhost:8080/web-demo/demo1
5. 浏览器访问后,在控制台会打印
servlet hello world~
说明servlet程序已经成功运行。
二、执行流程
- 浏览器发出
http://localhost:8080/web-demo/demo1
请求,从请求中可以解析出三部分内容,分别是localhost:8080
、web-demo
、demo1
○根据
localhost:8080
可以找到要访问的Tomcat Web服务器○根据
web-demo
可以找到部署在Tomcat服务器上的web-demo项目○根据
demo1
可以找到要访问的是项目中的哪个Servlet类,根据@WebServlet后面的值进行匹配
- 找到ServletDemo1这个类后,TomcatWeb服务器就会为ServletDemo1这个类创建一个对象,然后调用对象中的service方法
○ServletDemo1实现了Servlet接口,所以类中必然会重写service方法供Tomcat Web服务器进行调用
○service方法中有ServletRequest和ServletResponse两个参数,ServletRequest封装的是请求数据,ServletResponse封装的是响应数据,后期我们可以通过这两个参数实现前后端的数据交互
Servlet执行以下主要任务:
- 读取客户端(浏览器)发送的显式的数据。这包括页面上的HTML表单,或者也可以是来自Applet或自定义的HTTP客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的HTTP请求数据。这包括Cookies、媒体类型和浏览器能理解的压缩格式等
- 处理数据并生成结果。这个过程可能需要访问数据库,执行RMI或CORBA调用,调用Web服务,或者直接计算得出对应的响应
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML或XML)、二进制文件(GIF图像)、Excel等
- 发送隐式的HTTP响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如HTML),设置Cookies和缓存参数,以及其他类似的任务
小结:
1. Servlet由谁创建?Servlet方法由谁调用?
答:Servlet由Web服务器创建,Servlet方法由Web服务器调用
2. 服务器怎么知道Servlet中一定有service方法?
答:因为我们自定义的Servlet,必须实现Servlet接口并复写其方法,而Servlet接口中有service方法
2.1 执行原理
- 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
- 查找web.xml文件,是否有对应的<url-pattern>标签体内容
- 如果有,则在找到对应的<servlet-class>全类名
- tomcat会将字节码文件加载进内存,并且创建其对象
- 调用其方法(暂且理解为调用service()方法)
三、生命周期
生命周期: 对象的生命周期指一个对象从被创建到被销毁的整个过程。
Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:
1. 加载和实例化==:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象
默认情况,Servlet会在第一次访问被容器创建,但是如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,那么我们能不能把Servlet的创建放到服务器启动的时候来创建,具体如何来配置?
@WebServlet(urlPatterns = "/demo1",loadOnStartup = 1)
loadOnstartup的取值有两类情况
(1)负整数:第一次访问时创建Servlet对象
(2)0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
2. 初始化:在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只==调用一次==
3. 请求处理:每次请求Servlet时,Servlet容器都会调用Servlet的service()方法对请求进行处理
4. 服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
package com.learn.web;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
/**
* @author 咕咕猫
* @version 1.0
*/
@WebServlet(urlPatterns="/demo2",loadOnStartup = 1)
public class ServletDemo2 implements Servlet {
/*
初始化方法
1. 调用时机:默认情况下,Servlet被第一次访问时(第一次访问时会创建Servlet对象,这个对象会调用),调用
* loadOnStartup:
2. 调用次数:1次
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init....");
}
/*
提供服务方法
1. 调用时机:每一次Servlet对象对访问时,就会调用
2. 调用次数:多次
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Servlet,Hello world~");
}
/*
销毁方法
1. 调用时机:内存释放或服务器关闭时,Servlet对象会被销毁,会调用该方法
2. 调用次数:1次
*/
@Override
public void destroy() {
System.out.println("destroy.....");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
四、方法介绍
-
初始化方法,在Servlet被创建时执行,只执行一次
void init(ServletConfig config)
-
提供服务方法, 每次Servlet被访问,都会调用该方法
void service(ServletRequest req, ServletResponse res)
-
销毁方法,当Servlet被销毁时,调用该方法。在内存释放或服务器关闭时销毁Servlet
void destroy()
-
获取Servlet信息
String getServletInfo()
//该方法用来返回Servlet的相关信息,没有什么太大的用处,一般返回一个空字符串即可
public String getServletInfo() {
return " ";
}
-
获取ServletConfig(Servlet配置)对象
ServletConfig getServletConfig()
ServletConfig对象,在init方法的参数中有,而Tomcat Web服务器在创建Servlet对象的时候会调用init方法,必定会传入一个ServletConfig对象,我们只需要将服务器传过来的ServletConfig进行返回即可。
Servlet中的生命周期方法:
1. 被创建:执行init方法,只执行一次
- Servlet什么时候被创建
* 默认情况下,第一次被访问时Servlet被创建
* 可以配置执行Servlet的创建时机
在<servlet>标签下配置
①第一次被访问时,创建 <load-on-startup>的值为负数
②在服务器启动时,创建 <load-on-startup>的值为0或正整数
- Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Serlvet是单例的
* 多个用户同时访问时,可能存在线程安全问题
* 解决:尽量不要在Serlvet中定义成员变量。即使定义了成员变量,也不要修改值
2. 提供服务:执行service方法,执行多次
- 每次访问Servlet时,Service方法都会被调用一次
3. 被销毁:执行destroy方法,只执行一次
- Servlet被销毁时执行。服务器关闭时,Servlet被销毁
- 只有服务器正常关闭时,才会执行destroy方法
package com.learn.web;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
/**
* @author 咕咕猫
* @version 1.0
* Servlet生命周期方法
*/
@WebServlet(urlPatterns="/demo3",loadOnStartup = 1)
public class ServletDemo3 implements Servlet {
private ServletConfig config;
/*
初始化方法
1. 调用时机:默认情况下,Servlet被第一次访问时(第一次访问时会创建Servlet对象,这个对象会调用),调用
* loadOnStartup:
2. 调用次数:1次
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
this.config = config;
System.out.println("init....");
}
@Override
public ServletConfig getServletConfig() {
return config;
}
/*
提供服务方法
1. 调用时机:每一次Servlet对象对访问时,就会调用
2. 调用次数:多次
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Servlet,Hello world~");
}
/*
销毁方法
1. 调用时机:内存释放或服务器关闭时,Servlet对象会被销毁,会调用该方法
2. 调用次数:1次
*/
@Override
public void destroy() {
System.out.println("destroy.....");
}
@Override
public String getServletInfo() {
return null;
}
}
五、体系结构
- GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
◇ 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
- HttpServlet:对http协议的一种封装,简化操作
◇ 定义类继承HttpServlet
◇ 复写doGet/doPost方法
因为我们将来开发B/S架构的web项目,都是针对HTTP协议,所以我们自定义Servlet,会通过继承HttpServlet
@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//TODO GET 请求方式处理逻辑
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//TODO Post 请求方式处理逻辑
System.out.println("post...");
}
}
-
要想发送一个GET请求,请求该Servlet,只需要通过浏览器发送
http://localhost:8080/web-demo/demo4
,就能看到doGet方法被执行了 -
要想发送一个POST请求,请求该Servlet,单单通过浏览器是无法实现的,这个时候就需要编写一个form表单来发送请求,在webapp下创建一个
a.html
页面,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/web-demo/demo4" method="post">
<input name="username"/><input type="submit"/>
</form>
</body>
</html>
Servlet的简化编写就介绍完了,接着需要思考两个问题:
-
HttpServlet中为什么要根据请求方式的不同,调用不同的方法?
-
如何调用?
针对问题一,我们需要回顾之前的知识点==前端发送GET和POST请求的时候,参数的位置不一致,GET请求参数在请求行中,POST请求参数在请求体中==,为了能处理不同的请求方式,我们得在service方法中进行判断,然后写不同的业务处理,这样能实现,但是每个Servlet类中都将有相似的代码,针对这个问题,优化的策略如下
package com.learn.web;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author 咕咕猫
* @version 1.0
*/
@WebServlet("/demo5")
public class ServletDemo5 extends MyHttpServlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//根据请求方式的不同,进行分别的处理
HttpServletRequest request = (HttpServletRequest) servletRequest;
//1.获取请求方式
String method = request.getMethod();
//2.判断
if ("GET".equals(method)){
//get方式的处理逻辑
doget(servletRequest,servletResponse);
}else if ("POST".equals(method)){
//post方式的处理逻辑
dopost(servletRequest,servletResponse);
}
}
protected void dopost(ServletRequest servletRequest, ServletResponse servletResponse) {
}
protected void doget(ServletRequest servletRequest, ServletResponse servletResponse) {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
对Servlet接口进行继承封装,来简化代码开发。
package com.learn.web;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author 咕咕猫
* @version 1.0
*/
public class MyHttpServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//根据请求方式的不同,进行分别的处理
HttpServletRequest request = (HttpServletRequest) servletRequest;
//1.获取请求方式
String method = request.getMethod();
//2.判断
if ("GET".equals(method)){
//get方式的处理逻辑
doget(servletRequest,servletResponse);
}else if ("POST".equals(method)){
//post方式的处理逻辑
dopost(servletRequest,servletResponse);
}
}
protected void dopost(ServletRequest servletRequest, ServletResponse servletResponse) {
}
protected void doget(ServletRequest servletRequest, ServletResponse servletResponse) {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
有了MyHttpServlet这个类,以后再编写Servlet类的时候,只需要继承MyHttpServlet,重写父类中的doGet和doPost方法,就可以用来处理GET和POST请求的业务逻辑。接下来,可以把ServletDemo5代码进行改造
@WebServlet("/demo5")
public class ServletDemo5 extends MyHttpServlet {
@Override
protected void doGet(ServletRequest req, ServletResponse res) {
System.out.println("get...");
}
@Override
protected void doPost(ServletRequest req, ServletResponse res) {
System.out.println("post...");
}
}
将来页面发送的是GET请求,则会进入到doGet方法中进行执行,如果是POST请求,则进入到doPost方法。这样代码在编写的时候就相对来说更加简单快捷。
类似MyHttpServlet这样的类Servlet中已经为我们提供好了,就是HttpServlet,翻开源码,可以搜索service()
方法,会发现HttpServlet做的事更多,不仅可以处理GET和POST还可以处理其他五种请求方式。
六、urlPattern配置
Servlet类编写好后,要想被访问到,就需要配置其访问路径(urlPattern)
urlpartten:Servlet访问路径
- 一个Servlet可以定义多个访问路径:@WebServlet({ "/d4" , "/dd4" , "/ddd4" })
- 路径定义规则
1. /xxx
2. /xxx/xxx:多层路径,目录结构
3. *.do
- 一个Servlet,可以配置多个urlPattern
package com.learn.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author 咕咕猫 * @version 1.0 * urlPattern:一个Servlet可以配置多个路径 */ @WebServlet(urlPatterns = {"/demo7","/demo8"}) public class ServletDemo7 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo7 get。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
在浏览器上输入
http://localhost:8080/web-demo/demo7
,http://localhost:8080/web-demo/demo8
这两个地址都能访问到ServletDemo7的doGet方法。
6.1 urlPattern配置规则
- 精确匹配
package com.learn.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author 咕咕猫 * @version 1.0 * 精确匹配 */ @WebServlet(urlPatterns = "/user/select") public class ServletDemo8 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo8 get。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
访问路径
http://localhost:8080/web-demo/user/select
- 目录匹配
package com.learn.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author 咕咕猫 * @version 1.0 * 目录匹配 */ @WebServlet(urlPatterns = "/user/*") public class ServletDemo9 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo9 get。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
访问路径
http://localhost:8080/web-demo/user/任意
- 扩展名匹配
package com.learn.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author 咕咕猫 * @version 1.0 * 扩展名匹配 */ @WebServlet(urlPatterns = "*.do") public class ServletDemo10 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo10 get。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
- 任意匹配
package com.learn.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author 咕咕猫 * @version 1.0 * 任意匹配:/ * */ @WebServlet(urlPatterns = "/") public class ServletDemo11 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo11 get。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
访问路径
http://localhost:8080/demo-web/任意
6.2 小结
- urlPattern总共有四种匹配方式,分别是精确匹配、目录匹配、扩展名匹配、任意匹配
- 五种配置的优先级为 精确匹配 > 目录匹配 > 扩展名匹配 > /* > /
七、XML配置
前面对应Servlet的配置,我们都使用的是@WebServlet,这个是Servlet从3.0版本后开始支持注解配置,3.0版本前只支持XML配置文件的配置方法。
对于XML的配置步骤有两步:
- 编写Servlet类
package com.learn.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author 咕咕猫 * @version 1.0 * */ public class ServletDemo12 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("demo12 get。。。"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
在web.xml中配置该Servlet
<?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_4_0.xsd" version="4.0"> <!--Servlet全类名--> <servlet> <servlet-name>demo12</servlet-name> <servlet-class>com.learn.web.ServletDemo12</servlet-class> </servlet> <!--Servlet访问路径--> <servlet-mapping> <servlet-name>demo12</servlet-name> <url-pattern>/demo12</url-pattern> </servlet-mapping> </web-app>
这种配置方式和注解比起来,麻烦很多,所以建议使用注解来开发。但是要认识上面这种配置方式,因为并不是所有的项目都是基于注解开发的。