Servlet

Servlet
 
Java Servlet API的组成
      Java Servlet API由两个软件包组成:一个是对应HTTP的软件包,另一个是不对应HTTP的通用的软件包。这两个软件包的同时存在使得Java Servlet API能够适应将来的其他请求-响应的协议。
     
有关Java Servlets
      JavaTM servlets 是一个不受平台约束的Java小程序,它可以被用来通过多种方法扩充一个Web服务器的功能。你可以把Servlet理解成Server 上的 applets,它被编译成字节码,这样它就可以被动态地载入并用效地扩展主机的处理能力。
      Servlet与applets不同的地方是,它不运行在Web浏览器或其他图形化的用户界面上。Servlet通过servlet引擎运行在Web服务器中,以执行请求和响应,请求、响应的典型范例是HTTP协议。
      一 个客户端程序,可以是一个Web浏览器,或者是非其他的可以连接上Internet的程序,它会访问Web服务器并发出请求。这个请求被运行在Web服务 器上的Servlet引擎处理,并返回响应到Servlet。Servlet通过HTTP将这个响应转发到客户端。
在功能上,Servlet与CGI、NSAPI有点类似,但是,与他们不同的是:Servlet具有平台无关性。

      Java Servlet概论
      Servlet与其他普通的server扩展机制有以下进步:
      因为它采用了不同的进程处理模式,所以它比CGI更快。
      它使用了许多Web服务器都支持的标准的API。
      它继承了Java的所有优势,包括易升级以及平台无关性。
      它可以调用Java所提供的大量的API的功能模块。
      这份文档说明了Java Servlet API的类和接口的方法。有关更多的信息,请参看下面的API说明。

      Servlet的生命周期
      一个Java servlet具有一个生命周期,这个生命周期定义了一个Servlet如何被载入并被初始化,如何接收请求并作出对请求的响应,如何被从服务中清除。Servlet的生命周期被javax.servlet.Servlet这个接口所定义。
      所 有的Java Servlet都会直接地或间接地执行javax.servlet.Servlet接口,这样它才能在一个Servlet引擎中运行。 Servlet引擎是Web 服务器按照Java Servlet API定制的扩展。Servlet引擎提供网络服务,能够理解MIME请求,并提供一 个运行Servlet的容器。
      javax.servlet.Servlet接口定义了在Servlet的生命周期中特定时间以及特定顺序被调用的方法。

      Servlet的解析和载入
      Servlet引擎解析并载入一个Servlet,这个过程可以发生在引擎启动时,需要一个Servlet去响应请求时,以及在此之间的任何时候。
      Servlet引擎利用Java类载入工具载入一个Servlet,Servlet引擎可以从一个本地的文件系统、一个远程的文件系统以及网络载入Servlet。

      Servlet的初始化
      Servlet引擎载入Servlet后,Servlet引擎必须对Servlet进行初始化,在这一过程中,你可以读取一些固定存储的数据、初始化JDBC的连接以及建立与其他资源的连接。
      在初始化过程中,javax.servlet.Servlet接口的init()方法提供了Servlet的初始化信息。这样,Servlet可以对自己进行配置。
      init()方法获得了一个Servlet配置对象(ServletConfig)。这个对象在Servlet引擎中执行,并允许Servlet通过它获处相关参数。这个对象使得Servlet能够访问ServletContext对象。

      Servlet处理请求
      Servlet被初始化之后,它已经可以处理来自客户端的请求,每一个来自客户端的请求都被描述成一个ServletRequest对象,Servlet的响应被描述成一个ServletResponse对象。
      当客户端发出请求时,Servlet引擎传递给Servlet一个ServletRequest对象和一个ServletResponse对象,这两个对象作为参数传递到service()方法中。
      Servlet 也可以执行ServletRequest接口和ServletResponse接口。ServletRequest接口使得Servlet 有权使用客户 端发出的请求。Servlet可以通过ServletInputStream对象读取请求信息。
      ServletResponse接口允许Servlet建立响应头和状态代码。通过执行这个接口,Servlet有权使用ServletOutputStream类来向客户端返回数据。

      多线程和映射
      在多线程的环境下,Servlet必须能处理许多同时发生的请求。例外的情况是这个Servlet执行了SingleThreadModel接口,如果是那样的话,Servlet只能同时处理一个请求。
      Servlet依照Servlet引擎的映射来响应客户端的请求。一个映射对包括一个Servlet实例以及一个Servlet返回数据的URL,例如:HelloServlet with /hello/index.html。
      然 而,一个映射可能是由一个URL和许多Servlet实例组成,例如:一个分布式的Servlet引擎可能运行在不止一个的服务器中,这样的话,每一个服 务器中都可能有一个Servlet实例,以平衡进程的载入。作为一个Servlet的开发者,你不能假定一个Servlet只有一个实例。

      Servlet的卸载
      Servlet引擎并不必需保证一个Servlet在任何时候或在服务开启的任何时候都被载入。Servlet引擎可以自由的在任何时候使用或清除一个Servlet。因此,我们不能依赖一个类或实例来存储重要的信息。
      当Servlet引擎决定卸载一个Servlet时(例如,如果这个引擎被关闭或者需要让资源),这个引擎必须允许Servlet释放正在使用的资源并存储有关资料。为了完成以上工作,引擎会调用Servlet的destroy()方法。
      在 卸载一个Servlet之前,Servlet引擎必须等待所有的service()方法完成或超时结束(Servlet引擎会对超时作出定义)。当一 个 Servlet被卸载时,引擎将不能给Servlet发送任何请求。引擎必须释放Servlet并完成无用存储单元的收集

      Servlet映射技术
      作为一个Servlet引擎的开发者,你必须对于如何映射客户端的请求到Servlet有大量的适应性。这份说明文档不规定映射如何发生。但是,你必须能够自由地运用下面的所有技术:

      映射一个Servlet到一个URL
      例如,你可以指定一个特殊的Servlet它仅被来自/feedback/index.html的请求调用。

      映射一个Servlet到以一个指定的目录名开始的所有URL
      例如,你可以映射一个Servlet到/catalog,这样来自/catalog/、 /catalog/garden和/catalog /housewares/index.html的请求都会被映射到这个Servlet。但是来自 /catalogtwo 或/catalog.html 的请求没被映射。
 映射一个Servlet到所有以一个特定的字段结尾的所有URL
      例如,你可以映射一个来自于所有以in.thtml结尾的请求到一个特定的Servlet。

      映射一个Servlet到一个特殊的URL /servlet/servlet_name。
      例如,如果你建立了一个名叫listattributes的Servlet,你可以通过使用/servlet/listattributes来访问这个Servlet。

      通过类名调用Servlet
      例如,如果Servlet引擎接收了来自/servlet/com.foo.servlet.MailServlet的请求,Servlet引擎会载入这个 com.foo.servlet.MailServlet类,建立实例,并通过这个Servlet来处理请求。

      Servlet环境
      ServletContext 接口定义了一个Servlet环境对象,这个对象定义了一个在Servlet引擎上的Servlet的视图。通过使用这个对象,Servlet可以记录事 件、得到资源并得到来自Servlet引擎的类(例如RequestDispatcher对象)。一个Servlet只能运行在一个Servlet环境 中,但是不同的Servlet可以在Servlet引擎上有不同的视图。
      如果Servlet引擎支持虚拟主机,每个虚拟主机有一个Servlet环境。一个Servlet环境不能在虚拟主机之间共享。
      Servlet引擎能够允许一个Servlet环境有它自己的活动范围。
      例如,一个Servlet环境是属于bank应用的,它将被映射到/bank目录下。在这种情况下,一个对getContext方法的调用会返回/bank的Servlet环境。

      HTTP会话
      HTTP是一个没有状态的协议。要建立一个有效的Web服务应用,你必须能够识别一个连续的来自远端的客户机的唯一的请求。随着时间的过去,发展了许多会话跟踪的技术,但是使用起来都比较麻烦。
      Java Servlet API提供了一个简单的接口,通过这个接口,Servlet引擎可以有效地跟踪用户的会话。

      建立Session
      因为HTTP是一个请求-响应协议,一个会话在客户机加入之前会被认为是一个新的会话。加入的意思是返回会话跟踪信息到服务器中,指出会话已被建立。在客户端加入之前,我们不能判断下一个客户端请求是目前会话的一部分。
      在下面的情况下,Session会被认为是新的Session。
      客户端的Session在此之前还不知道
      客户端选择不加入Session,例如,如果客户端拒绝接收来自服务器的cookie
作 为一个Servlet的开发者,你必须决定你的Web应用是否处理客户机不加入或不能加入Session。服务器会在Web服务器或Servlet规定的 时间内维持一个Session对象。当Session终止时,服务器会释放Session对象以及所有绑定在Session上的对象。

绑定对象到Session中
      如果有助于你处理应用的数据需求,你也许需要绑定对象到Session中,你可以通过一个唯一的名字绑定任何的对象到Session中,这时,你需要使 用 HttpSession对象。任何绑定到Session上的对象都可以被处理同一会话的Servlet调用。
     有些对象可能需要你 知道什么时候会被放置到Session中或从Session中移开。你可以通过使用 HttpSessionBindingListener接口获得这些 信息。当你的应用存储数据到Session中,或从Session中清除数据, Servlet都会通过 HttpSessionBindingListener检杳什么类被绑定或被取消绑定。这个接口的方法会通报被绑定或被取消绑定的对象。

API对象的说明
      这一部分包含了对Java Servlet API的全部类和接口的详细说明。这个说明与Javadoc API差不多,但是这份文档提供了更多的信息。
API包含了两个软件包,十二个接口和九个类。
软件包:javax.servlet
所包含的接口:RequestDispatcher;Servlet;ServletConfig;ServletContext;ServletRequest;ServletResponse;SingleThreadModel。
所包含的类:GenericServlet;ServletInputStream;ServletOutputStream;ServletException;UnavailableException。
Servlet简介
Servlet是SUN公司提供的一门用于开发动态web资源的技术。
SUN公司在其API中提供了一个servlet接口,用户若想开发一个动态web ( 即由java程序向浏览器输出数据) 资源 ,需要完成以下2个步骤:
编写一个java类,实现servlet接口。
把开发好的java类部署到web服务器中。

Servlet的运行过程
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
1  web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。
2  装载并创建该Servlet的一个实例对象。
3  调用Servlet实例对象的init()方法。
4  创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
5  WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

Servlet接口实现类
Servlet接口SUN公司定义了两个默认实现类,分别为: GenericServlet ,HttpServlet.
HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为Get请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法,因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。


Servlet的一些细节(servlet的映射与注册 )
1 由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
<servlet>元素用于注册Servlet,        
        它包含有两个主要的子元素:
 <servlet-name> 用于设置Servlet的注册名称
<servlet-class> 用于设置Servlet的完整类名
3 一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,    
        它包含有两个子元素:
<servlet-name> 用于指定Servlet的注册名称
<url-pattern> 用于指定Servlet的对外访问路径
Servlet的一些细节(二)
1 同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
2 在Servlet映射到的RUL中也可以使用*通配符,但是只能有两种固定的格式:
一种格式是:   *.扩展名
另一种格式是:   /*
3 对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
问题:
当请求URL为“/abc/a.html”时,“/abc/*”和“/*”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1
当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应
Servlet引擎将调用Servlet3
当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet1
当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet2
当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
Servlet引擎将调用Servlet2
Servlet的一些细节(servlet的生命周期)
1 Servlet是一个供其他java程序(Servlet引擎)调用的java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
2 针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
3 在Servlet的整个生命周期内,Servlet的init方法只被调用一次,而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doxxx方法。
Servlet的一些细节(四)
1 如果在<servlet>元素中配置了一个<load-on-startup>元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
用途:用于在WEB应用启动时就加载数据库。
                为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。
Servlet的一些细节( 缺省Servlet 
1 如果某个Servlet的映射路径仅仅为一个正斜杠</>,那么这个Servlet就成为当前web应用程序的缺省Servlet.
2 凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。
        用途:
3 在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet.
当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet.
Servlet的一些细节(线程安全
1 当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。
2 如果某个servlet实现了SingleThreadModel接口,那么servlet引擎将以单线程模式来调用其service方法。
3 SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
4 对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
5 实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的).

ServletConfig对象——配置初始化参数并获取初始化参数的信息
在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而程序员通过调用this.getServletConfig().getInitParameter()方法就可以得到当前servlet的初始化参数信息。
在实际开发中,有一些数据不适合在servlet程序中写死,这类数据就可以通过配置web.xml的方式配给servlet,例如:servlet采用哪个编码表,servlet连接哪个数据库, servlet读取哪个配置文件(可 查看struts案例的web.xml文件 )。
servlet初始化参数
<init-param>
<param-name>data1 </param-name>
<param-value>xxxxx </param-value>
</init-param>
<init-param>
<param-name>data2 </param-name>
<param-value>yyyyy </param-value>
</init-param>
字符集编码信息
<init-param>
<param-name>charset </param-name>
<param-value>UTF-8 </param-value>
</init-param>
数据库连接信息
<init-param>
<param-name>url </param-name>
<param-value>jdbc:mysql://localhost:3306/test </param-value>
</init-param>
<init-param>
<param-name>usename </param-name>
<param-value>root </param-value>
</init-param>
<init-param>
<param-name>password </param-name>
<param-value>root </param-value>
</init-param>
配置文件信息
<init-param>
<param-name>config </param-name>
<param-value>/struts-config.xml </param-value>
</init-param>
  得到指定的参数信息
 String value = this.getServletConfig().getInitParameter("  charset  ");
 System.out.println(value);//显示 UTF-8
 
 
  得到所有的参数信息
 Enumeration e = this.getServletConfig().getInitParameterNames();
 while(e.hasMoreElements()){
        String name = (String) e.nextElement();
        String value = this.getServletConfig().getInitParmeter(name);
        System.out.println(name + "=" + value);  
 }
   
ServletContext对象
      ServletContext的生命周期
web容器在启动时,它会为每个web应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
        当WEB容器停止,或删除某个WEB应用,ServletContext将被销毁。
        
        ServletContext对象用于管理WEB资源,它提供了一些管理WEB资源的全局性方法,比如实现数据共享,实现实现WEB资源的数据转发,获取WEB应用的名称,获取当前Servlet的版本号,获取WEB应用的路径。

2 ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过 ServletConfig.getServletContext方法获得ServletContext对象。
        获取servletContext对象方法一:
        ServletContext context = this.getServletConfig().getServletContext();
        获取servletContext对象方法二:
        context = this.getServletContext();

3      由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,所以多个Servlet通过ServletContext对象实现数据共享。
        ServletContext对象通常也被称之为context对象。(WEB开发中另三个域对象分别是,request,session,page)
           ServletContext  域:首先这是一个容器,其次此容器的作用范围是应用程序范围
   实现数据共享
Servlet1       String data = "aaa" ;
                  this.getSevletContext().setAttribute("data",data);
Servlet2      String value =  this.getSevletContext().getAttribute("data");
                  System.out.println(value);//显示aaa
          
4     当一个项目中有多个servlet需要配置相同功能(如有多个servlet需要连接数据库,但是需要连接的数据库不止一种)的初始化参数时, 使用<context-param>标签为整个WEB应用配置初始化参数。
   获取整个WEB应用的初始化参数 
context初始化参数
<context-param>
<param-name>data </param-name>
<param-value>xxxx </param-value>
</context-param>
  得到指定的参数信息
 String value = this.g etSevletContext ().getInitParameter("  data ");
 System.out.println(value);//显示 xxxx
 
  得到所有的参数信息
 Enumeration e = this.g etSevletContext () .getInitParameterNames();
 while(e.hasMoreElements()){
        String name = (String) e.nextElement();
        String value = this.getServlet Context ().getInitParmeter(name);
        System.out.println(name + "=" + value);  
 }
通过serletcontext实现请求转发
Servlet
  servlet产生的数据
 String data = "aaaaaa";
将数据封装到servletContext域对象中
 this.getServletContext().setAttribute("data",data);
在实际开发中通常不会用context域封装数据,而采用request域封装数据
因为context域封装的数据被整个WEB应用所共享,会产生线程安全问题
调用getRequestDispatcher() 取转发对象
 ResquestDispatcher rd = this.getServletContext(). getRequestDispatcher("/1.jsp");
调用forward()方法将数据转发给JSP显示
 rd. forward(request,response);
JSP
  <font color="red">
   <%
            JSP调用aplictation对象的getAttribute()方法获取数据
           在JSP中aplictation就是指ServletContext
           String data = (String) applictation. getAttribute("data");
           out.write(data); 
   %>
 </font>
JSP显示页面
  aaaaaa

利用ServletContext对象读取资源文件
  配置文件的两种类型:
        properties文件:  存储的数据是无关系的
        xml文件:           存储的数据是有关系的
db.properties文件
 url=jdbc:mysql://localhost:3306/test
 username=root
 password=root
    通过ServletContext对象getResourceAsStream()方法获取资源文件的流对象  
  InputStream in =                                                                                                        
     this.getServletContext(). getResourceAsStream("/WEB-INF/classes/db.properties");
如果资源文件是proerties文件,可以通过Properties对象的load()方法从流中读 取数据,Properties对象中使用Map集合保存数据。
  Properties props = new   Properties();
  props. load(in);
    调用Properties对象的getProperty()方法从Map集合中获取数据
  String url = props. getProperty("url");
  String username = props. getProperty("usename");
  String password = props. getProperty("password");
   通过ServletContext对象getRealPath()方法获取资源的绝对路径
  String path = 
                  this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");
   再通过传统方式获取 资源文件的流对象
  FileInputStream in = new FileInputStream(path);

  Properties props = new  Properties();
  props. load(in);
   调用Properties对象的getProperty()方法从Map集合中获取数据
  String url = props. getProperty("url");
  String username = props. getProperty("usename");
  String password = props. getProperty("password");
    通过此种方式不但可以获取资源的数据,也可以获取资源的名称
  String filename = path.substring(path.lastIndexOf("\\")+1);
如果读取资源文件的程序不是servlet的话,只能通过类装载器读取
   servlet调用其它程序,在其它程序中如何读取资源文件(通过类装载器)
通过类装载器读取的文件不能太大,否则会造成内存溢出
servlet程序
  UserDao dao = new UserDao(); 
  dao.update();
通过类装载器直接读取,但此方式只能读取第一次被加载的数据,不能读取 更新后的数据
  InputStream in = 
                     UserDao. class. getClassLoader(). getResourceAsStream(" db.properties"
   Properties props = new  Properties ();
   props. load (in);
  String url = props. getProperty ("url");
通过类装载器获取资源文件的路径,再通过传统方式获取资源文件的数据,此种方式可以读取更新后的数据
 String path 
                 = UserDao.class.getClassLoader().getResource("db.properties").getPath();
 FileInputStream in = new FileInputStream(path);
 Properties props = new  Properties ();
 props. load (in);
 String url = props. getProperty ("url");

Response对象
向客户端输出中文数据 
通过OutputStream输出
  String data = "中国";
  OutputStream out = response.getOutputStream();
    因为此处设置采用UTF-8编码写入到response中,而浏览器默认采用本地编码(GB2312),所以浏览器将显示乱码
  out.write(data.getBytes("UTF-8"));
    可以通过respons的setHeader()方法控制浏览器以指定编码显示解决乱码问题
  response.setHeader("Content-type","text/html;charset=UTF-8");
   也可以通过HTML的<meta>标签模拟一个http响应头,来控制浏览器的显示
out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>".getBytes());
   如果要输出数字,必须要将数字转换成字符串,否则将出现乱码
  out.write((1+"").getBytes());
通过PrintWriter输出
设置response使用的码表以控制response以何种编码形式向浏览器发送数据
如果不指定response使用的码表,浏览器将显示乱码,因为response默认使用IOS8859码表,而ISO8859码表中并没有“中国”。
  response.setCharacterEncoding("UTF-8");
指定浏览器以何种编码表处理服务器发送的数据
  response.setHeader("content-type","text/html;charset=UTF-8");
通过response的setContentType()方法可以将response和浏览器的码表共同指定
  response.setContentType("text/html;charset="UTF-8"");
   String data = "中国";
  PrintWriter out = response.getWriter();
  out.writer(data);
通过rseponse实现文件下载
如果下载文件是非中文文件,则采用下面的方式下载
  String path = this.getServletContext().getRealPath("/download/1.jpg");
  String filename = path.substring(path.lastIndexOf("\\"+1));
  response.setHeader(" content-disposition" ," attachment;filename=" + filename);
如果下载文件是中文文件,则文件名需要经过url编码
  String path = this.getServletContext().getRealPath("/download/日本妞.jpg");
  String filename = path.substring(path.lastIndexOf("\\"+1));
  response.setHeader("content-disposition","attachment;filename=" + 
                                                                   URLEncoder.encode(filename,"UTF-8"));
  InputStream in = null;
  OutputStream out = null;
  try{
         in = new FileInputStream(path);
         int len = 0;
        byte[] buffer = new byte[1024];
        out = response.getOutputStream();
        while((len= in.read(buffer))>0){
               out.write(buffer,0,len);
        }
    } finally{
               if(in!=null){
                   try(){
                             in.close();
                           }  catch(Exception e){
                              e.printStackTrace();
                           }
                  } if(out!=null){
                       try{
                               out.close();
                           } catch(Exception e ){
                                e.printStackTrace();
                           }
                 }  
          }
通过rseponse实现请求重定向
请求重定向指:一个WEB资源收到客户端请求后,通知客户端去访问另外一个WEB资源,这称之为请求重定向。应用场景-用户登录。
重定向的特点
1  浏览器会向服务器发送两次请求,意味着就有2个request和respons对象
2  用重定向技术,浏览器地址栏发生变化
转发的特点
1  浏览器会向服务器发送一次请求,意味着只有1个request和respons对象
2  用转发技术,浏览器地址栏不会发生变化
用户登录和显示购物车时,通常会用到重定向技术。
通过发送302状态码和location头方法实现重定向
response.setStatus(302);
response.setHeader("location","URL")
通过调用response的sendRedirect()方法实现重定向
response.sendRedirect("URL");
输出随机认证码图片
        private static final int WIDTH = 120;
private static final int HEIGHT = 30;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
BufferedImage image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
//设置背景色
setBackGround(g);
//设置边框
setBorder(g);
//设置干扰线
drawRandomLine(g);
//设置随机数
drawRandomNum((Graphics2D)g);
//控制浏览器以什么方式处理数据
response.setContentType("image/jpeg");
//控制浏览器不要缓存
response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("pragma", "no-cache");
//将图形写给浏览器
ImageIO.write(image,"jpg",response.getOutputStream());
}
private void drawRandomLine(Graphics g) {
g.setColor(Color.CYAN);
for(int i=0;i<4;i++){
//通过随机数发生器产生的数作为x和y的坐标
int x1 = new Random().nextInt(WIDTH);
int y1 = new Random().nextInt(HEIGHT);
int x2 = new Random().nextInt(WIDTH);
int y2 = new Random().nextInt(HEIGHT);
g.drawLine(x1, y1, x2, y2);
}
}
private void drawRandomNum(Graphics2D g) {
g.setColor(Color.BLACK);
g.setFont(new Font("宋体",Font.BOLD,20));
String base = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5726\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a";
    int x =8;
for(int i=0;i<6;i++){
String  ch = base.charAt(new Random().nextInt(base.length()))+"";
int degree = new Random().nextInt()%30;
g.rotate(degree*Math.PI/180,x,20);
g.drawString(ch, x, 20);
g.rotate(-degree*Math.PI/180,x,20);
x+=30;
}
}
private void setBorder(Graphics g) {
g.setColor(Color.BLUE);
g.drawRect(1, 1, WIDTH-2, HEIGHT-2);
}
private void setBackGround(Graphics g) {
g.setColor(Color.WHITE);
//填充矩形
g.fillRect(0, 0, WIDTH, HEIGHT);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
用refresh控制浏览器定时刷新
假设这是一个用于处理登录的servlet
假设程序运行到此,用户登录成功了
String message =                                                                                                           "<meta http-equlv='refresh' content='3;url='day06/index.jasp'>恭喜您,登录成功,页面将在3秒后跳转。"
this.getServletContext().setAttribute("message",message);
this.getServletContext().getRequestDispatcher("/message.jsp").forward(request,response);
通过expires控制浏览器缓存
response.setHeader("expires",System.currentTimeMillis()+1000*3600)
String data = "aaaaaaa";
response.getWriter().write(data);


response细节
getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的SevletOutputStream、PrintWriter对象。
etOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。如果同时调用,则会出现IllegalStateException: getOutputStream() has already been called for this response异常。
Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
Servlet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。
request对象
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP    请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。
URI:指资源的路径
URL:网络资源的路径

request常用方法
获取客户机信息
getRequestURL:         返回客户端发出请求时的完整URL。
getRequestURI:         返回请求行中的资源名部分。
getQueryString:         返回请求行中的参数部分。
getRemoteAddr:         返回请求的客户机的IP地址
getRemoteHost: 返回发出请求的客户机的完整主机名(需要在DNS上注册,否则返回IP地址 )
getRemotePort:         返回客户机所使用的网络端口号
getLocalAddr:         返回WEB服务器的IP地址
getLocalName:         返回WEB服务器的主机名
getMethod: 返回客户机的请求方式

会话
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
每个用户与服务器进行交互的过程中,各自有一些数据,程序要想办法保存每个用户的数据。
保存会话数据的两种技术
 Cookie
cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再次访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
Session
session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再访问服务器中的其它web资源时,其他web资源再从用户各自的session中取出数据为用户服务。 
Cookie类
public Cookie(Sttring name,String value)
添加Cookie
response接口中定义了addCookis方法,它用于在其响应头中增加一个相应的Set-Cookie头字段
获取Cookie
request接口中定义了getCookies方法,它用于获取客户端提交的Cookie.
Cookie类中的方法
setValue与 getValue 设置/获取Cookie的值
setMaxAge与getMaxAge 设置/获取Cooike的有效期 默认有效期是浏览器进程
setPath与getPath方法   设置Cookie的有效目录 默认有效目录是发出Cookie的servlet所在的目录
getName方法
Cookie细节
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(Name)和设置值(Value)
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie.
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
如果创建了一个Cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在硬盘上,则需要使用maxAge,并给出一个以秒为单位的时间,将最大时效设为0,则是命令浏览器删除该cookie.
注意:删除cookie时,path必须一致,否则不会删除。
Cookie案例:显示用户上次访问网站的时间
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("您上次访问时间是:");
//获取用户的时间Cookie
Cookie[] cookie = resquest.getCookies();
for(int i=0;cookies!=null&&i<cookies.length;i++){
    if(cookies[i].getName().equals(" lastAccessTime")){
        long cookieValue = long.parseLong(cookies[i].getValue());
        Date date = new Date(cookieValue);
        out.print(date.toLocalString());
    }
}
//给用户回送最新的访问时间
Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
cookie.setMaxAge(1*30*24*3600);
cookie.setPath("/day07");
response.addCookie(cookie);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值