官网:Servlet 3.1 API Documentation - Apache Tomcat 8.0.53
为什么需要?
提出需求: 请用你现有的html css javascript,开发网站,比如可以让用户留言/购物/支付?
引入我们动态网页(能和用户交互)技术===>Servlet
是什么?
Servlet是Server Applet的简称,是用Java编写的服务器端程序,是JavaEE平台下的技术标准。其主要功能在于和浏览器交互并生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。从原理上来讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
Servlet是Tomcat的一个组件,Servlet的功能需要依赖一个servlet-api.jar,这个包是由Tomcat提供的,Tomcat在初始化Servlet时,首先读取web.xml文件,根据web.xml文件中的参数信息初始化ServletConfig、ServletContext对象,同时帮助我们创建HttpServletRequest和HttpServletResponse对象一并交给Servlet实例,此时,Servlet就具有了相关的功能。
Servlet技术的核心是Servlet,它是所有Servlet类必须直接或者间接实现的一个接口。在编写实现Servlet接口的类时,直接实现它,在扩展实现Servlet这个接口的类时,间接实现它。Servlet有三种实现方式:实现Servlet接口、继承抽象类GenericServlet、继承HttpServlet。
【Tomcat部分详见Java Web(七)__Tomcat(二)-CSDN博客和Java Web(七)__Tomcat(一)-CSDN博客 】
注:
Servlet 类没有 main() 函数,不能独立运行,只能作为一个模块被载入到 Servlet 容器,然后由 Servlet 容器来实例化,并调用其中的方法;
一个动态页面对应一个 Servlet 类,开发一个动态页面就是编写一个 Servlet 类;【当用户请求到达时,Servlet 容器会根据配置文件(web.xml)来决定调用哪个类】
浏览器访问 Servlet 流程分析
注:
servlet3.0 前使用 web.xml
servlet3.0 版本以后(包括 3.0)支持注解, 同时支持 web.xml。
Servlet接口,只负责定义Servlet程序的访问规范;
GenericServlet抽象类实现了Servlet接口,做了很多空实现,并持有一个ServletConfig类的引用,并提供了一些ServletConfig的使用方法;
HttpServlet抽象类实现了service方法,并实现了请求分发处理;
所有的 Servlet 功能都是通过一个名为Servlet的接口(Interface)向外暴露的,编写 Servlet 代码,可以从实现 Servlet 接口开始。
返回值 | 方法 | 备注 |
void | init(ServletConfig config) | Servlet 实例化之后,由 Servlet 容器调用,用来初始化 Servlet 对象。该方法只能被调用一次。 |
参数 config 用来向 Servlet 传递配置信息【即Servlet的初始化参数】。 |
||
void | service(ServletRequest req,ServletResponse res) | Servlet 容器调用该方法处理客户端请求。 【每次请求都会调用该方法】 |
void | destroy() | 服务器关闭、重启或者 Servlet 对象被移除时,由 Servlet 容器调用,负责释放 Servlet 对象占用的资源。 |
ServletConfig | getServletConfig() | 该方法用来获取 ServletConfig 对象,该对象中包含了 Servlet 的初始化参数。 |
String | getServletInfo() | 该方法用于获取 Servlet 的信息,例如作者、版本、版权等。 |
注:
init( )、service( )、destroy( )是Servlet生命周期的方法。init()、destroy( )各自只执行一次,即Servlet创建和销毁时。而Service会在每次有新请求到来时被调用。也就是说我们的业务代码需要写在service()方法中。
Tomcat反射创建Servlet后,会调用init()时传入ServletConfig对象,并创建ServletRequest和ServletResponse对象传递给service()方法。
需要重写其全部的方法,比较繁琐。
需求:
1、开发一个 HelloServlet
2、当浏览器 访问 http://localhost:8080/web 应用名/helloServlet 时,后台输出 "hiHelloServelt"
实现:
1、编写类HelloServlet,实现 Servlet 接口
2、实现 service 方法,处理请求,并响应数据
package com.lhyedu.servlet;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 1. 开发一个Servlet 需要 实现Servlet接口
* 2. 实现Servlet接口的方法5个
*/
public class HelloServlet02 implements Servlet {
/**
* 1.初始化 servlet
* 2.当创建HelloServlet 实例时,会调用init方法
* 3. 该方法只会被调用一次
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
/**
* 返回ServletConfig 也就是返回Servlet的配置
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 1. service方法处理浏览器的请求(包括get/post)
* 2. 当浏览器每次请求Servlet时,就会调用一次service
* 3. 当tomcat调用该方法时,会把http请求的数据封装成实现ServletRequest接口的request对象
* 4. 通过servletRequest 对象,可以得到用户提交的数据
* 5. servletResponse 对象可以用于返回数据给tomcat->浏览器
*/
@Override
public void service(ServletRequest servletRequest,
ServletResponse servletResponse) throws ServletException, IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
if("GET".equals(method)) {
doGet(); //用doGet() 处理GET请求
} else if("POST".equals(method)) {
doPost(); //用doPost() 处理POST请求
}
}
/**
* 用于响应get请求的
*/
public void doGet() {
System.out.println("doGet() 被调用..");
}
/**
* 用于响应post请求的
*/
public void doPost() {
System.out.println("doPost() 被调用..");
}
/**
* 返回servlet信息,使用较少
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 1. 该方法是在servlet销毁时,被调用
* 2. 只会调用一次
*/
@Override
public void destroy() {
System.out.println("destroy() 被调用...");
}
}
3、在 web.xml 中去配置 servlet 程序的访问地址,即:给hello.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">
<!--web.xml主要用来配置该web应用使用到的Servlet-->
<!-- 配置HelloServlet -->
<!--servlet标签: 给tomcat配置Servlet程序-->
<servlet>
<!--servlet-name标签: 给Servlet程序取一个别名(程序员决定), 该名字唯一;一般跟写的Servlet类名相同-->
<!--servlet-class标签:Servlet的全限定类名,即Servlet类的位置: Tomcat在反射生成该Servlet需要使用。-->
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
<!--load-on-startup 表示在tomcat 启动时,会自动的加载servlet实例-->
<!--<load-on-startup>1</load-on-startup>-->
</servlet>
<!--servlet-mapping标签: 给Servlet程序配置方位地址-->
<servlet-mapping>
<!--servlet-name标签:告诉服务器,当前配置的地址给哪个Servlet使用-->
<!--url-pattern标签:对外提供访问Servlet的url地址:http://ip[域名]:port/工程路径/helloServlet
取名是程序员决定的,相当于项目名。【/符号一定要加上】-->
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
执行步骤:
【1】首先浏览器通过http://localhost:8080/servletDemo/helloServlet来找到web.xml中的url-pattern;
【2】匹配到url-pattern后,就会找到servlet的名字helloServlet;
【3】通过servlet-name找到servlet-class【即servlet的位置】,然后到其中找到对应的处理方式进行处理。
<load-on-startup>标签:
- <load-on-startup>节点必须写在<servlet>节点的内部,且作为最后一个节点,取值必须是整数;
- 如果是大于等于0的整数:表示在服务器启动时就被创建并初始化。【避免第一次访问时被创建并初始化的,减少第一次访问的耗时】
- 如果有多个Servlet设置了<load-on-startup>节点的值,那么值越小越优先执行;
- 如果是负数则表示第一次被访问时创建并初始化,也就是默认情况,可以省略不写。
4、通过浏览器访问HelloServlet ,进行验证;
一个通用的 Servlet 类,并没有针对某种场景进行特殊处理,尤其是 HTTP 协议,我们必须手动分析和封装 HTTP 协议的请求信息和响应信息;
实现了 Servlet 接口,并提供了除 service() 方法以外的其他四个方法的简单实现;
通过继承 GenericServlet 类创建 Servlet ,只需要重写 service() 方法即可,大大减少了创建 Servlet 的工作量。
GenericServlet做了如下工作:
• 提升init方法中原本是形参的servletConfig对象的作用域(成员变量 , 方便其他方法使用)
• init方法中还调用了一个init空参方法, 如果我们希望在servlet创建时做一些什么初始化操作,可以继承GenericServlet后,覆盖init空参方法
• 由于其他方法内也可以使用servletConfig, 于是写了一个getServletContext方法
• 但没有实现service()方法。
用于开发基于 HTTP 协议的 Servlet 程序,在 GenericServlet 的基础上专门针对 HTPP 协议进行了处理。
它继承了GenericServlet且为抽象类。
通过源码发现,HttpServlet已经实现了service方法,但不希望被实例化;
创建示意图:
package com.hspedu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class OKServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//可以写自己的业务处理代码
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//可以写自己的业务处理代码
}
}
说明:
【1】重写方法分析
Pretected修饰,希望子类重写
若不重写get和post方法,浏览器页面会显示:405 (http.method get not supported)。即HttpServlet虽然在service中帮我们写了请求方式的判断。但是针对每一种请求,业务逻辑代码是不同的,HttpServlet无法知晓子类想干嘛,所以就抽出七个方法,并且提供了默认实现:报405、400错误,提示请求不支持。
但这种实现本身非常鸡肋,简单来说就是等于没有。所以,不能让它被实例化,不然调用doXxx方法是无用功。Filter用到了责任链模式,Listener用到了观察者模式、Servlet也不会放过使用设计模式的机会:模板方法模式【即父类把能写的逻辑都写完,把不确定的业务代码抽成一个方法,调用它。就等子类重写该方法,实现整个业务代码】。
【2】热加载选项说明
on update action
on frame deactivation
【3】端口修改
只会影响到当前的项目,而不是去修改 server.xml。
在默认情况下,Tomcat 会自动加载 webapps 目录下的 JavaWeb 应用,并把它发布到名为 localhost 的虚拟主机中。
部署 JavaWeb 应用最快捷的方式:直接将 JavaWeb 应用的所有文件复制到 Tomcat 的 /webapps 目录下
运行方式:
开放式目录结构__部署步骤
1. 进入 Windows DOS 命令行窗口
2. 引入 javax.servlet 包
先在命令行中输入set classpath=,然后将 servlet-api.jar 文件拖进命令行内,回车执行 。
3. 编译 Servlet
4. 创建目录结构
依照 JavaWeb 应用的固定目录结构,在 Tomcat 中为该 Servlet 创建目录。
5. 将 Servlet 移动到 Tomcat 目录中
6. 配置 web.xml
7、访问
Servlet 的生命周期由 Servlet 容器管理;在 javax.servlet.Servlet 接口中定义3 个方法:init()、service()、destory(),它们分别在 Servlet 生命周期的不同阶段被 Servlet 容器调用。
【1】初始化阶段
Servlet 容器(比如: Tomcat)加载 Servlet,加载完成后,Servlet 容器会创建一个 Servlet 实例并调用init()方法,init()方法只会调用一次。
(1)加载和实例化Servlet
加载成功后,容器会通过反射对 Servlet 进行实例化【编写 Servlet 类时,需要一个无参构造方法】;
Servlet 容器装载 Servlet的情形:
通过在 web.xml 文件中添加<load-on-startup>1</load-on-startup>
1 表示装载的顺序,默认值为0;
load-on-startup 是 web.xml 中的一个节点,是 servlet 元素的子元素,用来标记 Servlet 容器启动时是否初始化当前 Servlet,以及当前 Servlet 的初始化顺序
(2)调用init()方法初始化Servlet实例
在 Servlet 的整个生命周期内,init() 方法只能被调用一次。
初始化期间,Servlet 实例可以通过 ServletConfig 对象获取在 web.xml 或者 @WebServlet 中配置的初始化参数。
【2】处理浏览器请求阶段(service 方法)
当初始化完成后,Servlet 容器会将该实例保存在内存中,通过调用它的 service() 方法,为接收到的请求服务。
每收到一个 http 请求,服务器就会产生一个新的线程去处理;对于 Servlet 的每一次请求,Servlet 容器都会调用一次 service() 方法,并创建新的 ServletRequest 和 ServletResponse 对象;即 service() 方法在 Servlet 的整个生命周期中会被调用多次。
Servlet 容器接收到来自客户端请求时,容器会针对该请求分别创建一个 ServletRequst 对象和 ServletResponse 对象,将它们以参数的形式传入 service() 方法内,并调用该方法对请求进行处理;
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//count值,在不断的累计,说明HelloServlet是单例的
count++;
//tomcat每处理一次http请求,就生成一个新的线程
System.out.println("hi,helloservlet......" + count + Thread.currentThread().getId());
}
注:执行 service() 方法前,init() 方法必须已成功执行;
【3】终止阶段 destory 方法(体现 Servlet 完整的生命周期)
当web 应用被终止,或者Servlet 容器终止运行,或者Servlet 类重新装载时,会调用destroy()方法,释放该实例使用的资源;
比如重启tomcat ,或者 redeploy web 应用;例如:关闭数据库连接,关闭文件的输入流和输出流等,随后该实例被 Java 的垃圾收集器所回收。
对于每个 Servlet 实例来说,destory() 方法只能被调用一次;
注:要看到 destory 效果,需要保证 servlet 实例被创建过或者 Servlet 重新装载时(比如 redeploy)。
一般至少需要三个组件,分别是 Web 服务器、脚本语言运行时和数据库。例如,部署 PHP 网站一般选择「Apache + PHP 运行时 + MySQL」的组合。
【1】web服务器
一种对外提供 Web 服务的软件,可以接收浏览器的 HTTP 请求,并将处理结果返回给浏览器。
通常所说的 Web 服务器,功能往往都比较单一,一般只能提供 http(s) 服务,让用户访问静态资源(HTML 文档、图片、CSS 文件、JavaScript 文件等)。它们不能执行任何编程语言,也不能访问数据库,更不能让用户注册和登录。
若只有 Web 服务器,那您只能部署静态网站,不能部署动态网站。
【2】运行环境(运行时)
习惯将以上各种支持脚本语言运行的部件统称为运行环境,或者运行时(Runtime)。开发网站使用的编程语言一般都是脚本语言(比如 PHP、ASP、Python)。
部署网站时都是将源代码直接扔到服务器上,然而源代码自己并不能运行,必须要有解释器的支持。当用户访问动态页面时,解释器负责分析、编译和执行源代码,然后得到处理结果。
解释器:执行脚本语言的核心部件。包含如下辅助性的部件:
【3】数据库
Web 服务器不带数据库,编程语言也不带数据库。数据库是一款独立的软件,若要实现用户注册、发布文章、提交评论等功能,就必须安装一款数据库。
更多【java-Java Web(八)--Servlet(一)】相关视频教程:www.yxfzedu.com