Servlet是运行在java服务器端的程序,用于接收和响应来着客户端基于HTTP协议的请求
如果想实现Servlet的功能,可以通过实现javax。servlet。Servlet接口或者继承它的实现类
核心方法:service(),任何客户端的请求都会经过该方法
1.创建一个WEB项目
2.重写一个类继承GenericServlet
3.重写service方法
4.在web.xml中配置Servlet
5.部署并启动项目
6.通过浏览器测试
例:
- package servlet;
- import javax.servlet.GenericServlet;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
-
- import java.io.IOException;
-
- /*
- Servlet快速入门
- */
- public class ServletDemo01 extends GenericServlet {
-
- @Override
- public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
- System.out.println("service执行了...");
- }
- }
web.xml
- "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-name>servletDemo01servlet-name>
- <servlet-class>servlet.ServletDemo01servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo01servlet-name>
- <url-pattern>/servletDemo01url-pattern>
- servlet-mapping>
- web-app>
网页输入:http://localhost:8080/demo1/servletDemo01
Servlet实现方式
1.第一种
实现Servlet接口,实现所有的抽象方法。该方法支持最大程度的自定义
2.第二种
继承GenericServlet抽象类,必须重写servic方法,其他方法可选择重写。该方法让我们开发Servlet变得简单。但是这种方式和HTTP协议无关
3.第三种
继承HttpServlet抽象类,需要重写doGet和doPost方法。该方式表示请求和响应都需要和HTTP协议相关
步骤
1.创建一个类继承HttpServlet
2.重写doGet和doPost方法
3.在web.xml中配置Servlet
4.部署并启动项目
5.通过浏览器测试
例:
- package servlet;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /*
- 快速入门02
- */
- public class ServletDemo02 extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- System.out.println("方法执行了...");
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req, resp);
- }
- }
web.xml:
- "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-name>servletDemo01servlet-name>
- <servlet-class>servlet.ServletDemo01servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo01servlet-name>
- <url-pattern>/servletDemo01url-pattern>
- servlet-mapping>
-
-
- <servlet>
- <servlet-name>servletDemo02servlet-name>
- <servlet-class>servlet.ServletDemo02servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo02servlet-name>
- <url-pattern>/servletDemo02url-pattern>
- servlet-mapping>
- web-app>
对象的生命周期,就是对象从出生到死亡的过程。即:出生 -> 活着 -> 死亡。官方说法是对象创建到销毁的过程
出生:请求第一次到达 Servlet 时,对象就创建出来,并且初始化成功。只出生(创建)一次,将对象放到内存中
活着:服务器提供服务的整个过程中,该对象一直存在,每次都是执行 service 方法
死亡:当服务停止时,或者服务器宕机时,对象死亡
例:
- package servlet;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /*
- Servlet的生命周期
- */
- public class ServletDemo03 extends HttpServlet {
- //对象出生的过程
- @Override
- public void init() throws ServletException {
- System.out.println("对象创建并初始化成功");
- }
- //对象服务的过程
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- System.out.println("接收到了客户端的请求...");
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req, resp);
- }
- //对象销毁的过程
-
- @Override
- public void destroy() {
- System.out.println("对象销毁了...");
- }
- }
web.xml:
-
- <servlet>
- <servlet-name>servletDemo03servlet-name>
- <servlet-class>servlet.ServletDemo03servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo03servlet-name>
- <url-pattern>/servletDemo03url-pattern>
- servlet-mapping>
结论:Servlet 对象只会创建一次,销毁一次。所以 Servlet 对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就称它为单例模式
由于 Servlet 采用的是单例模式,也就是整个应用中只有一个实例对象。所以我们需要分析这个唯一的实例对象中的类成员是否线程安全
模拟用户登录功能来查看 Servlet 线程是否安全
例:
- package 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;
- /*
- Servlet线程安全的问题
- */
- public class ServletDemo04 extends HttpServlet{
- //1.定义用户名成员变量
- private String username = null;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //2.获取用户名
- username = req.getParameter("username");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //3.获取输出流对象
- PrintWriter pw = resp.getWriter();
- //4.响应给客户端浏览器
- pw.print("welcome:" + username);
- //5.关流
- pw.close();
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
- <servlet>
- <servlet-name>servletDemo04servlet-name>
- <servlet-class>servlet.ServletDemo04servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo04servlet-name>
- <url-pattern>/servletDemo04url-pattern>
- servlet-mapping>
谷歌浏览器中url传递参数username=aaa
火狐浏览器中url传递参数username=bbb
结果两个浏览器都是welcome:bbb
结论:一个浏览器代表一个线程,多个浏览器代表多个线程。按理说我们期望的应该是每个浏览器查看的都应该是自己的用户名。而现在的结果是浏览器中数据混乱。因此,我们可以认为 Servlet 是线程不安全的!
解决:定义类成员要谨慎。如果是共用的,并且只会在初始化时赋值,其他时间都是获取的话,那么是没问题的。如果不是共用的,或者每次使用都有可能对其赋值,那就要考虑线程安全问题了,可以将其定义到 doGet 或 doPost 方法内或者使用同步功能即可。
方法1:将username由成员变量,放到方法中
- package 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;
- /*
- Servlet线程安全的问题
- */
- public class ServletDemo04 extends HttpServlet{
- //1.定义用户名成员变量
- // private String username = null;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- String username = null;
- //2.获取用户名
- username = req.getParameter("username");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //3.获取输出流对象
- PrintWriter pw = resp.getWriter();
- //4.响应给客户端浏览器
- pw.print("welcome:" + username);
- //5.关流
- pw.close();
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
方法二:使用同步代码块
- package 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;
- /*
- Servlet线程安全的问题
- */
- public class ServletDemo04 extends HttpServlet{
- //1.定义用户名成员变量
- private String username = null;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //String username = null;
- synchronized (this){
- //2.获取用户名
- username = req.getParameter("username");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //3.获取输出流对象
- PrintWriter pw = resp.getWriter();
- //4.响应给客户端浏览器
- pw.print("welcome:" + username);
- //5.关流
- pw.close();
- }
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
第一种
具体名称的方式。访问的资源路径必须和映射配置完全相同
第二种
/ 开头 + 通配符的方式。只要符合目录结构即可,不用考虑结尾是什么
第三种
通配符 + 固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径
注意:优先级问题。越是具体的优先级越高,越是模糊通用的优先级越低。第一种 -> 第二种 -> 第三种
例:
第一种:具体名称的方式
访问:http://localhost:8080/demo1/servletDemo05
- package servlet;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /*
- Servlet不同映射方式
- */
- public class ServletDemo05 extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- System.out.println("ServletDemo05执行了...");
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
-
- }
-
-
- <servlet>
- <servlet-name>servletDemo05servlet-name>
- <servlet-class>servlet.ServletDemo05servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo05servlet-name>
- <url-pattern>/servletDemo05url-pattern>
- servlet-mapping>
第二种:/开头+通配符的方式
访问:http://localhost:8080/demo1/servlet/....
-
- <servlet>
- <servlet-name>servletDemo05servlet-name>
- <servlet-class>servlet.ServletDemo05servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo05servlet-name>
- <url-pattern>/servlet/*url-pattern>
- servlet-mapping>
第三种:通配符+固定格式结尾
访问:http://localhost:8080/demo1/xxx.do
- <servlet>
- <servlet-name>servletDemo05servlet-name>
- <servlet-class>servlet.ServletDemo05servlet-class>
-
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo05servlet-name>
- <url-pattern>*.dourl-pattern>
- servlet-mapping>
1.第一次访问时创建
优势:减少对服务器内存的浪费。提高了服务器启动的效率
弊端:如果有一些要在应用加载时就做的初始化操作,无法完成
2.服务器加载时创建
优势:提前创建好对象,提高了首次执行的效率。可以完成一些应用加载时要做的初始化操作
弊端:对服务器内存占用较多,影响了服务器启动的效率
修改Servlet创建时间:在
正整数代表服务器加载时创建,值越小、优先级越高。 负整数或不写代表第一次访问时创建
序号为1,就是服务器启动时第一个加载
序号为2,就是服务器启动时第二个加载
如果两个Servlet都要配置为正整数,那么值小的优先级高
-
- <servlet>
- <servlet-name>servletDemo03servlet-name>
- <servlet-class>servlet.ServletDemo03servlet-class>
- <load-on-startup>1load-on-startup>
- servlet>
- <servlet-mapping>
- <servlet-name>servletDemo03servlet-name>
- <url-pattern>/servletDemo03url-pattern>
- servlet-mapping>
会在启动tomcat的时候初始化
默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中
它的映射路径是
但是当找不到对应的 Servlet 路径时,就去找默认的 Servlet,由默认 Servlet 处理。所以,一切都是 Servlet。
如果我们访问一个不存在的url,浏览器会加载一个404界面,这个404界面,其实就是tomcat配置的默认的Servlet处理的结果。
ServletConfig 是 Servlet 的配置参数对象,在 Servlet 的规范中,允许为每一个 Servlet 都提供一
些初始化的配置。所以,每个 Servlet 都有一个自己的 ServletConfig
作用:在 Servlet 的初始化时,把一些配置信息传递给 Servlet
生命周期
生命周期:和Servlet相同
如果Servlet配置了
ServletConfig的配置信息都是键值对的形式
在
一个init-param配置一个信息,一个信息由name和value组成(只允许一个键值对存在)
例:
- package servlet;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /*
- ServletConfig的演示
- */
- public class ServletConfigDemo extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
web.xml
- "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-name>servletConfigDemoservlet-name>
- <servlet-class>servlet.ServletConfigDemoservlet-class>
-
- <init-param>
-
- <param-name>encodingparam-name>
-
- <param-value>UTF-8param-value>
- init-param>
- <init-param>
- <param-name>descparam-name>
- <param-value>This is ServletConfigparam-value>
- init-param>
- servlet>
- <servlet-mapping>
- <servlet-name>servletConfigDemoservlet-name>
- <url-pattern>/servletConfigDemourl-pattern>
- servlet-mapping>
-
- web-app>
返回值 | 方法名 | 说明 |
---|---|---|
String | getInitParameter(String name) | 根据参数名称获取参数的值 |
Enumeration | getlnitParameterNames() | 获取所有参数名称的枚举 |
String | getServletName() | 获取Servlet的名称 |
ServletContext | getServletContext() | 获取ServletContext对象 |
- package servlet;
-
- import javax.servlet.ServletConfig;
- import javax.servlet.ServletContext;
- 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.util.Enumeration;
-
- /*
- ServletConfig的演示
- */
- public class ServletConfigDemo extends HttpServlet {
-
- //声明ServletConfig配置对象
- private ServletConfig config;
-
- //通过init方法来为ServletConfig配置对象赋值
- @Override
- public void init(ServletConfig config) throws ServletException {
- this.config = config;
- }
-
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //演示ServletConfig常用方法
- //根据key获取value
- String encodingValue = config.getInitParameter("encoding");
- System.out.println(encodingValue);
- //获取所有的key
- Enumeration
keys = config.getInitParameterNames(); - while (keys.hasMoreElements()){
- //获取每一个key
- String key = keys.nextElement();
- //根据key获取value
- String value = config.getInitParameter(key);
- System.out.println(key + "," + value);
- }
- //获取Servlet的名称
- String servletName = config.getServletName();
- System.out.println(servletName);
- //获取ServletContext对象
- ServletContext context = config.getServletContext();
- System.out.println(context);
-
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
访问:http://localhost:8080/demo2/servletConfigDemo
ServletContext 是应用上下文对象(应用域对象)。每一个应用中只有一个 ServletContext 对象
作用:可以配置和获得应用的全局初始化参数,可以实现 Servlet 之间的数据共享
生命周期:应用一加载则创建,应用被停止则销毁。
出生——活着——死亡
出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)
活着:只要应用一直提供服务,该对象就一直存在。
死亡:应用被卸载(或者服务器挂了),该对象消亡。
域对象指的是对象有作用域。也就是有作用范围域对象可以实现数据的共享不同作用范围的域对象,共享数据的能力也不一样
在 Servlet 规范中,一共有 4 个域对象ServletContext 就是其中的一个它也是 web 应用中最大的作用域,也叫 application 域它可以实现整个应用之间的数据共享
ServletContext 并不属于某个 Servlet 的配置,而是针对于整个应用的配置,也叫全局的初始化参数
在
- package servlet;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- public class ServletContextDemo extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
web.xml
- <servlet>
- <servlet-name>servletContextDemoservlet-name>
- <servlet-class>servlet.ServletContextDemoservlet-class>
- servlet>
- <servlet-mapping>
- <servlet-name>servletContextDemoservlet-name>
- <url-pattern>/servletContextDemourl-pattern>
- servlet-mapping>
-
-
- <context-param>
- <param-name>globalEncodingparam-name>
- <param-value>UTF-8param-value>
- context-param>
- <context-param>
- <param-name>globalDescparam-name>
- <param-value>This is ServletContextparam-value>
- context-param>
返回值 | 方法名 | 说明 |
---|---|---|
String | getInitParameter(String name) | 根据名称获取全局配置的参数 |
String | getContextPath() | 获取当前应用的虚拟访问目录 |
String | getRealPath(string path) | 根据虚拟目录获取应用部署的磁盘绝对路径 |
void | setAttribute(String name,Object value) | 向应用域对象中存储数据 |
Object | getAttribute(String name) | 通过名称获取应用域对象中的数据 |
void | removeAttribute(String name) | 通过名称移除应用域对象中的数据 |
- package servlet;
-
- import javax.servlet.ServletContext;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- public class ServletContextDemo extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //获取ServletContext对象
- ServletContext context = getServletContext();
- //常用方法演示
- //获取全局配置参数:getInitParameter(String key) 根据key获取value
- String value = context.getInitParameter("globalDesc");
- System.out.println(value);
- //获取应用的虚拟目录:getContextPath()
- String contextPath =context.getContextPath();
- System.out.println(contextPath);
- //根据虚拟目录获取绝对路径:getRealPath(String path)
- String realPath = context.getRealPath("/");
- System.out.println(realPath);
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
访问:http://localhost:8080/demo2/servletContextDemo
1.创建一个web项目
2.定义一个类,继承HttpServlet
3.重写doGet和doPost方法
4.在类上使用@WebServlet注解配置Servlet
5.部署并启动项目
6.通过浏览器测试
- package Servlet;
-
- 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;
-
- /*
- 基于注解方式开发
- @WebServlet("Servlet路径")
- */
- @WebServlet("/servletDemo01")
- public class ServletDemo01 extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- System.out.println("ServletDemo01执行了...");
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req,resp);
- }
- }
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface WebServlet {
- //指定Servlet的名称。等效于web.xml文件
标签下的 - String name() default "";
- //用于映射Servlet。等效于
- String[] value() default {};
- String[] urlPatterns() default {};
- //指定Servlet的加载时机。等效于
- int loadOnStartup() default -1;
- //指定Servlet的初始化参数。等效于
- WebInitParam[] initParams() default {};
- //指定Servlet是否支持异步
- boolean asyncSupported() default false;
- //指定Servlet的小图标
- String smallIcon() default "";
- //指定Servlet的大图标
- String largeIcon() default "";
- //指定Servlet的描述信息
- String description() default "";
- //指定Servlet的显示名称
- String displayName() default "";
- }