JavaWeb基础----Servlet

Servelt 与 Servlet 容器

Servlet容器的概念
    Servlet容器为JavaWeb应用提供运行时环境,它负责管理Servlet和JSP的生命周期,以及管理它们的共享数据。
    Servlet容器也称为JavaWeb应用容器,或者Servlet/JSP容器。
    目前最流行的Servlet容器软件括:Tomcat,Resin,J2EE服务器(如Weblogic)中也提供了内置的Servlet容器

Servlet 简介
    Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中。Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet和客户的通信采用“请求/响应”的模式。
    Servlet可完成如下功能:
      - 创建并返回基于客户请求的动态HTML页面
      - 创建可嵌入到现有HTML 页面中的部分HTML 页面(HTML 片段)。
      - 与其它服务器资源(如数据库或基于Java的应用程序)进行通信。
Servlet容器响应客户请求的过程

web.xml的部分配置

    <!--配置当前WEB应用的初始化参数,这是一个全局的初始化参数(如果给多个servlet用就用这个),而<init-param>是局部的,只有它当前的servlet可以用-->
    <context-param>
        <param-name>driver</param-name>
        <param-value>com.mysql.jdbc.Driver</param-value>
    </context-param>
    <context-param>
        <param-name>jdbcUrl</param-name>
        <param-value>jdbc:mysql:///changwenWeb</param-value>
    </context-param>


    <!-- 为了能够通过浏览器的方式去访问这个类,需要配置和映射Servlet
         一个servlets可以有多个servlet-mapping -->
    <servlet>
        <!--Servlet注册的名字 -->
        <servlet-name>helloServlet</servlet-name>
        <!--Servlet的全类名 -->
        <servlet-class>com.changwen.javaweb.HelloServlet</servlet-class>

        <!--配置Servlet的局部!!初始化参数,需要在load-on-startup前面-->
        <init-param>
            <!-- 参数名 -->
            <param-name>username</param-name>
            <!-- 参数值 -->
            <param-value>changwen</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>112345</param-value>
        </init-param>

        <!--可以指定Servlet被创建的时机,越小越早被创建 ,也就说刚启动Tomcat,还没发送请求就会调用构造器和init访求 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <!--需要和某一个servlet节点的servlet-name子节点的文件节点一致 -->
        <servlet-name>helloServlet</servlet-name>
        <!-- 映射具体的访问路径: / 代表当前 WEB 应用的根目录.形如http://localhost:8080/ServletTest/hello-->
        <!-- html表单里的action <form action="loginServlet" method="post">-->
        <url-pattern>/hello</url-pattern>
        <!-- <url-pattern>*do</url-pattern> -->
        <!-- <url-pattern>/action/*</url-pattern> -->
    </servlet-mapping>

      在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。     同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。 

1.第一个HelloServlet

public class HelloServlet implements Servlet {
	// 生命周期的方法:1234
	// 1、构造器:只被调用一次,只有第一次请求Servlet时,创建Servlet的实例,调用构造器。
	public HelloServlet() {
		System.out.println("helloServlet's constructor...");
	}

	// 2、init方法:只被调用一次,在创建好实例后立即被调用,用于初始化当前Serlvet
	/**
	 * ServletConfig:是一个接口,封装了Serlvet的配置信息(web.xml),并且可以获取ServletContext对象
	 * 1)、配置Servlet的初始化参数,在web.xml配置<init-param> .
	 * 2). 获取初始化参数:
	 * getInitParameter(String name): 获取指定参数名的初始化参数 .
	 * getInitParameterNames():获取参数名组成的 Enumeration 对象.
	 */
//	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		System.out.println("init...");
		// 一获取初始化参数:
		String value = servletConfig.getInitParameter("username");
		System.out.println(value);  // changwen
		
		// 2、getInitParameterNames(): 获取参数名组成的 Enumeration 对象.
		Enumeration<String> names = servletConfig.getInitParameterNames();
		while (names.hasMoreElements()) {
			String name = names.nextElement();
			String value2 = servletConfig.getInitParameter(name);
			System.out.println("name: " + name + " value: " + value2);
			// name: username value: changwen
			// name: password value: 112345
		}

		String servletName = servletConfig.getServletName();
		// web.xml里的<servlet-name>helloServlet</servlet-name>  了解就行
		System.out.println("servletName-->" + servletName);  //servletName-->helloServlet
// ----------------------------------------------------------------
		// 获取ServletContext对象,是一个接口:
		//1、可以由ServletConfig获取。
		// 2、该对象代表当前 WEB 应用: 可以认为 SerlvetContext 是当前 WEB 应用的一个大管家.
		//   可以从中获取到当前 WEB 应用的各个方面的信息.

		// 设置初始化参数: 可以为所有的 Servlet 所获取, 而 Servlet 的初始化参数只用那个 Servlet 可以获取.
		// ①. 获取当前 WEB 应用的初始化参数
		ServletContext servletContext = servletConfig.getServletContext();
      String driver = servletContext.getInitParameter("driver");
      System.out.println("driver" + driver); //drivercom.mysql.jdbc.Driver

      Enumeration<String> names2 = servletContext.getInitParameterNames();
      while (names2.hasMoreElements()) {
          String name2 = names2.nextElement();
          String value2 = servletContext.getInitParameter(name2);
          //-->name2:driver value: com.mysql.jdbc.Driver
          //-->name2:jdbcUrl value: jdbc:mysql:///changwenWeb
          System.out.println("-->name2:" + name2+ " value: " +value2);
      }
		
      //②. 获取当前 WEB 应用的某一个文件在服务器上的绝对路径(获取虚拟路径所映射的本地路径), 而不是部署前的路径
      //目前这个note.txt是在WEB-INF下
      String realPath = servletContext.getRealPath("/note.txt");
		System.out.println(realPath);//D:\eclipse\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\ServletTest\note.txt

      //③. 获取当前 WEB 应用的名称:
      String contextPath = servletContext.getContextPath();
      System.out.println("contextPath: " + contextPath);//ServletBase

      //④. 获取当前 WEB 应用的某一个文件对应的输入流.
      //getResourceAsStream(String path): path 的 / 为当前 WEB 应用的根目录.
      ClassLoader classLoader = getClass().getClassLoader();
      InputStream is = classLoader.getResourceAsStream("src/main/resource/jdbc.properties");
      System.out.println("1. " + is); //1. java.io.BufferedInputStream@357eb194

      InputStream is2 = servletContext.getResourceAsStream("/WEB-INF/classes/jdbc.properties");
      System.out.println("2. "+is2);//2. java.io.FileInputStream@10bc86fc
	}

	// 3、service方法:被多次调用,每次请求都会调用service方法,实际用于响应请求的
	@Override
	public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
		System.out.println("service...");
	}

	// 4、destory访求:只被调用一次,在当前Servlet所在的web应用程序被卸载前调用,用于释放当前Servlet所占用的资源
	@Override
	public void destroy() {
		System.out.println("destory...");
	}

	@Override
	public ServletConfig getServletConfig() {
		System.out.println("getServletConfig...");
		return null;
	}
	@Override
	public String getServletInfo() {
		System.out.println("getSerlvetInfo...");
		return null;
	}

}

2.ServletConfig 接口

    Servlet在有些情况下可能需要访问Servlet容器或借助Servlet容器访问外部的资源,所以,Serlvet引擎需要将表示Servlet容器的对象传递给Servlet。另外,在web.xml文件中为某个Servlet设置的友好名称和初始化参数等信息也需要传递给该Servlet
    Servlet引擎将代表Servlet容器的对象(ServletContext)和Servlet的配置参数信息一并封装到一个称为ServletConfig的对象中,并在初始化Servlet实例对象时传递给该Servlet。ServletConfig接口则用于定义ServletConfig对象需要对外提供的方法,以便在Servlet程序中可以调用这些方法来获取有关信息。
    Servlet引擎调用Servlet的实例对象的init(ServletConfig config)方法将ServletConfig对象传递给Servlet。Servlet.getServletConfig()方法必须返回init(ServletConfig config)方法传递进来的这个ServletConfig对象的引用。
ServletConfig接口的方法
    getInitParameterNames
    getInitParameter
    getServletName
    getServletContext

2-1.ServletContext接口

    Servlet引擎为每个WEB应用程序都创建一个对应的ServletContext对象,ServletContext对象被包含在ServletConfig对象中,调用ServletConfig.getServletContext方法可以返回ServletContext对象的引用。
    由于一个WEB应用程序中的所有Servlet都共享同一个ServletContext对象,所以,ServletContext对象被称之为 application 对象(Web应用程序对象)。  
功能:
    获取WEB应用程序的初始化参数
    记录日志
    application域范围的属性
    访问资源文件
    获取虚拟路径所映射的本地路径
    WEB应用程序之间的访问
    ServletContext的其他方法

3.HTTP简介

    WEB浏览器与WEB服务器之间的一问一答的交互过程必须遵循一定的规则,这个规则就是HTTP协议。
    HTTP是 hypertext transfer protocol(超文本传输协议)的简写,它是 TCP/IP 协议集中的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程以及数据本身的格式。
    HTTP协议的版本 HTTP/1.0、HTTP/1.1、HTTP-NG
 HTTP 的会话方式

    浏览器与WEB服务器的连接过程是短暂的,每次连接只处理一个请求和响应。对每一个页面的访问,浏览器与WEB服务器都要建立一次单独的连接。
     浏览器到WEB服务器之间的所有通讯都是完全独立分开的请求和响应对。
http请求是无状态的,建立一次连接并疯狂的交换就结束。

ServletContext
    当 Servlet 容器(比如 Apache Tomcat)启动后,会部署和加载所有 web 应用。当web 应用被加载,Servlet 容器会创建一次 ServletContext,然后将其保存在服务器的内存中。web 应用的 web.xml 被解析,找到其中所有 servlet、filter 和 Listener 或 @WebServlet、@WebFilter 和 @WebListener 注解的内容,创建一次并保存到服务器的内存中。对于所有过滤器会立即调用 init()。当 Servlet 容器停止,将卸载所有 web 应用,调用所有初始化的 Servlet 和过滤器的 destroy() 方法,最后回收 ServletContext 和所有 Servlet、Filter 与 Listener 实例。
    当问题中的 Servlet 配置的 load-on-startup 或者 @WebServlet(loadOnStartup) 设置了一个大于 0 的值,则同样会在启动的时候立即调用 init() 方法。“load-on-startup”中的值表示那些 Servlet 会以相同顺序初始化。如果配置的值相同,会遵循 web.xml 中指定的顺序或 @WebServlet 类加载的顺序。另外,如果不设置 “load-on-startup” 值,init() 方法只在第一次 HTTP 请求命中问题中的 Servlet 时才被调用。

HttpServletRequest 与 HttpServletResponse
    Servlet 容器附加在一个 web 服务上,这个 web 服务会在某个端口号上监听 HTTP 请求,在开发环境中这个端口通常为 8080,生产环境中通常为 80。当客户端(web 浏览器)发送了一个 HTTP 请求,Servlet 容器会创建新的 HttpServletRequest 和 HttpServletResponse 对象,传递给已创建好并且请求的 URL 匹配 url-pattern 的 Filter 和 Servlet 实例中的方法,所有工作都在同一个线程中处理。
    request 对象可以访问所有该 HTTP 请求中的信息,例如 request header 和 request body。response 对象为你提供需要的控制和发送 HTTP 响应方法,例如设置 header 和 body(通常会带有 JSP 文件中的 HTML 内容)。提交并完成HTTP 响应后,将回收 request 和 response 对象。

HttpSession
    当用户第一次访问该 web 应用时,会通过 request.getSession() 第一次获得 HttpSession。之后 Servlet 容器将会创建 HttpSession,生成一个唯一的 ID(可以通过 session.getId() 获取)并储存在服务器内存中。然后 Servlet 容器在该次 HTTP 响应的 Set-Cookie 头部设置一个 Cookie,以 JSESSIONID 作为 Cookie 名字,那个唯一的 session ID 作为 Cookie 的值。
    按照 HTTP cookie 规则(正常 web 浏览器和 web 服务端必须遵循的标准),当 cookie 有效时,要求客户端(浏览器)在后续请求的 Cookie 头中返回这个 cookie。使用浏览器内置的 HTTP 流量监控器,你可以查看它们(在 Chrome、Firefox23+、IE9+ 中按 F12,然后查看 Net/Network 标签)。Servlet 容器将会确定每个进入的 HTTP 请求的 Cookie 头中是否存在名为JSESSIONID 的 cookie,然后用它的值(session ID)从服务端内存中找到关联的 HttpSession。
    你可以在 web.xml 中设置 session-timeout ,默认值为 30 分钟。超时到达之前 HttpSession 会一直存活。所以当客户端不再访问该 web 应用超过 30 分钟后,Servlet 容器就会回收这个 session。后续每个请求,即使指定 cookie 名称也不能再访问到相同的 session。Servlet 容器会创建一个新的 Cookie。
    另一方面,客户端上的 session cookie 有一个默认存活时间,该事件和该浏览器实例运行时间一样长。所以,当客户端关闭该浏览器实例(所有标签和窗口)后,这个 session 就会被客户端回收。新浏览器实例不再发送与该 session 关联的 cookie。一个新的 request.getSession() 将会返回新的 HttpSession 并设置一个拥有新 session ID 的 cookie。

概述
    ServletContext 与 web 应用存活时间一样长。它被所有 session 中的所有请求共享。
    只要客户端一直与相同浏览器实例的web应用交互并且没有超时,HttpSession就会存在。
    HttpServletRequest 和 HttpServletResponse 的存活时间为客户端发送完成到完整的响应(web 页面)到达的这段时间。不会被其他地方共享。
    所有 Servlet、Filter 和 Listener 对象在 web 应用运行时都是活跃的。它们被所有 session 中的请求共享。
    你设置在 HttpServletRequest、HttpServletResponse 和 HttpSession 中的所有属性在问题中的对象存活时都会一直保持存活。

线程安全
    即便如此,你最关心的可能是线程安全。你现在应该学习到 Servlet 和 filter 被所有请求共享。那是 Java 的一个优点,使得多个不同线程(读取 HTTP 请求)可以使用同一个实例。否则为每个请求重新创建线程的开销实在过于昂贵。
    但你应该也意识到永远不要将任何 request 或 session 域中的数据赋值给 servlet 或 filter 的实例变量。它将会被所有其他 session 中的所有请求共享。那是非线程安全的!下面的示例对这种情况进行了展示:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值