Java web之Servlet技术详解

Java web之Servlet技术详解
write:2022-4-14

详细学习Servlet之前,需要明白java web的入门知识即对Servlet容器和对Servlet有大概认识,回顾:java web应用入门(https://blog.csdn.net/qq_48666555/article/details/124085461)

1. Servlet简介

(1)Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中。
(2)Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet和客户的通信采用“请求/响应”的模式。
(3)运行在Web服务器中的Servlet可完成如下功能:
动态生成HTML文档。
把请求转发给其他Servlet组件。
读取客户端的Cookie,以及向客户端写入Cookie。
访问其他服务器资源(如数据库服务器或基于Java的应用服务器)。

2. Servlet API

Servlet的框架是由两个Java包组成:
javax.servlet包:定义了所有的Servlet类都必须实现或扩展的通用接口和类。
javax.servlet.http包:定义了采用HTTP协议通信的HttpServlet类。

2.1 Servlet API类框图

Servlet类框图
servlet之所以功能众多,一是servlet由java语言编写,只要编写者能力深厚,能写出完成各种功能的servlet,二是由于Servlet容器为servlet提供了许多资源;
Servlet容器为Servlet提供了十八般武器:
1)请求对象(ServletRequest和HttpServletRequest):Servlet从该对象中获取来自客户端的请求信息。
2)响应对象(ServletResponse和HttpServletResponse):Servlet通过该对象来生成响应结果。
3)Servlet配置对象(ServletConfig):当容器初始化一个Servlet对象时,会向Servlet提供一个ServletConfig对象,Servlet 通过该对象来获取初始化参数信息以及ServletContext对象。
4)Servlet上下文对象(ServletContext):Servlet通过该对象来访问容器为当前Web应用提供的各种资源。

2.2 Servlet接口

1)Servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必须实现这一接口。
2)在Servlet接口中定义了五个方法,其中有三个方法在Servlet的生命周期的不同阶段被Servlet容器调用(Servlet规范规定):
init()方法:负责初始化Servlet对象;
service()方法:负责响应客户的请求;
destroy()方法:当Servlet对象退出生命周期时,负责释放占用的资源。
3)Servlet接口还定义了以下两个返回Servlet的相关信息的方法。JavaWeb应用中的程序代码可以访问Servlet的这两个方法,从而获得Servlet的配置信息以及其他相关信息:
getServletConfig():返回一个ServletConfig对象,该对象中包含了Servlet的初始化参数信息。
getServletInfo():返回一个字符串,该字符串中包含了Servlet的创建者、版本和版权等信息。

2.3 Servlet接口及实现类

在这里插入图片描述

2.3.1 GenericServlet抽象类

GenericServlet抽象类为Servlet接口提供了通用实现(只要是实现init()方法),它与任何网络应用层协议无关。

public abstract class GenericServlet 
  implements Servlet, ServletConfig, java.io.Serializable{

 private transient ServletConfig config;
 
 public void destroy(){}
 
 public void init(ServletConfig config) throws ServletException {
   this.config = config;  //使当前Servlet对象与Servlet容器传入的ServletConfig对象关联
   this.init();
 }
 
 public void init() throws ServletException {} //子类可以重新实现该方法
 
 public abstract void service(ServletRequest req, ServletResponse res)
 throws ServletException, IOException;
 ... 
 public ServletContext getServletContext() {
   return getServletConfig().getServletContext();
 }

}

2.3.2 HttpServlet抽象类

如果你的Servlet类扩展了HttpServlet类,你通常不必实现service方法,因为HttpServlet类已经实现了service方法,该方法的声明形式如下:

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;

在HttpServlet的service方法中,首先从HttpServletRequest对象中获取HTTP请求方式的信息,然后再根据请求方式调用相应的方法。例如:如果请求方式为GET,那么调用doGet方法;如果请求方式为POST,那么调用doPost方法。

2.3.3 ServletRequest接口

在这里插入图片描述
ServletRequest接口中封装了客户请求信息,如客户请求方式、参数名和参数值、客户端正在使用的协议, 以及发出客户请求的远程主机信息等。ServletRequest接口还为Servlet提供了直接以二进制方式读取客户请求数据流的ServletInputStream。
ServletRequest的子类可以为Servlet提供更多的和特定协议相关的数据. 例如: HttpServletRequest 提供了读取HTTP Head信息的方法。

ServletRequest接口的主要方法:
getContentType 返回客户请求数据MIME类型
getInputStream 返回以二进制方式直接读取客户请求数据的输入流
getParameter 根据给定的参数名返回参数值
getRemoteAddr 返回远程客户主机的IP地址
getRemoteHost 返回远程客户主机名
getRemotePort 返回远程客户主机的端口

2.3.4 ServletResponse 接口

ServletResponse 接口为Servlet提供了生成响应结果的方法。它允许Servlet设置返回数据的长度和MIME类型, 并且提供输出流ServletOutputStream。
ServletResponse子类可以提供更多和特定协议相关的方法。例如: HttpServletResponse 提供设定HTTP HEAD信息的方法。

ServletResponse 接口的主要方法:
getOutputStream 返回可以向客户端发送二进制数据的输出流对象ServletOutputStream
getWriter 返回可以向客户端发送字符数据的PrintWriter对象
setCharacterEncoding 设置Servlet发送的响应数据的字符编码
setContentType 设置Servlet发送的响应数据的MIME类型
getCharacterEncoding 返回Servlet发送的响应数据的字符编码
getContentType 返回Servlet发送的响应数据的MIME类型

3. Java Web应用的生命周期

在一个Servlet容器中,可以同时运行多个java web应用,每个java web应用的生命周期都是独立的。
java web的生命周期由Servlet容器控制,每个java web的生命周期分为3个阶段:
启动阶段:加载Web应用的有关数据,创建ServletContext对象,对一些Servlet进行初始化。
运行时阶段:为客户端提供服务。
终止阶段:释放Web应用所占用的各种资源。

3.1 Java Web应用的启动阶段

Servlet容器在启动JavaWeb应用时,会完成以下操作:
(1)把web.xml文件中的数据加载到内存中。
(2)为JavaWeb应用创建一个ServletContext对象(每个java web应用都有唯一的一个)。
(3)对所有的Filter(过滤器)进行初始化。
(4)对那些需要在Web应用启动时就初始化的Servlet进行初始化。

3.1.1 JavaWeb应用被启动的时机

当Servlet容器启动时,会启动已经发布其中的所有Web应用。

以Tomcat作为Servlet容器为例:当web应用发布到Tomcat中,那么web有多种被启动的时机:
一:随着Tomcat的启动而被启动;
二:通过Tomcat的管理平台(手动控制)启动Web应用;
三:如果Web应用发布到Tomcat中,可以为Web应用配置一个元素,该元素有一个reloadable属性。如果这个属性设为true,Tomcat在运行时会监视在Web应用的WEB-INF/classes 和WEB-INF/lib目录下的类文件的改动,以及监视Web应用的WEB-INF/web.xml文件的改动。如果监测到有类文件或web.xml文件被更新,Tomcat会自动重新启动Web应用。

3.1.2 如何登入Tomcat的管理平台

1)从Tomcat的主页( http://localhost:8080 )上选择Tomcat Manager链接,就会登入Tomcat的管理平台http://localhost:8080/manager/html,会弹出安全验证窗口
2)为了通过安全验证,必须先修改Tomcat根目录下conf/tomcat-users.xml文件:

<user username="tomcat" password="tomcat" roles="admin,manager"/>

3)重启Tomcat服务器,然后就能以用户名tomcat,口令tomcat进行
在这里插入图片描述
注意:
\webapps\manager\META-INF\目录下的context.xml文件,新增方框内容,注释原来标签,否则是默认主机访问
在这里插入图片描述
Tomcat管理平台:
已发布的java web应用
在这里插入图片描述

3.2 JavaWeb应用的运行时阶段

在这个阶段,它的所有Servlet都处于待命状态,随时可以响应客户端的特定请求,提供相应的服务。
假如客户端请求的Servlet还没有被初始化,Servlet容器会先初始化Servlet,然后再调用它的service()服务方法。

3.3 JavaWeb应用的终止阶段

(1)销毁JavaWeb应用中所有处于运行时状态的Servlet。
(2)销毁JavaWeb应用中所有处于运行时状态的Filter。
(3)销毁所有与JavaWeb应用相关的对象,如ServletContext对象等,并且释放Web应用所占用的相关资源。

4. Servlet的生命周期

Servlet 的生命周期(由Servlet容器管理)可以分为三个阶段:
初始化阶段
运行时阶段,可以响应客户请求
终止阶段
在javax.servlet.Servlet接口中定义了三个方法init(), service(), destroy(),它们将分别在Servlet的不同阶段被调用。

4.1 Servlet的初始化阶段

1)Servlet容器加载Servlet类,把它的.class文件中的数据读入到内存中。
2)Servlet容器创建ServletConfig对象。ServletConfig对象包含了特定Servlet的初始化配置信息,如Servlet的初始参数。此外,Servlet容器还会使得ServletConfig对象与当前Web应用的ServletContext对象关联。
每一个web应用有一个ServletContext对象,对于其中的每一个Servlet,都有一个ServletConfig对象:
在这里插入图片描述
3)Servlet容器创建Servlet对象。
4)Servlet容器调用Servlet对象的init(ServletConfig config)方法。在Servlet接口的GenericServlet实现类的init(ServletConfig config)方法中,会建立Servlet对象与ServletConfig对象的关联关系。
Servlet–>ServletConfig–>ServletContext

4.1.1 Servlet被初始化的时机

在下列情况之一,Servlet会进入初始化阶段:
(1)当前Web应用处于运行时阶段,特定Servlet被客户端首次请求访问。多数Servlet都会在这种情况下被Servlet容器初始化。
(2)如果在web.xml文件中为一个Servlet设置了load-on-startup元素,那么当Servlet容器启动Servlet所属的Web应用时,就会初始化这个Servlet。

<servlet>
    <servlet-name>myservlet</servlet-name>
    <servlet-class> MyServlet</servlet-class>
    <load-on-startup> 1 </load-on-startup>  //设置为1,当web应用启动时第一个被初始化的Servlet
   </servlet>

eg:

<servlet>
    <servlet-name>servlet1</servlet-name>
    <servlet-class> Servlet1</servlet-class>
    <load-on-startup> 1 </load-on-startup>  //设置为1,当web应用启动时第一个初始化的是Servlet1
   </servlet> 
   
  <servlet>
    <servlet-name>servlet2</servlet-name>
    <servlet-class> Servlet2</servlet-class>
    <load-on-startup> 2 </load-on-startup>   //设置为2,当web应用启动时初始化完Servlet1,Servlet2初始化
   </servlet>
  
  <servlet>
    <servlet-name>servletX</servlet-name>
    <servlet-class> ServletX</servlet-class>  //没有设置,只有当ServletX被客户端首次请求访问才初始化
   </servlet>

4.2 Servlet的运行时阶段

在这个阶段,Servlet可以随时响应客户端的请求。
对于到达Servlet容器的客户请求,Servlet容器创建特定于这个请求的ServletRequest对象(此时创建的ServletRequest对象就包含客户请求信息)和ServletResponse对象,然后调用 Servlet 的 service方法。service方法从ServletRequest对象获得客户请求信息、处理该请求,并通过ServletResponse对象来生成响应结果。

4.3 Servlet的终止阶段

当Web应用被终止,或Servlet容器终止运行时,Servlet容器会先调用 Servlet的destroy方法,终止所有在这个容器中处于运行时状态的Servlet。在destroy方法中,可以释放Servlet所占用的资源(例如关闭文件输入流和输出流,关闭与数据库的连接等) 。

5. 创建Servlet

使用HTTP协议通信,创建用户自己的HttpServlet类的步骤:
(1) 扩展 HttpServlet 抽象类。
(2) 覆盖HttpServlet的doGet()或doPost()等方法。
(3) 获取HTTP 请求信息,例如通过HttpServletRequest 对象来检索 HTML 表单所提交的数据或 URL 上的查询字符串。无论是HTML表单数据还是URL 上的查询字符串,在HttpServletRequest 对象中都以参数名/参数值的形式存放,你可以通过getParameter(String name)方法检索参数信息。
(4) 生成 HTTP 响应结果。通过HttpServletResponse 对象可以生成响应结果。HttpServletResponse 对象有一个 getWriter()方法,该方法返回一个 PrintWriter 对象。使用 PrintWriter 的 print()或 println()方法可以向客户端输出字符串数据。

5.1 创建HelloServlet类

第一步: 扩展 HttpServlet 抽象类。

public class HelloServlet extends HttpServlet

第二步:覆盖doGet()方法(此处假定客户请求是以get方式)

public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException ,ServletException

第三步:获取HTTP 请求中的请求参数信息

//用户请求:http://localhost:8080/helloapp/hello?clientName=小芳
//                                              请求参数名 =请求参数值
String clientName=request.getParameter("clientName");
if(clientName!=null)
       //字符编码转换
   		clientName=new String(clientName.getBytes("ISO-8859-1"),"GB2312");
else
   		clientName="我的朋友";

第四步:生成 HTTP响应结果

PrintWriter out;
String title="HelloServlet";

//设置响应正文的类型
response.setContentType("text/html;charset=GB2312");

//生成HTML正文
out = response.getWriter();
out.print("<HTML><HEAD><TITLE>"+title+"</TITLE>");
out.print("</HEAD><BODY>");
out.println("<h1><P> "+clientName+" : 您好</h1>");
out.print("</BODY></HTML>");

//关闭输出流
out.close();

5.2 发布HelloServlet类

HelloServlet的.class文件位于helloapp/WEB-INF/classes/mypack目录下。
在web.xml中为HelloServlet类加上如下和元素:

 <servlet>
       	 <servlet-name>HelloServlet</servlet-name>
        	<servlet-class>mypack.HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
  </servlet-mapping>

5.3 运行HelloServlet类

通过如下URL访问HelloServlet:
http://localhost:8080/helloapp/hello?clientName=小芳
在这里插入图片描述

6. ServletContext类的用法

6.1 ServletContext和Web应用关系

Servlet容器为java web应用提供的一个得力助手即ServletContext,下图说明ServletContext的作用:
在这里插入图片描述
1)当Servlet容器启动Web应用时,并为每个Web应用创建惟一的ServletContext对象。
2)在ServletContext中可以存放共享数据,它提供了读取或设置共享数据的方法:
setAttribute(String name,Object object)把一个对象和一个属性名绑定,将这个对象存储在ServletContext中。
getAttribute(String name)根据给定的属性名返回所绑定的对象
eg:

Apple o=new Apple( );
servletContext.setAttribute(“苹果",0);     //存放共享数据

Apple o1=(Apple )servletContext.getAttribute(“苹果");    //读取共享数据

3)可以把ServletContext看成是一个Web应用的服务器端组件的共享内存。

一个Servlet类如何获得当前web应用的ServletContext对象:GenericServlet抽象类(所有Servlet类的父类)中有一个getServletContext()方法,就用于返回与当前web应用关联的ServletContext对象。

6.2 CounterServlet例

  1. 创建CounterServlet
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class CounterServlet extends HttpServlet {

  private static final String CONTENT_TYPE = "text/html";

  public void init(ServletConfig config) throws ServletException {
  	super.init(config);
}

  
  public void service(HttpServletRequest request,
    HttpServletResponse response)
    throws ServletException, IOException {

    //获得ServletContext的引用
    ServletContext context = getServletContext();
    // 从ServletContext读取count属性
    Integer count = (Integer)context.getAttribute("count");

    // 如果count属性还没有设置, 那么创建count属性,初始值为0
    // one and add it to the ServletContext
    if ( count == null ) {
      count = Integer.valueOf(0);
      context.setAttribute("count", count);
    }

    response.setContentType(CONTENT_TYPE);
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head><title>WebCounter</title></head>");
    out.println("<body>");
    // 输出当前的count属性值
    out.println("<p><h1>The current COUNT is : " + count + ".</h1></p>");
    out.println("</body></html>");

    // 创建新的count对象,其值增1
    count =Integer.valueOf(count.intValue() + 1);
    // 将新的count属性存储到ServletContext中
    context.setAttribute("count", count);
  }
  public void destroy() {
  }
}
  1. 测试CounterServlet:
    (1)通过如下URL访问CounterServlet:
    http://localhost:8080/helloapp/counter
    当你第一次访问该Servlet,你会在浏览器上看到count值为0。
    (2)刷新该页面,你会看到每刷新一次count值增加1,假定最后一次刷新后count值为5。
    (3)再打开一个新的浏览器,访问CounterServlet。此时count值为6。
    (4)重新启动Tomcat服务器,然后再访问CounterServlet,你会看到count值又被初始化为0。

7. 多线程运行Servlet的并发问题

Servlet容器允许多个客户同时访问同一个Servlet
1)Servlet/JSP技术和ASP、PHP等相比,由于其多线程运行而具有很高的执行效率。
2)由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的并发问题。
3)如果在编写Servlet/JSP程序时不注意到多线程并发的问题,这往往造成编写的程序在少量用户访问时没有任何问题,而在并发用户上升到一定值时,就会经常出现一些莫明其妙的问题,对于这类随机性的问题调试难度也很大。

7.1 并发问题

  1. 会导致并发问题的HelloServlet1
public class HelloServlet1 extends HttpServlet  {
    String clientName=null; //clientName为实例变量,实例变量属于类的成员变量
    public void doGet(HttpServletRequest request,
    HttpServletResponse response) throws IOException ,ServletException
    {
    clientName=request.getParameter("clientName");
     if(clientName!=null)
     clientName=new String(clientName.getBytes("ISO-8859-1"),"GB2312");
     else
     clientName="我的朋友";

     System.out.println(Thread.currentThread().getName());
     try{Thread.sleep(10000); }catch(Exception e){}

     // 第四步:生成 HTTP 响应结果。
      PrintWriter out;
      ……
      out.println("<h1><P> "+clientName+" : 您好</h1>");
      out.print("</BODY></HTML>");
      //close out.
      out.close();

    }
}

通过两个浏览器同时访问HelloServlet1
在这里插入图片描述
两个线程同时操纵HelloServlet1对象的clientName实例变量
在这里插入图片描述
导致并发问题的操作时序
在这里插入图片描述
两个线程各自操纵HelloServlet对象的clientName局部变量
在这里插入图片描述

7.2 解决并发问题的方案

(1)根据实际应用需求,合理决定在Servlet中定义的变量的作用域,变量到底为实例变量,还是局部变量,是由实际应用需求决定的。
(2)对于多个线程同时访问共享数据而导致并发问题的场合,使用Java同步机制对线程进行同步。
synchronized{…}

8. 练习题

  1. 问题:HttpServletRequest对象是由谁创建的?
    选项:
    (A)由Servlet容器负责创建,对于每个要求访问HttpServlet的HTTP请求, Servlet容器都会创建一个HttpServletRequest对象
    (B)由JavaWeb应用的Servlet或JSP组件负责创建,当Servlet或JSP组件响应HTTP请求时,先创建HttpServletRequest对象
    答案: A
  2. 问题:HttpServlet从HTTP请求中获得请求参数,应该调用哪个方法?
    选项:
    (A)调用HttpServletRequest对象的getAttribute()方法
    (B)调用ServletContext对象的getAttribute()方法
    ©调用HttpServletRequest对象的getParameter()方法
    答案: C
  3. 问题:ServletContext对象是由谁创建的?
    选项:
    (A)由Servlet容器负责创建,对于每个HTTP请求, Servlet容器都会创建一个ServletContext对象
    (B)由JavaWeb应用本身负责为自己创建一个ServletContext对象
    ©由Servlet容器负责创建,对于每个JavaWeb应用,在启动时,Servlet容器都会创建一个ServletContext对象
    答案: C
  4. 问题:当Servlet容器销毁一个Servlet时,会销毁哪些对象?(多选)
    选项:
    (A)Servlet对象
    (B)与Servlet对象关联的ServletConfig对象
    (C)ServletContext对象
    (D)ServletRequest和ServletResponse对象
    答案: A,B
    (B)与Servlet对象关联的ServletConfig对象(当Servlet容器初始化一个Servlet时,会先创建一个包含了Servlet配置信息的ServletConfig对象,然后创建Servlet对象,Servlet与ServletConfig关联)
    (C)ServletContext对象(是与整个java web应用关联的,只有当终止应用才会销毁与它关联ServletContext对象)
    (D)ServletRequest和ServletResponse对象 (当响应结果到达客户端,这两个对象就会结束生命周期)
  5. 分析ServletRequest、ServletResponse、Servlet、ServletContext等对象的生命周期,何时被创建,何时被销毁。
    ServletRequest、ServletResponse:Servlet容器创建,当响应结果到达客户端,这两个对象就会结束生命周期
    Servlet、ServletConfig:Servlet容器创建,当Servlet容器初始化一个Servlet时,会先创建一个包含了Servlet配置信息的 ServletConfig对象,然后创建Servlet对象,Servlet与ServletConfig关联
    ServletContext:Servlet容器创建,与整个java web应用关联的,只有当终止应用才会销毁与它关联ServletContext对象
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值