Servlet

Servlet

概述

servlet是在服务器上运行的小程序。全称Java Servlet。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容,是为java web开发**服务的。* 。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。

三大组件之一

java web 是用java编写的程序在服务器·中运行

  • Servlet由来

做过BS项目的人都知道,浏览器能够根据HTML静态标记语言来显示各式各样的网页。但是如果我们需要在网页上完成一些业务逻辑:比如登陆验证。或者说网页显示的内容在服务器的数据库中。如果是这样,除了负责显示的HTML标记之外,必须还要有完成这些业务功能的代码存在。这种网页我们就叫做动态网页。 对于静态网页而言,服务器上存在的是一个个纯HTML文件。当客户端浏览器发出HTTP请求时,服务器可以根据请求的URL找到对应的HTML文件,并将HTML代码返回给客户端浏览器。

但是对于动态网页,服务器上除了找到需要显示的HTML标记外,还必须执行所需要的业务逻辑,然后将业务逻辑运算后的结果和需要显示的HTML标记一起生成新的HTML代码。最后将新的带有业务逻辑运算结果的HTML代码返回给客户端。

为了实现动态网页的目标,JavaServlet技术因应而生,它能够以一种可移植的方法来提供动态的、面向用户的内容。

  • tomcat和servlet的关系

Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.而Servlet是一种运行在支持Java语言的服务器上的组件. Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品.

从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。但是在上面这个图中,并不知道是如何转变的,只知道浏览器发送过来的请求也就是request,我们响应回去的就用response。忽略了其中的细节,现在就来探究一下。

①:Tomcat将http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象,所有的HTTP头数据读可以通过request对象调用对应的方法查询到。

②:Tomcat同时会要响应的信息封装为HttpServletResponse类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器

Java Servlet API 是Servlet容器(tomcat)和servlet之间的接口,它定义了serlvet的各种方法,还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。所以说我们在编写servlet时,需要实现Servlet接口,按照其规范进行操作。

Tomcat工作机制动画演示(图片来源网上)

CGI与Servlet对比

开始的时候,公共网关接口(CommonGateway Interface,CGI)脚本是生成动态内容的主要技术。虽然使用得非常广泛,但CGI脚本技术有很多的缺陷,这包括平台相关性和缺乏可扩展性。为了避免这些局限性,JavaServlet技术因应而生,它能够以一种可移植的方法来提供动态的、面向用户的内容。处理用户请求。

对比一:当用户浏览器发出一个Http/CGI的请求,或者说调用一个CGI程序的时候,服务器端就要新启用一个进程(而且是每次都要调用),调用CGI程序越多(特别是访问量高的时候),就要消耗系统越多的处理时间,只剩下越来越少的系统资源,对于用户来说,只能是漫长的等待服务器端的返回页面了,这对于电子商务激烈发展的今天来说,不能不说是一种技术上的遗憾。

而Servlet充分发挥了服务器端的资源并高效的利用。每次调用Servlet时并不是新启用一个进程,而是在一个Web服务器的进程中共享和分离线程,而线程最大的好处在于可以共享一个数据源,使系统资源被有效利用。故servlet不是线程安全的,单实例多线程的

​ 对比二:传统的CGI程序,不具备平台无关性特征,系统环境发生变化,CGI程序就要瘫痪,而Servlet具备Java的平台无关性,在系统开发过程中保持了系统的可扩展性、高效性。

对比三:传统技术中,一般大都为二层的系统架构,即Web服务器+数据库服务器,导致网站访问量大的时候,无法克服CGI程序与数据库建立连接时速度慢的瓶颈,从而死机、数据库死锁现象频繁发生。而我们的Servlet有连接池的概念,它可以利用多线程的优点,在系统缓存中事先建立好若干与数据库的连接,到时候若想和数据库打交道可以随时跟系统"要"一个连接即可,反应速度可想而知。

运行过程(原理)

⒈ 客户端发送请求至服务器端;

⒉服务器端根据web.xml文件中的Servlet相关配置信息,将客户端请求转发到相应的Servlet

⒊ Servlet引擎调用Service()方法,根据request对象中封装的用户请求与数据库进行交互,返回数据之后,Servlet会将返回的数据封装到response对象中;

⒋ Servlet生成响应内容并将其传给服务器。响应内容动态生成,通常取决于客户端的请求

⒌ 服务器将响应返回给客户端

Servlet接口定义了Servlet与servlet容器之间的契约。这个契约是:Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例。

用户请求致使Servlet容器调用Servlet的Service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如TomCat)封装好的,并不需要程序员去实现,程序员可以直接使用这两个对象。

ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,程序员只需直接操作ServletResponse对象就能把响应轻松的发回给用户。

对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。

生命周期

1) 加载和实例化;在第一次请求Servlet时,Servlet容器将会创建Servlet实例;

2) 初始化;Servlet容器加载完成Servlet之后,必须进行初始化,此时,init方法将被调用;

3) Servlet初始化之后,就处于响应请求的就绪状态,此时如有客户端请求发送,就会调用Servlet实例的service()方法,并且根据用户的请求方式,调用doPost或者doGet方法;

4) 最后,Servlet容器负责将Servlet实例进行销毁,调用destroy方法实现;

对于更多的客户端请求,Server创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用init()方法。

一般Servlet只初始化一次(只有一个对象),当Server不再需要Servlet时(一般当Server关闭时),Server调用Servlet的Destroy()方法。

  • Servlet如何同时处理多个请求?

Servlet采用多线程来处理多个请求的同时访问。Servlet容器通过线程池来管理维护服务请求。所谓线程池,相当于数据库连接池,实际上是等待执行代码的一组线程,叫做工作者线程。Servlet容器通过一个调度线程来管理工作者线程。

当容器收到一个Servlet的访问请求,调度者线程就从线程池中选出一个工作者线程,将用户请求传递给该线程,然后由该线程处理Servlet的service()方法; 当这个线程在执行的时候,容器收到一个新的请求,调度者线程再次从线程池中选出一个新的工作者线程;当容器同时收到对同一个Servlet的多个请求时,那么Servlet的service方法将在多线程中并发执行。

注:

  1. Servlet容器默认采用单实例多线程的方式来处理请求。这样减少了产生Servlet实例的开销,提升了对请求的响应时间;
  2. 对于Tomcat容器来讲,可以在其server.xml中通过中设置线程池中的线程数目
  • 如何开发线程安全的Servlet?

Servlet容器采用多线程来处理请求,提高性能的同时也造成了线程安全问题。要开发线程安全的Servlet应该从一下几个方面进行:

1. 变量的线程安全; 多线程并不共享局部变量,所以我们要尽可能的在Servlet中使用局部变量;
2. 代码块的线程安全; 使用同步块Synchronized,防止可能调用的代码块;但是要注意的是,要尽可能得缩小同步代码的方范围,不要在service方法和响应方法上直接使用同步,这会严重影响性能。
3. 属性的线程安全; ServletContext,HttpSession,ServletRequest对象中属性;
4. 使用同步集合; 使用Vector代替ArrayList,使用HashTable代替HashMap;
5. 不要在Servlet中创建自己的线程来完成某个功能; Servlet本身就是多线程的,如果再创建新的线程,将会导致线程执行复杂化,出现线程安全问题;
6. 在多个Servlet中,对外部对象,比如:文件;进行修改操作一定要加锁,做到互斥访问;

常用类

Servlet

public interface Servlet

  • 概述

定义所有servlet必须实现的方法。servlet是一个运行在Web服务器中的小型Java程序。servlet接收和响应来自Web客户机的请求,通常是通过超文本传输协议HTTP。

要实现这个接口,您可以编写一个扩展javax.servlet的通用servlet。GenericServlet或扩展javax.servlet.http.HttpServlet的HTTP servlet。

这个接口定义了初始化servlet、服务请求和从服务器删除servlet的方法。这些方法被称为生命周期方法,并按以下顺序调用:

构造servlet,然后用init方法初始化。客户端对服务方法的任何调用都会被处理。servlet从服务中取出,然后用destroy方法销毁,然后垃圾收集并最终完成。除了生命周期方法之外,该接口还提供了getServletConfig方法(servlet可以使用该方法获取任何启动信息)和getServletInfo方法(它允许servlet返回关于自身的基本信息,如作者、版本和版权)。

  • 常用方法

    • voiddestroy() 由servlet容器调用,以指示servlet正在退出服务。
      ServletConfiggetServletConfig() 返回一个ServletConfig对象,该对象包含这个servlet的初始化和启动参数。
      StringgetServletInfo()返回关于servlet的信息,例如作者、版本和版权。
      `voidinit(ServletConfig config) 由servlet容器调用,以指示servlet正在被放置到服务中。
      voidservice(ServletRequest req, ServletResponse res) 由servlet容器调用,允许servlet响应请求。

GenericServlet

public abstract class GenericServlet extends Object implements Servlet, ServletConfig, Serializable

  • 概述

定义一个通用的、独立于协议的servlet。要编写一个在Web上使用的HTTP servlet,应该扩展HttpServlet。GenericServlet实现了Servlet和ServletConfig接口。GenericServlet可以直接由servlet扩展,不过更常见的是扩展特定于协议的子类,如HttpServlet。GenericServlet使编写servlet变得更容易。

它提供了生命周期方法init和destroy以及ServletConfig接口中的方法的简单版本。GenericServlet还实现了在ServletContext接口中声明的日志方法。要编写一个通用servlet,您只需要重写抽象服务方法。

  • 常用方法

    • voiddestroy() 由servlet容器调用,以指示servlet正在退出服务。
      StringgetInitParameter(String name) 返回一个包含指定初始化参数值的字符串,如果参数不存在,则返回null。
      EnumerationgetInitParameterNames() 以字符串对象的枚举形式返回servlet初始化参数的名称,如果servlet没有初始化参数,则返回空枚举。
      ServletConfiggetServletConfig() 返回这个servlet的ServletConfig对象
      ServletContextgetServletContext() 返回对servlet运行所在的ServletContext的引用。
      StringgetServletInfo() 返回关于servlet的信息,例如作者、版本和版权。
      StringgetServletName() 返回这个servlet实例的名称。
      voidinit() 一个方便的方法,它可以被覆盖,因此不需要调用super.init(config)。
      voidinit(ServletConfig config) 由servlet容器调用,以指示servlet正在被放置到服务中。
      voidlog(String msg) 将指定的消息写入servlet日志文件,并以servlet的名称作为前缀。
      voidlog(String message, Throwable t) 为给定的可抛出异常向servlet日志文件写入解释性消息和堆栈跟踪,前面加上servlet的名称。
      abstract voidservice(javax.servlet.ServletRequest, javax.servlet.ServletResponse)) 由servlet容器调用,允许servlet响应请求。

在第一个带参数的init()方法中就已经把ServletConfig对象传入并且通过引用保存好了,完成了Servlet的初始化过程,那么为什么后面还要加上一个不带任何参数的init()方法呢?这不是多此一举吗?

当然不是多此一举了,存在必然有存在它的道理。我们知道,抽象类是无法直接产生实例的,需要另一个类去继承这个抽象类,那么就会发生方法覆盖的问题,如果在类中覆盖了GenericServlet抽象类的init()方法,那么程序员就必须手动的去维护ServletConfig对象了,还得调用super.init(servletConfig)方法去调用父类GenericServlet的初始化方法来保存ServletConfig对象,这样会给程序员带来很大的麻烦。GenericServlet提供的第二个不带参数的init( )方法,就是为了解决上述问题的。

这个不带参数的init()方法,是在ServletConfig对象被赋给ServletConfig引用后,由第一个带参数的init(ServletConfig servletconfig)方法调用的,那么这意味着,当程序员如果需要覆盖这个GenericServlet的初始化方法,则只需要覆盖那个不带参数的init( )方法就好了,此时,servletConfig对象仍然有GenericServlet保存着。

说了这么多,通过扩展GenericServlet抽象类,就不需要覆盖没有计划改变的方法。因此,代码将会变得更加的简洁,程序员的工作也会减少很多。

然而,虽然GenricServlet是对Servlet一个很好的加强,但是也不经常用,因为他不像HttpServlet那么高级。HttpServlet才是主角,在现实的应用程序中被广泛使用。那么我们接下来就看看传说中的HttpServlet到底厉害在哪里吧

HttpServlet

public abstract class HttpServlet extends GenericServlet

  • 概述

提供一个要子类化的抽象类,以创建适合网站的HTTP servlet。

HttpServlet的子类必须覆盖至少有一个方法,通常其中一个:

doGet, doPost如果servlet支持HTTP GET请求,HTTP POST请求doPut, doDelete HTTP PUT请求,HTTP DELETE请求初始化和销毁管理资源生活的servlet getServletInfo举行,该servlet使用提供的信息本身几乎没有理由覆盖的服务方法。服务通过将标准HTTP请求分派给每种HTTP请求类型的处理程序方法(上面列出的doXXX方法)来处理它们。同样,几乎没有理由重写doOptions和doTrace方法。

servlet通常运行在多线程服务器上,因此要注意servlet必须处理并发请求,并注意同步对共享资源的访问。共享资源包括内存中的数据(如实例或类变量)和外部对象(如文件、数据库连接和网络连接)。

  • 常用方法

    • protected voiddoDelete(HttpServletRequest req, HttpServletResponse resp)
      protected voiddoGet(HttpServletRequest req, HttpServletResponse resp)`
      protected voiddoHead(HttpServletRequest req, HttpServletResponse resp)
      protected voiddoOptions(HttpServletRequest req, HttpServletResponse resp)
      protected voiddoPost(HttpServletRequest req, HttpServletResponse resp)
      protected voiddoPut(HttpServletRequest req, HttpServletResponse resp)
      protected voiddoTrace(HttpServletRequest req, HttpServletResponse resp)
      protected longgetLastModified(HttpServletRequest req)
      protected voidservice(HttpServletRequest req, HttpServletResponse resp)
      voidservice(ServletRequest req, ServletResponse res)

总之,HttpServlet有两个特性是GenericServlet所不具备的:

1.不用覆盖service方法,而是覆盖doGet或者doPost方法。在少数情况,还会覆盖其他的5个方法。

2.使用的是HttpServletRequest和HttpServletResponse对象。

ServletConfig

public interface ServletConfig

  • 概述

servlet容器使用的一个servlet配置对象,用于在初始化期间将信息传递给servlet

  • 常用方法

StringgetInitParameter(字符串名称)
获取具有给定名称的初始化参数的值。

Enumeration getInitParameterNames ()
以字符串对象的枚举形式返回servlet初始化参数的名称,如果servlet没有初始化参数,则返回空枚举。

ServletContextgetServletContext ()
返回对调用者正在其中执行的ServletContext的引用。

StringgetServletName ()
返回这个servlet实例的名称。

ServletContext

public interface ServletContext

  • 概述

    ServletContext对象表示Servlet应用程序。每个Web应用程序都只有一个ServletContext对象。在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象。

那么为什么要存在一个ServletContext对象呢?存在肯定是有它的道理,因为有了ServletContext对象,就可以共享从应用程序中的所有资料处访问到的信息,并且可以动态注册Web对象。前者将对象保存在ServletContext中的一个内部Map中。保存在ServletContext中的对象被称作属性。

  • 常用方法

Object getAttribute(字符串名称) 返回具有给定名称的servlet容器属性,如果没有该名称的属性,则返回null。

Enumeration getAttributeNames () 返回一个枚举,其中包含这个ServletContext中可用的属性名。

ClassLoadergetClassLoader ()
获取由这个ServletContext表示的web应用程序的类装入器。

ServletContextgetContext(字符串uripath)
返回一个ServletContext对象,该对象对应于服务器上的指定URL。

StringgetContextPath ()
返回web应用程序的上下文路径。

StringgetMimeType(字符串文件)
返回指定文件的MIME类型,如果MIME类型未知,则返回null。

StringgetRealPath (String路径)
获取与给定虚拟路径对应的实路径。

StringgetServerInfo ()
返回正在运行servlet的servlet容器的名称和版本。

void removeAttribut(String name) 从这个ServletContext中移除具有给定名称的属性。

setAttribute(String name, Object object) 在这个ServletContext中将对象绑定到给定的属性名。

setInitParameter(String name, String value) 在这个ServletContext上使用给定的名称和值设置上下文初始化参数。

ServletRequest

public interface ServletRequest

  • 概述

定义一个对象来向servlet提供客户端请求信息。servlet容器创建一个ServletRequest对象,并将其作为参数传递给servlet的服务方法。
ServletRequest对象提供数据,包括参数名称和值、属性和输入流。

扩展ServletRequest的接口可以提供额外的特定于协议的数据(例如,HTTP数据是由HttpServletRequest提供的。

  • 常用方法

Object getAttribute(字符串名称)将指定属性的值作为对象返回,如果给定名称的属性不存在,则返回null。

Enumeration getAttributeNames() 返回一个枚举,包含此请求可用属性的名称。

String getCharacterEncoding()返回在请求体中使用的字符编码的名称。

int getContentLength()返回请求体的长度(以字节为单位),该长度由输入流提供,如果长度未知则返回-1,ir大于Integer.MAX_VALUE。

long getContentLengthLong()返回请求体的长度(以字节为单位),该长度由输入流提供,如果长度未知,则返回-1。

String getContentType()返回请求体的MIME类型,如果类型未知则返回null。

DispatcherTypeget DispatcherType()获取此请求的dispatcher类型。

ServletInputStream getInputStream()使用ServletInputStream作为二进制数据检索请求体。

String getParameter(字符串名称)以字符串形式返回请求参数的值,如果参数不存在,则返回null。

Map<String,String[]> getParameterMap()返回一个java.util。此请求的参数映射。

EnumerationgetParameterNames() 返回一个字符串对象的枚举, 其中包含这个请求中包含的参数的名称。返回一个字符串对象数组,该数组包含给定请求参数的所有值,如果参数不存在,则返回null。

String getProtocol()以protocol/majorVersion形式返回请求使用的协议的名称和版本。minorVersion,例如HTTP/1.1。

BufferedReader getReader()使用BufferedReader作为字符数据检索请求体。

void removeAttribute(字符串名称)
从此请求中删除一个属性。

void setAttribute(String name, Object o)

在此请求中存储一个属性。

void setCharacterEncoding env(字符串)
重写此请求正文中使用的字符编码的名称。

HttpServletRequest

public interface HttpServletRequest extends ServletRequest

  • 概述

扩展ServletRequest接口,为HTTP servlet提供请求信息。
servlet容器创建了一个HttpServletRequest对象,并将其作为参数传递给servlet的服务方法(doGet、doPost等)。

  • 常用方法

String getRequestedSessionId()返回客户端指定的会话ID。

String getRequestURI()返回该请求的URL的一部分,从协议名称一直到HTTP请求第一行的查询字符串。

StringBuffer getRequestURL()重新构造客户机用于发出请求的URL。

String getServletPath()返回请求URL中调用servlet的部分。

http sessiongetsession()返回与此请求相关联的当前会话,如果该请求没有会话,则创建一个会话。

http sessiongetsession (boolean create)返回与此请求相关联的当前HttpSession,如果没有当前会话且create为true,则返回一个新的会话。

String getContextPath()返回请求URI中指示请求上下文的部分。

getCookies()返回一个数组,该数组包含客户端发送的所有Cookie对象。

String getHeader(字符串名称)以字符串的形式返回指定请求头的值。

Enumeration getHeaderNames()返回这个请求包含的所有头名称的枚举。以字符串对象的枚举形式返回指定请求头的所有值。

String getMethod()返回发起请求的HTTP方法的名称,例如GET、POST或PUT。

因为Request代表请求,所以我们可以通过该对象分别获得HTTP请求的请求行,请求头和请求体。

Request乱码问题的解决方法

在前面我们讲过,****在service中使用的编码解码方式默认为:ISO-8859-1编码****,但此编码并不支持中文,因此会出现乱码问题,所以我们需要手动修改编码方式为UTF-8编码,才能解决中文乱码问题,下面是发生乱码的具体细节:

*request.setCharacterEncoding(“UTF-8”);*

ServletResponse

public interface ServletResponse

  • 概述

定义一个对象来帮助servlet向客户端发送响应。servlet容器创建一个ServletResponse对象,并将其作为参数传递给servlet的服务方法。要在MIME体响应中发送二进制数据,请使用getOutputStream()返回的ServletOutputStream。要发送字符数据,请使用getWriter()返回的PrintWriter对象。

定义一个对象来帮助servlet向客户端发送响应。servlet容器创建一个ServletResponse对象,并将其作为参数传递给servlet的服务方法。要在MIME体响应中发送二进制数据,请使用getOutputStream()返回的ServletOutputStream。要发送字符数据,请使用getWriter()返回的PrintWriter对象。例如,要混合二进制数据和文本数据来创建多部分响应,请使用ServletOutputStream并手动管理字符部分。MIME体响应的字符集可以显式地使用setCharacterEncoding(java.lang.String)和setContentType(java.lang.String)方法指定,或者隐式地使用setLocale(java.util.Locale)方法指定。显式规范优先于隐式规范。如果不指定字符集,则使用ISO-8859-1。setCharacterEncoding, setContentType,或setLocale方法必须在gettwriter之前和提交要使用的字符编码响应之前被调用。有关MIME的更多信息,请参阅Internet RFC(如RFC 2045)。像SMTP和HTTP这样的协议定义了MIME的配置文件,而且这些标准还在不断发展。 。 \

  • 常用方法

void flushBuffer()强制将缓冲区中的任何内容写入客户端。

int getBufferSize()返回响应使用的实际缓冲区大小。

String getCharacterEncoding()返回这个响应中发送的主体所使用的字符编码(MIME字符集)的名称。

String getContentType()返回此响应中发送的MIME主体所使用的内容类型。

ServletOutput StreamgetOutputStream()返回一个适合在响应中写入二进制数据的ServletOutputStream。

PrintWriter getWriter()返回一个PrintWriter对象,该对象可以向客户端发送字符文本。返回一个布尔值,指示响应是否已经提交。

void reset()清除缓冲区中存在的任何数据以及状态代码headers。

void resetBuffer()清除响应中底层缓冲区的内容,而不清除报头或状态代码。

void setBufferSize(int size)设置响应体的首选缓冲区大小。

void setCharacterEncoding(String charset)设置发送到客户端的响应的字符编码(MIME字符集),例如UTF-8。

voidsetContentLength(int len)设置HTTP servlet响应中内容体的长度,此方法设置HTTP content - length头。在HTTP servlet中设置响应内容体的长度,此方法设置HTTP content - length头。

void setContentType(String type)设置发送到客户端的响应的内容类型,如果响应还没有提交。

其中的getWriter方法,它返回了一个可以向客户端发送文本的的Java.io.PrintWriter对象。默认情况下,PrintWriter对象使用ISO-8859-1编码(该编码在输入中文时会发生乱码)。

还有一个方法也可以用来向浏览器发送数据,它就是getOutputStream,从名字就可以看出这是一个二进制流对象,因此这个方法是用来发送二进制数据的。

在发送任何HTML之前,应该先调用setContentType()方法,设置响应的内容类型,并将“text/html”作为一个参数传入,这是在告诉浏览器响应的内容类型为HTML,需要以HTML的方法解释响应内容而不是普通的文本,或者也可以加上“charset=UTF-8”改变响应的编码方式以防止发生中文乱码现象。

HttpServletResponse

public interface HttpServletResponse extends ServletResponse

  • 概述

在Service API中,定义了一个HttpServletResponse接口,它继承自ServletResponse接口,专门用来封装HTTP响应消息。 由于HTTP请求消息分为状态行,响应消息头,响应消息体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码,响应消息头,响应消息体的方法。

  • 常用方法

void addCookie(Cookie cookie)
将指定的cookie添加到响应中。

void addHeader(字符串名称,字符串值)
使用给定的名称和值添加响应头。

void adddintheader(字符串名称,int值)
添加具有给定名称和整数值的响应头。

boolean containsHeader(字符串名称)
返回一个布尔值,指示命名的响应头是否已经设置。

String getHeader(字符串名称)
获取具有给定名称的响应头的值。

Collection getHeaderNames ()
获取此响应的头的名称。

Collection getHeaders(字符串名称)
获取具有给定名称的响应头的值。

void setHeader(字符串名称,字符串值)
使用给定的名称和值设置响应头。

void setIntHeader(字符串名称,int值)
使用给定的名称和整数值设置响应头。

void setStatus (int sc)
设置此响应的状态代码。

void sendRedirect(字符串位置)
使用指定的重定向位置URL向客户端发送临时重定向响应,并清除缓冲区。

*注意:虽然response对象的getOutSream()和getWriter()方法都可以发送响应消息体,但是他们之间相互排斥,不可以同时使用,否则会发生异常。*

通过更改response的编码方式为UTF-8,任然无法解决乱码问题,因为发送端服务端虽然改变了编码方式为UTF-8,但是接收端浏览器端仍然使用GB2312编码方式解码,还是无法还原正常的中文,因此还需要告知浏览器端使用UTF-8编码去解码。

转发与重定向的区别

  • 转发

httpServletRequest.getRequestDispatcher(“资源路径”).forward(httpServletRequest, httpServletResponse);

  • 浏览器向服务器发送请求
  • 服务器接收请求并处理请求
  • 这时服务器发现有转发代码存在,
  • 直接跳转到新的资源(注意:这个过程是连续的,在这个过程中session可以跟随传递)
  • 网页显示返回结果,地址栏不会变化
  • 重定向

httpServletResponse.sendRedirect("/资源路径");

  • 览器向服务器发送请求
  • 服务器接收请求并处理请求
  • 这时服务器发现有重定向代码存在
  • 服务器会立即通知浏览器,告诉它,你去访问这个资源
  • 这时浏览器会对新资源重新发起访问(这个过程是断开的,中间不连续)
  • 地址栏产生相应的变化
  • 区别
  • 转发地址栏不会变化,重定向会变化。
  • 转发是一次请求,而重定向是两次。
  • 转发速度较快,重定向较慢(因为浏览器要重新发起请求)。
  • 由于重定向是重新对资源发起访问,而浏览器默认访问方式为get,所以对应的新响应要换成get,当然这是默认情况。注意一下即可。
  • 转发不会造成信息丢失,而重定向则会造成信息丢失。
  • 转发只能将请求转发给同一个WEB应用中的组件,重定向可以指向任何的资源,包括当前应用程序中的其他资源,同一个站点上的其他应用程序中的资源,其他站点的资源。
  • 形象比喻

转 发-----你找我借钱,我没钱,但是我可以帮你去找别人借钱。(再“借钱”这个动作由我去执行)

重定向-----你找我借钱,我没钱,但是我让你去找别人借钱。(再“借钱”这个动作由你自己去执行)

listener

概述

JavaWeb中的监听器是Servlet规范中定义的一种特殊类,它用于监听web应用程序中的ServletContext, HttpSession和 ServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。

在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为ServletContextHttpSessionServletRequest这三个域对象

分类

Servlet规范针对这三个对象上的操作,又把多种类型的监听器划分为三种类型:

  • 监听域对象自身的创建和销毁的事件监听器。

    1. ServletContextListener

    ServletContextListener接口的类都可以对ServletContext对象的创建和销毁进行监听。

    当ServletContext对象被创建时,激发contextInitialized (ServletContextEvent sce)方法。

    当ServletContext对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。

    1. HttpSessionListener

    HttpSessionListener 接口用于监听HttpSession对象的创建和销毁
      创建一个Session时,激发sessionCreated (HttpSessionEvent se) 方法
      销毁一个Session时,激发sessionDestroyed (HttpSessionEvent se) 方法。

    1. ServletRequestListener

    ServletRequestListener接口用于监听ServletRequest 对象的创建和销毁
      Request对象被创建时,监听器的requestInitialized(ServletRequestEvent sre)方法将会被调用
      Request对象被销毁时,监听器的requestDestroyed(ServletRequestEvent sre)方法将会被调用

  • 监听域对象中的属性的增加和删除的事件监听器。

    1. ServletContextAttributeListener

    监听ServletContext对象中的属性变更的监听器

    1. HttpSessionAttributeListener

    监听HttpSession对象中的属性变更的监听器

    1. ServletRequestAttributeListener

    监听ServletRequest对象中的属性变更的监听器

  • 监听绑定到HttpSession域中的某个对象的状态的事件监听器。

    1. HttpSessionBindingListener

    监听器作用在JavaBean上.JavaBean可以自己感知在session中状态.

    这类监听器不用配置.

    1. HttpSessionActivationListener

    监听HttpSession中的JavaBean的钝化和活化的状态

    活化,反序列化

    钝化,序列化

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("servlet 被创建");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("server 被销毁");
    }
}

Filter

public interface Filter

概述

过滤器是一个对象,它可以对对资源的请求(servlet或静态内容)执行过滤任务,也可以对来自资源的响应执行过滤任务,或者同时对两者执行过滤任务。

过滤器使用doFilter方法进行过滤。每个过滤器都可以访问FilterConfig对象,从该对象可以获得其初始化参数,以及对ServletContext的引用,例如,它可以使用ServletContext来加载过滤任务所需的资源。过滤器是在web应用程序的部署描述符中配置的

应用场景

自动登录
统一设置编码格式
访问权限控制
敏感字符过滤等

原理

  1. Filter接口中有一个doFilter方法,当开发人员编写好Filter类实现doFilter方法,并配置对哪个web资源进行拦截后,WEB服 务器每次在调用web资源的service方法之前  (服务器内部对资源的访问机制决定的),都会先调用一下filter的doFilter方法。

  2. 常用配置项

    以指定资源匹配。例如"/index.jsp"
    以目录匹配。例如"/servlet/"
    以后缀名匹配,例如"
    .jsp"
    通配符,拦截所有web资源。"/*"

  3. **initParams **
    配置初始化参数,跟Servlet配置一样

常用方法

void destroy ()
由web容器调用,以指示筛选器它将退出服务。

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

由于客户端请求链末端的资源,每次请求/响应对通过链传递时,容器都会调用过滤器的doFilter方法。

void init(FilterConfig filterConfig)
由web容器调用,以指示筛选器它正在被放置到服务中。

生命周期

  1. 在应用启动的时候就进行装载 Filter类(与Servlet的load-on-startup配置效果相同)。
  2. 容器创建好Filter对象实例后,调用init()方 法。接着被Web容器保存进应用级的集合容器中去了等待着,用户访问资源。
  3. 当用户访问的资源正好被Filter的url-pattern拦截 时,容器会取出Filter类调用doFilter方法,下次或多次访问被拦截的资源时,Web容器会直接取出指定Filter对象实例调用 doFilter方法(Filter对象常驻留Web容器了)。
  4. 当应用服务被停止或重新装载了,则会执行Filter的destroy方 法,Filter对象销毁。

注意:

  1. init方法与destroy方法只会直接一次。

  2. filter-mapping标签中servlet-name与url-pattern。
    Filter不仅可以通过url-pattern来指定拦截哪些url匹配的资源。而且还可以通过servlet-name来指定拦截哪个指定的servlet(专门为某个servlet服务了,servlet-name对应    Servlet的相关配置)。

  3. filter-mapping标签中dispatcher。
    指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多 个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。

    REQUEST:
    当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问或ERROR情况时,那么该过滤器就不会被调用。
    INCLUDE:
    如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
    FORWARD:
    如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
    ERROR:
    如若在A.jsp页面page指令中指定了error属性=examError.jsp,那么A.jsp中若出现了异常,会跳转到examError.jsp中处理。而在跳转到examError.jsp时,若过滤器配置了ERROR的dispather那么则会拦截,否则不会拦截。

常用类

FilterConfig

public interface FilterConfig

  • 概述

servlet容器使用的过滤器配置对象,在初始化期间将信息传递给过滤器。

  • 常用方法

String getFilterName() 返回在部署描述符中定义的该过滤器的过滤器名称。

String getInitParameter(String name) 返回一个包含指定初始化参数值的字符串,如果初始化参数不存在,则返回null。

Enumeration getInitParameterNames() 字符串对象的枚举形式返回筛选器初始化参数的名称,如果筛选器没有初始化参数,则返回空枚举。

ServletContext getServletContext() 返回对调用者正在执行的ServletContext的引用。

FilterChain

public interface FilterChain

  • 概述

FilterChain是servlet容器提供给开发人员的一个对象,它提供了一个查看经过过滤的资源请求的调用链的视图。过滤器使用FilterChain调用链中的下一个过滤器,或者如果调用过滤器是链中的最后一个过滤器,则使用FilterChain调用链末端的资源。

  • 常用方法

void doFilter(ServletRequest request, ServletResponse response)
导致调用链中的下一个过滤器,或者如果调用过滤器是链中的最后一个过滤器,则导致调用链末端的资源。

  • 注意

在web.xml中,filter执行顺序跟的顺序有关,先声明的先执行
使用注解配置的话,filter的执行顺序跟名称的字母顺序有关,例如AFilter会比BFilter先执行
如果既有在web.xml中声明的Filter,也有通过注解配置的Filter,那么会优先执行web.xml中配置的Filter

String getInitParameter(String name) 返回一个包含指定初始化参数值的字符串,如果初始化参数不存在,则返回null。

Enumeration getInitParameterNames() 字符串对象的枚举形式返回筛选器初始化参数的名称,如果筛选器没有初始化参数,则返回空枚举。

ServletContext getServletContext() 返回对调用者正在执行的ServletContext的引用。

FilterChain

public interface FilterChain

  • 概述

FilterChain是servlet容器提供给开发人员的一个对象,它提供了一个查看经过过滤的资源请求的调用链的视图。过滤器使用FilterChain调用链中的下一个过滤器,或者如果调用过滤器是链中的最后一个过滤器,则使用FilterChain调用链末端的资源。

  • 常用方法

void doFilter(ServletRequest request, ServletResponse response)
导致调用链中的下一个过滤器,或者如果调用过滤器是链中的最后一个过滤器,则导致调用链末端的资源。

  • 注意

在web.xml中,filter执行顺序跟的顺序有关,先声明的先执行
使用注解配置的话,filter的执行顺序跟名称的字母顺序有关,例如AFilter会比BFilter先执行
如果既有在web.xml中声明的Filter,也有通过注解配置的Filter,那么会优先执行web.xml中配置的Filter

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值