• 最近搞了一点后端 随便看了点 记录下。

servlet 的作用

    当使用交互式 Web 站点时,您所看到的所有内容都是在浏览器中显示的。在这些场景背后,有一个 Web 服务器接收会话中来自于您的请求,可能要切换到其他代码(可能位于其他服务器上)来处理该请求和访问数据,并生成在浏览器中显示的结果。

     servlet 就是用于该过程的网守(gatekeeper)。它驻留在 Web 服务器上,处理新来的请求和输出的响应。它与表示无关,实际上也不它应该与表示有关。您可以使用 servlet 编写一个流,将内容添加到 Web 页面中,但那通常也不是一个好办法,因为它有鼓励表示与业务逻辑的混合的倾向。

servlet 的替代品

是公共网关接口(CGI),但那样就要为每个请求派生不同的进程,因而会影响效率。还有专用服务器扩展,如 Netscape Server API(NSAPI),但那些都是完全专用的。在 Microsoft 的世界里,有活动服务器页面(ASP)标准。

servlet 为所有这些提供了一个替代品,并提供了一些好处:

它们与 Java 语言一样是与平台无关的。

它们允许您完全访问整个 Java 语言 API,包括数据访问库(如 JDBC)。

大多数情况下,它们内在地比 CGI 更高效,因为 servlet 为请求派生新的线程,而非不同的进程。

对于 servelet 有一个广泛的行业支持,包括用于最流行的 Web 和应用程序服务器的容器。

但什么是 servlet?

 Java servlet 大部分是为响应 Web 应用程序上下文中的 HTTP 请求而设计的。因此,javax.servlet 和javax.servlet.http 包中特定于 HTTP 的类是您应该关心的。

在创建一个 Java servlet 时,一般需要子类 HttpServlet。该类中的方法允许您访问请求和响应包装器(wrapper),您可以用这个包装器来处理请求和创建响应。

当然,HTTP 协议不是特定于 Java 的。它只是一个规范,定义服务请求和响应的大致式样。Java servlet 类将那些低层的结构包装在 Java 类中,这些类所包含的便利方法使其在 Java 语言环境中更易于处理。

正如您正使用的特定 servlet 容器的配置文件中所定义的,当用户通过 URL 发出一个请求时,这些 Java servlet 类就将之转换成一个  HttpServletRequest,并发送给 URL 所指向的目标。当服务器端完成其工作时,Java 运行时环境(Java Runtime Environment)就将结果包装在一个 HttpServletResponse 中,然后将原 HTTP 响应送回给发出该请求的客户机。在与 Web 应用程序进行交互时,通常会发出多个请求并获得多个响应。所有这些都是在一个会话语境中,Java 语言将之包装在一个HttpSession

容器(如 Tomcat)将为 servlet 管理运行时环境。您可以配置该容器,定制 J2EE 服务器的工作方式,而且您必须配置它,以便将 servlet 暴露给外部世界。正如我们将看到的,通过该容器中的各种配置文件,您在URL(由用户在浏览器中输入)与服务器端组件之间搭建了一座桥梁,这些组件将处理您需要该 URL 转换的请求。在运行应用程序时,该容器将加载并初始化 servlet,管理其生命周期。

当我们说 servlet 具有生命周期时,只是指在调用 servlet 时,事情是以一种可预见的方式发生的。换言之,在任何 servlet 上创建的方法总是按相同的次序被调用的。下面是一个典型场景:

servlet 容器所管理的 servlet。

         如果还没有创建该 servlet 的一个实例(一个应用程序只有一个 servlet 实例),那么该容器就加载该类,并将之实例化。

         该容器调用 servlet 上的 init()。

         该容器调用 servlet 上的 service(),并在包装的 HttpServletRequest 和 HttpServletResponse 中进行传递。

         该 servlet 通常访问请求中的元素,代表其他服务器端类来执行所请求的服务并访问诸如数据库之类的资源,然后使用该信息填充响应。

         如果有必要,在 servlet 的有用生命结束时,该容器会调用 servlet 上的 destroy() 来清除它。

如何“运行”servlet

运行 servlet 主要是指正确配置它,然后将浏览器指向正确的 URL。当然,servlet 中的代码正是发现有趣的业务逻辑的地方。您不必担心低层事件的进展,除非发生某种错误。

高效使用一个 servlet

就使用尽可能少的数目。实际上,我建议您只使用一个,直到肯定无法再这样下去。应该不需很多 servlet,您肯定无需为每种类型的请求提供一个 servlet。

耗费太长时间,在 servlet 中尽可能少花时间。

  servlet 不是为业务逻辑提供的场所。只有糟糕的 OOP 设计才那样。

将 servlet 考虑成以下两种事物之一:

         UI 后面的一个附加层,帮助服务器了解“事件”。

         服务器前面的一个附加层,允许您将浏览器用作 UI。

         无论将 servlet 视为哪一种事物,它都是您快速将问题分派到应用程序的其他部分、然后退出的一个地方。

servlet 世界中有许多人们声称,您不应重载 service()。并且说您应该重载 doGet() 和/或 doPost(),可以让它们相互调用,

使代码类似于下面所示:

public void doGet(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     statements
}
      
public void doPost(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException {
     doGet(request, response);
}

不要混合表示和业务逻辑

 对于简单的应用程序而言,生成复杂的 HTML 字符串来输出 JSP 页面的输出流很不错,但是创建功能更丰富的应用程序时,该方法要困难得多。

将表示放置在它所属的地方是一种比较明智的做法:在页面中。JSP 技术允许您完成该工作,但正如我前面所说的,它需要做大量工作才能使业务逻辑与表示分离。诸如 Velocity 之类的模板化引擎通常是更好的选择。无论您选择哪种方法,都要尽可能少地混合业务逻辑和表示。

很好地处理异常

许多打包的 Web 应用程序开发库,如 Struts,都附带了用来处理动态消息(包括错误)显示的内置框架。您可以使用这些功能。 

不要使用每种功能

  您是否需要使用您正采用的 Web 应用程序开发框架或库中的每项功能呢?很可能不需要,每一种功能都使用将使代码比您所需要的要复杂得多。实际上,除了可以工作的最简单框架,我建议您根本不要使用别的框架。

是过分设计。您应针对所需要的进行设计;设计通常会随着系统扩展而更改。在开发开始之前选择框架是无益的,如果该框架不支持或不允许完成您需要完成的一些工作,那会让您急得撞墙。

Servlet由来

浏览器能够根据HTML静态标记语言来显示各式各样的网页。但是如果我们需要在网页上完成一些业务逻辑:比如登陆验证。或者说网页显示的内容在服务器的数据库中。如果是这样,除了负责显示的HTML标记之外,必须还要有完成这些业务功能的代码存在。这种网页我们就叫做动态网页。

     对于静态网页而言,服务器上存在的是一个个纯HTML文件。当客户端浏览器发出HTTP请求时,服务器可以根据请求的URL找到对应的HTML文件,并将HTML代码返回给客户端浏览器。

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

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

简单来说:

servlet是在服务器上运行的小程序。Servlet的主要功能在于交互式地浏览和修改数据,生成动态Web内容,是为web开发服务的。

CGI与Servlet对比

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

 对比一:

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

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

对比二:

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

对比三:

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

Servlet的运行过程

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

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

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

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

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

Servlet生命周期

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的service()方法;

    当这个线程在执行的时候,容器收到一个新的请求,调度者线程再次从线程池中选出一个新的工作者线程;

那么Servlet的service方法将在多线程中并发执行。

注:

单实例多线程的方式来处理请求。这样减少了产生Servlet实例的开销,提升了对请求的响应时间;

server.xml中通过<Connector>中设置线程池中的线程数目。

如何开发线程安全的Servlet?

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

1.  变量的线程安全; 多线程并不共享局部变量,所以我们要尽可能的在Servlet中使用局部变量;

2.  代码块的线程安全; 使用同步块Synchronized,防止可能调用的代码块;但是要注意的是,要尽可能得缩小同步代码的方范围,不要在service方法和响应方法上直接使用同步,这会严重影响性能。

3.  属性的线程安全; ServletContext,HttpSession,ServletRequest对象中属性;

4.  使用同步集合; 使用Vector代替ArrayList,使用HashTable代替HashMap;

5.  不要在Servlet中创建自己的线程来完成某个功能; Servlet本身就是多线程的,如果再创建新的线程,将会导致线程执行复杂化,出现线程安全问题;

6.  在多个Servlet中,对外部对象,比如:文件;进行修改操作一定要加锁,做到互斥访问;

Servlet接口

GET/POST/HEAD/PUT/DELETE/TRACE

一般为 doGet()/doPost() .../getLastModified(request)返回文档的最后修改时间

Java Web目录结构

Web程序部署在Tomcat的/webapps下面,用 http://localhost:8080/web1

编写Servlet

javax.servlet.* javax.servlet.http.* 

一般直接继承HttpServlet,覆盖方法即可,一般只覆盖doGet(),doPost()

3.3.1 实现Servlet

编写Web Project,FirstServlet.java,覆盖常用三个方法

本书使用的是java EE5,新建项目时需要注意

新建web Project-> 新建servlet,

FirstServlet.java

/** 
      * The doGet method of the servlet. <br> 
      * 
      * This method is called when a form has its tag value method equals to get. 
      * 
      * @param request the request send by the client to the server 
      * @param response the response send by the server to the client 
      * @throws ServletException if an error occurred 
      * @throws IOException if an error occurred 
      * 
      * GET方式访问页面执行该函数 执行doGet之前会执行getLastModified,如果发现返回的数值与上次访问是相同, 
      * 则认为文档没有更新,浏览器采用缓存而不执行,如果返回 -1,则认为最新,总是执行该函数 
      */  
     public void doGet(HttpServletRequest request , HttpServletResponse response )  
               throws ServletException, IOException {  
  
           this.log("执行doGet方法..." );// 控制台日志输出信息  
           this.execute(request , response );// 处理doGet  
     }  
  
     /** 
      * The doPost method of the servlet. <br> 
      * 
      * This method is called when a form has its tag value method equals to post. 
      * 
      * @param request the request send by the client to the server 
      * @param response the response send by the server to the client 
      * @throws ServletException if an error occurred 
      * @throws IOException if an error occurred 
      * 
      *  以post方式访问页面时执行该函数,执行前不会调用getLastModified 
      */  
     public void doPost(HttpServletRequest request , HttpServletResponse response )  
               throws ServletException, IOException {  
  
           this.log("执行doPost方法" );  
           this.execute(request , response );  
     }  
  
     @Override  
     protected long getLastModified(HttpServletRequest req) {  
           // TODO Auto-generated method stub  
           this.log("执行 getLastModified 方法" );  
           return -1;  
     }  
       
     // 执行方法  
           private void execute(HttpServletRequest request,  
                   HttpServletResponse response) throws ServletException, IOException {  
               response.setCharacterEncoding( "UTF-8");  
               request.setCharacterEncoding( "UTF-8");  
  
              String requestURI = request.getRequestURI();  
              String method = request.getMethod();  
              String param = request.getParameter("param" );// 客户端提交的参数 param值  
  
               response.setContentType( "text/html");// 文档类型为HTML类型  
              PrintWriter out = response.getWriter();  
               out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");  
               out.println( "<HTML>");  
               out.println( " <HEAD><TITLE>A Servlet</TITLE></HEAD>");  
               out.println( " <BODY>");  
               out.println( " 以" + method + " 方式访问该页面。取到的param参数为:" + param + "<br/>" );  
  
               out.println( " <form action='"  
                        + requestURI  
                        + "' method='get'> <input type='text' name='param' value='param string'><input type='submit' value='以GET方式查询页面"  
                        + requestURI + "'> </form>" );  
               out.println( " <form action='"  
                        + requestURI  
                        + "' method='post'> <input type='text' name='param' value='param string'><input type='submit' value='以POST方式提交页面 "  
                        + requestURI + "'></form>" );  
                
               //客户端浏览器读取该文档的更新时间  
               out.println( " <script>document.write('本页面最后更新时间: ' + docment.lastModified); </script>" );  
               out.println( " </BODY>");  
               out.println( "</HTML>");  
               out.flush();  
               out.close();  
          }

3.3.2 配置<Servlet>

首先名称及类名:

<servlet-name> FirstServlet</servlet-name >  
<servlet-class >servlet.FirstServlet </servlet-class>  
  
<init-param>  
     <param-name >message </param-name>  
     <param-value >welcome to FisrtServlet </param-value>  
    </init-param >  
     <init-param >  
     <param-name >encoding </param-name>  
     <param-value >utf-8</ param-value>  
    </init-param >

配置一个初始化参数

< load-on-startup>1 </load-on-startup>

配置Servlet的加载方式。0和1,1启动加载,否则,Tomcat会在有人第一次请求加载

<servlet-mapping>  
    <servlet-name >FirstServlet </servlet-name>  
    <url-pattern >/ servlet/FirstServlet</url-pattern >  
  </servlet-mapping >

<url-pattern > 配置该Servlet的访问方式:这里即http://localhost:8080/servlet /servlet/FirstServlet

如果配置为/servlet/FirstServlet.* 使用http://localhost:8080/servlet /servlet/FirstServlet.XXX访问

通配符试了好像不行,还是支持多个映射,把需要的映射加进去吧

一个完整的servlet包括servlet类,<servlet>配置,<servlet-mapping>配置。利用myeclipse向导自动完成配置。

???部署到Tomcat6.0系统,然后启动,发现超时,可以使用:

部署的时候出现Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor?

我们找到当前工程的workplace目录,然后按下面的操作:

找到workspace\.metadata\.plugins\org.eclipse.wst.server.core\servers.xml文件。

把其中的start-timeout="45" 改为  start-timeout="100" 或者更长,根据不同同学的工程大小来设置。

最后重启eclipse就可以了。