zoukankan      html  css  js  c++  java
  • 第四部分_Servlet核心概念与原理

    Servlet API

    Servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必须实现这一接口。在Servlet接口中定义了五个方法,其中有三个方法代表了Servlet的生命周期:

    • init方法:负责初始化Servlet对象
    • service方法:负责响应客户的请求
    • destroy方法:当Servlet对象退出生命周期时,负责释放占用的资源。

    换句话说,Servlet的生命周期可分为三个阶段:初始化阶段;响应客户请求阶段;终止阶段。

    Servlet的初始化阶段:

      在下列时刻Servlet容器装载Servlet:

    • Servlet容器启动时自动装载某些Servlet
    • 在Servlet容器启动后,客户首次向Servlet发出请求
    • Servlet的类文件被更新后,重新装载Servlet

      Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期中,init方法只会被调用一次。

    Servlet的响应客户请求阶段:

      对于到达Servlet容器的客户请求,Servlet容器创建特定于这个请求的ServletRequest对象和ServletResponse对象,然后调用Servlet的service方法。service方法从ServletRequest对象获得客户请求信息、处理该请求,并通过ServletResponse对象向客户返回响应结果。

    Servlet的终止阶段:

      当Web应用被终止,或Servlet容器终止运行,或Servlet容器重新装载Servlet的新实例时,Servlet容器会先调用Servlet的destroy方法。在destroy方法中,可以释放Servlet所占用的资源。

    Servlet API类图如下:

    • 如果你的Servlet类扩展了HttpServlet类,你通常不必实现service方法,因为HttpServlet类已经实现了service方法,该方法的声明形式:protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    • 在HttpServlet的service方法中,首先从HttpServletRequest对象中获取HTTP请求方式的信息,然后再根据请求方式调用相应的方法。例如,如果请求方式为GET,那么调用doGet方法;如果请求方式为POST,那么调用doPost方法。

    Tomcat service源码伪码描述如下:

    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
    	String method = request.getMethod();
    	
    	if(method.equalsIgnoreCase("get"))
    	{
    		this.doGet(request, response);
    	}
    	else if(method.equalsIgnoreCase("post"))
    	{
    		this.doPost(request, response);
    	}
    	else if ...
    }

      ServletRequest接口

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

      ServletRequest接口的主要方法

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

      ServletResponse接口

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

      ServletResponse接口的主要方法

    • getOutputStream,返回可以向客户端发送二进制数据的输出流对象ServletOutputStream
    • getWriter,返回可以向客户端发送字符数据的PrintWrite对象
    • getCharacterEncoding,返回Servlet发送的相应数据的字节编码
    • getContentType,返回Servlet发送的响应数据的MIME类型
    • setContentType,设置Servlet发送的响应数据的MIME类型

    创建自己的HttpServlet

    下面给出一个实例:

    package com.test.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class HelloServlet extends HttpServlet // 第一步: 扩展 HttpServlet 抽象类。
    {
    	// 第二步:覆盖doGet()方法
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws IOException, ServletException
    	{
    		// 第三步:获取HTTP 请求中的参数信息
    		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";
    		String heading1 = "This is output from HelloServlet by doGet:";
    		// set content type.
    		response.setContentType("text/html;charset=GB2312");
    		// write html page.
    		out = response.getWriter();
    		out.print("<HTML><HEAD><TITLE>" + title + "</TITLE>");
    		out.print("</HEAD><BODY>");
    		out.print(heading1);
    		out.println("<h1><P> " + clientName + " : 您好</h1>");
    		out.print("</BODY></HTML>");
    		// close out.
    		out.close();
    	}
    }
    

    在浏览器地址栏中键入:http://localhost:8080/test/HelloServlet,返回信息为:

    This is output from HelloServlet by doGet:

    我的朋友 : 您好

     键入:http://localhost:8080/test/HelloServlet?clientName=Eric,返回信息为:

    This is output from HelloServlet by doGet:

    Eric : 您好

    Servlet对象何时被创建

    • 默认情况下,当Web客户第一次请求访问某个Servlet时,Web容器创建这个Servlet的实例
    • 如果设置了<servlet>元素的<load-on-startup>子元素,Servlet容器在启动Web应用时,将按照指定的顺序创建并初始化这个Servlet。如:
    <servlet>
    	<servlet-name>HelloServlet</servlet-name>
    	<servlet-class>com.test.HelloServlet</servlet-class>
    	<load-on-startup>2</load-on-startup>
    </servlet>
    

    ServletContext和Web应用的关系

    • 当Servlet容器启动Web应用时,并为每个Web应用创建唯一的ServletContext对象。你可以把ServletContext看成是一个Web应用的服务器端组件的共享内存。在ServletContext中可以存放共享数据,它提供了读取或设置共享数据的方法:-setAttribute(String name, Object object)把一个对象和一个属性名绑定,将这个对象存储在ServletContext中。-getAttribute(String name)根据给定的属性名返回绑定的对象。application就是ServletContext类型的。

    Web应用何时被启动

    • 当Servlet容器启动时,会启动所有的Web应用
    • 通过控制台启动Web应用

    创建CounterServlet

    package com.test.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    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;
    
    public class CounterServlet extends HttpServlet
    {
    	private static final String CONTENT_TYPE = "text/html";
    
    	public void init(ServletConfig config) throws ServletException // 仅仅执行一次
    	{
    		super.init(config);
    		System.out.println("init invoked"); 
    	}
    
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException
    	{
    		doPost(request, response);
    	}
    
    	public void doPost(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 = new Integer(0);
    			context.setAttribute("count", new Integer(0));
    		}
    
    		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 = new Integer(count.intValue() + 1);
    		// 将新的count属性存储到ServletContext中
    		context.setAttribute("count", count);
    	}
    
    	public void destroy()
    	{
    		
    	}
    }
    

    测试ConterServlet:

    • 通过如下URL访问CounterServlet:http://localhost:8080/test/CounterServlet,当你第一次访问该Servlet时,你会在浏览器上看到count的值为0
    • 刷新该页面,你会看到每刷新一次count值增加1,假定最后一次刷新后count值为5
    • 再打开一个新的浏览器,访问CounterServlet,此时count值为6
    • 重新启动Tomcat服务器,然后再访问CounterServlet,你会看到count值又被初始化为0

    Servlet的多线程同步问题(重要)

    package com.test.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class HelloServlet extends HttpServlet // 第一步: 扩展 HttpServlet 抽象类。
    {
    	String clientName = null;
    	// 第二步:覆盖doGet()方法
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws IOException, ServletException
    	{
    		// 第三步:获取HTTP 请求中的参数信息
    		clientName = request.getParameter("clientName");
    		if (clientName != null)
    		{
    			clientName = new String(clientName.getBytes("ISO-8859-1"), "GB2312");
    		}
    
    		else
    		{
    			clientName = "我的朋友";
    		}
    
    		try
    		{
    			Thread.sleep(10000); // 模拟后台复杂处理
    		}
    		catch(Exception e)
    		{
    			
    		}
    		// 第四步:生成 HTTP 响应结果。
    
    		PrintWriter out;
    		String title = "HelloServlet";
    		String heading1 = "This is output from HelloServlet by doGet:";
    		// set content type.
    		response.setContentType("text/html;charset=GB2312");
    		// write html page.
    		out = response.getWriter();
    		out.print("<HTML><HEAD><TITLE>" + title + "</TITLE>");
    		out.print("</HEAD><BODY>");
    		out.print(heading1);
    		out.println("<h1><P> " + clientName + " : 您好</h1>");
    		out.print("</BODY></HTML>");
    		// close out.
    		out.close();
    	}
    }
    

    在浏览器中同时访问:http://localhost:8080/test/HelloServlet?clientName=李四;http://localhost:8080/test/HelloServlet?clientName=王五,输出结果将被后者覆盖,这是因为Servlet是单例的多线程的,只要把clientName定义成局部变量就可以了。这里解决同步问题最好的方案是:去除实例变量,使用局部变量。

    Cookie

    • Cookie的英文原意是"点心",它是用户访问Web服务器时,服务器在用户硬盘上存放的信息,好像是服务器送给客户的"点心"。
    • 服务器可以根据Cookie来跟踪用户,这对于需要区别用户的场合(如电子商务)特别有用。
    • 一个Cookie包含一对Key/Value。下面的代码生成一个Cookie并把它写到用户的硬盘上:Cookie theCookie = new Cookie("cookieName", "cookieValue"); response.addCookie(theCookie);
    package com.test.servlet;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class CookieServlet extends HttpServlet
    {
    	private int count1 = 0;
    
    	private int count2 = 0;
    
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException
    	{
    		process(request, response);
    	}
    
    	public void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException
    	{
    		process(request, response);
    	}
    
    	public void process(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException
    	{
    
    		Cookie cookie = new Cookie("cookieName" + count1++, "cookieValue"
    				+ count2++);
    		cookie.setMaxAge(10); // cookie最大存活时间为10秒钟
    		response.addCookie(cookie);
    
    		Cookie[] cookies = request.getCookies(); // 首次刷新浏览器,Console不显示cookie,因为当前request对象不包括cookie信息
    
    		if (cookies == null)
    			return;
    		for (int i = 0; i < cookies.length; i++)
    		{
    
    			System.out.println("Cookie Name :" + cookies[i].getName());
    			System.out.println("Cookie Value :" + cookies[i].getValue());
    		}
    
    	}
    
    }
    

    下面是其对应的JSP版本:

    <%@ page import="javax.servlet.http.Cookie" %>
    <html>
    <head><title>jspCookie.jsp</title></head>
    <body>
         <%!
    		int count1 = 0;
    		int count2 = 0;
        %>
        <%
        		Cookie cookie = new Cookie("cookieName" + count1++, "cookieValue" + count2++);
        		cookie.setMaxAge(10);
                response.addCookie(cookie);
        %>
        <%
                Cookie[] cookies = request.getCookies();
                if(cookies==null)
                	return;
                for(int i = 0; i < cookies.length; i++)
                {
        %>
        <p>
                <b>Cookie name:</b>
                <%= cookies[i].getName() %>
    
                <b>Cookie value:</b>
                <%= cookies[i].getValue() %>
        </p>
        
        <%
                }
        %>
       
    
    </body></html>
    

    对于这个应用而言,Servlet代码更易读一些。

    比较Servlet和JSP

    • 有许多相似之处,都可以生成动态网页
    • JSP的优点是擅长于网页制作,生成动态页面,比较直观。JSP的缺点是不容易跟踪与排错
    • Servlet是纯Java语言,擅长于处理流程和业务逻辑。Servlet的缺点是生成动态网页不直观

    练习题

      

  • 相关阅读:
    python正则表达式re模块
    frp+proxifier实现内网socks5反向代理
    新版kali 添加root权限用户,和字体颜色解决办法
    通达OA 11.6 RCE 漏洞(含EXP,POC,环境)
    水泽信息收集自动化工具(安装教程)
    宝塔面板phpmyadmin未授权访问漏洞
    greenplum窗口函数使用浅析
    执果索因调整greenplum table dk值
    greenplum分区表查看所占空间大小
    greenplum查看表的数据分布情况来调整dk值
  • 原文地址:https://www.cnblogs.com/Code-Rush/p/4618999.html
Copyright © 2011-2022 走看看