1 servlet
1.1 servlet介绍
1.2 servlet入门
1.2.1 案例
(1)
(2)
(3)
(4)
1.2.2 servlet执行流程
详解:
1、输入地址回车发起请求
2、解析请求地址
3、找到对应的项目
4、根据项目找到xml文件
5、解析资源路径
6、找到应用资源
7、执行方法,进行响应
1.2.3 servlet关系视图
1.2.3 servlet实现方式
我们在实现Servlet功能时,可以选择以下三种方式:
第一种:实现Servlet接口,接口中的方法必须全部实现。
使用此种方式,表示接口中的所有方法在需求方面都有重写的必要。此种方式支持最大程度的自定义。
第二种:继承GenericServlet,service方法必须重写,其他方可根据需求,选择性重写。
使用此种方式,表示只在接收和响应客户端请求这方面有重写的需求,而其他方法可根据实际需求选择性重写,使我们的开发Servlet变得简单。但是,此种方式是和HTTP协议无关的。
第三种:继承HttpServlet,它是javax.servlet.http包下的一个抽象类,是GenericServlet的子类。如果我们选择继承HttpServlet时,只需要重写doGet和doPost方法,不要覆盖service方法。
使用此种方式,表示我们的请求和响应需要和HTTP协议相关。也就是说,我们是通过HTTP协议来访问的。那么每次请求和响应都符合HTTP协议的规范。请求的方式就是HTTP协议所支持的方式(目前我们只知道GET和POST,而实际HTTP协议支持7种请求方式,GET POST PUT DELETE TRACE OPTIONS HEAD )。
HttpServlet实现:
1.3 servlet使用细节
1.3.1 Servlet的生命周期
对象的生命周期,就是对象从生到死的过程,即:出生——活着——死亡。用更偏向 于开发的官方说法就是对象创建到销毁的过程。
出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。
活着:服务器提供服务的整个过程中,该对象一直存在,每次只是执行service方法。
死亡:当服务停止时,或者服务器宕机时,对象消亡。
方式一:通过分析Servlet的生命周期我们发现,它的实例化和初始化只会在请求第一次到达Servlet时执行,而销毁只会在Tomcat服务器停止时执行,由此我们得出一个结论,Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。
方式二:一旦tomcat服务器启动的时候就会创建Servlet对象, 后面每次请求也不会再次创建Servlet对象,而是直接调用 doGet 或者 doPost方法来处理用户的请求, 服务器关闭
<servlet> <!--servlet的名字 必须和 mapping 中的name一样--> <servlet-name>ServletDemo1</servlet-name> <!--servlet的全路径--> <servlet-class>com.itheima.servlet.ServletDemo1</servlet-class> <!--参数 可以是负数也可以是正数 默认是负数 负数: 第一次请求servlet的时候会创建对象,且调用init方法进行初始化 正数:服务器一旦启动就会创建servlet对象,且数字越小优先级别越高--> <load-on-startup>1</load-on-startup> </servlet>
1.3.2 Servlet的线程安全
代码
package com.itheima.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类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为Servlet它不是线程安全的。
图解
解决方式
-
方式一:将成员变量定义为局部变量
package com.itheima.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 com.itheima.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 { 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); } }
1.3.2 Servlet不同的映射方式
2 ServletConfig
2.1 介绍
基本概念
它是Servlet的配置参数对象,在Servlet规范中,允许为每个Servlet都提供一些初始化配置。所以,每个Servlet都一个自己的ServletConfig。它的作用是在Servlet初始化期间,把一些配置信息传递给Servlet。
生命周期
由于它是在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同。这里需要注意的是,如果Servlet配置了<load-on-startup>1</load-on-startup>
,那么ServletConfig也会在应用加载时创建。
2.2 ServletConfig使用
2.2.1 如何获取
首先,我们要清楚的认识到,它可以为每个Servlet都提供初始化参数,所以肯定可以在每个Servlet中都配置。那是配置在Servlet的声明部分,还是映射部分呢?我们接下来先准备一个Servlet,然后给同学们揭秘。
/** * 演示Servlet的初始化参数对象 */ public class ServletDemo8 extends HttpServlet { //定义Servlet配置对象ServletConfig private ServletConfig servletConfig; /** * 在初始化时为ServletConfig赋值 * @param config * @throws ServletException */ @Override public void init(ServletConfig config) throws ServletException { this.servletConfig = config; } /** * @param req * @param resp * @throws ServletException * @throws IOException */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //输出ServletConfig System.out.println(servletConfig); } /** * 调用doGet方法 * @param req * @param resp * @throws ServletException * @throws IOException */ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
<!--配置ServletDemo8--> <servlet> <servlet-name>servletDemo8</servlet-name> <servlet-class>com.itheima.web.servlet.ServletDemo8</servlet-class> </servlet> <servlet-mapping> <servlet-name>servletDemo8</servlet-name> <url-pattern>/servletDemo8</url-pattern> </servlet-mapping>
2.2.2 如何配置
在上一小节中,我们已经准备好了Servlet,同时也获取到了它的ServletConfig对象,在本小节中我们将告诉同学们如何配置初始化参数,它需要使用<servlet>
标签中的<init-param>
标签来配置。这也就揭秘上一小节的悬念,Servlet的初始化参数都是配置在Servlet的声明部分的。并且每个Servlet都支持有多个初始化参数,并且初始化参数都是以键值对的形式存在的。接下来,我们看配置示例:
<!--配置ServletDemo8--> <servlet> <servlet-name>servletDemo8</servlet-name> <servlet-class>com.itheima.web.servlet.ServletDemo8</servlet-class> <!--配置初始化参数--> <init-param> <!--用于获取初始化参数的key--> <param-name>encoding</param-name> <!--初始化参数的值--> <param-value>UTF-8</param-value> </init-param> <!--每个初始化参数都需要用到init-param标签--> <init-param> <param-name>servletInfo</param-name> <param-value>This is Demo8</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>servletDemo8</servlet-name> <url-pattern>/servletDemo8</url-pattern> </servlet-mapping>
2.2.3 常用方法
/**
* 演示Servlet的初始化参数对象
*/
public class ServletDemo8 extends HttpServlet {
//定义Servlet配置对象ServletConfig
private ServletConfig servletConfig;
/**
* 在初始化时为ServletConfig赋值
* @param config
* @throws ServletException
*/
@Override
public void init(ServletConfig config) throws ServletException {
this.servletConfig = config;
}
/**
* doGet方法输出一句话
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.输出ServletConfig
System.out.println(servletConfig);
//2.获取Servlet的名称
String servletName= servletConfig.getServletName();
System.out.println(servletName);
//3.获取字符集编码
String encoding = servletConfig.getInitParameter("encoding");
System.out.println(encoding);
//4.获取所有初始化参数名称的枚举
Enumeration<String> names = servletConfig.getInitParameterNames();
//遍历names
while(names.hasMoreElements()){
//取出每个name
String name = names.nextElement();
//根据key获取value
String value = servletConfig.getInitParameter(name);
System.out.println("name:"+name+",value:"+value);
}
//5.获取ServletContext对象
ServletContext servletContext = servletConfig.getServletContext();
System.out.println(servletContext);
}
/**
* 调用doGet方法
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
3 ServletContext
3.1 ServletContext概述
3.1.1 基本介绍
ServletContext对象,它是应用上下文对象。每一个应用有且只有一个ServletContext对象。它可以实现让应用中所有Servlet间的数据共享。
3.1.2 生命周期
出生——活着——死亡
出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)
活着:只要应用一直提供服务,该对象就一直存在。
死亡:应用被卸载(或者服务器挂了),该对象消亡。
3.1.3 域对象概念
域对象的概念,它指的是对象有作用域,即有作用范围。
域对象的作用,域对象可以实现数据共享。不同作用范围的域对象,共享数据的能力不一样。
在Servlet规范中,一共有4个域对象。今天我们讲解的ServletContext就是其中一个。它也是我们接触的第一个域对象。它是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间的数据共享功能。
3.2 ServletContext使用
配置全局化初始参数
常用方法
获取该对象的方法有
1. 通过request对象获取
request.getServletContext();
2. 通过HttpServlet获取
this.getServletContext();
3. 通过ServletConfig对象
ServletConfig对象.getServletContext()
其它路径如何获取
//String getRealPath(String path): 获取文件的真实(服务器)路径
String b = context.getRealPath("/b.txt");//web目录下资源访问
System.out.println(b);
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
System.out.println(c);
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
System.out.println(a);
设置的数据都是全局的,也就是说你在一个类里设置了数据,其它的类也是可以获取到这个数据
4 注解开发Servlet
4.1 Servlet3.0规范
4.1 注解开发案例
浏览器输入对应路径即可