servlet 订阅
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。 展开全文
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
信息
类    别
程序
平    台
Java Web Server
环    境
Java applet
中文名
小服务程序或服务连接器
外文名
Servlet
servlet历程
Servlet 是在服务器上运行的小程序。这个词是在 Java applet的环境中创造的,Java applet 是一种当作单独文件跟网页一起发送的小程序,它通常用于在客户端运行,结果得到为用户进行运算或者根据用户互作用定位图形等服务。服务器上需要一些程序,常常是根据用户输入访问数据库的程序。这些通常是使用公共网关接口(Common Gateway Interface,CGI)应用程序完成的。然而,在服务器上运行 Java,这种程序可使用 Java 编程语言实现。在通信量大的服务器上,JavaServlet 的优点在于它们的执行速度更快于 CGI 程序。各个用户请求被激活成单个程序中的一个线程,而无需创建单独的进程,这意味着服务器端处理请求的系统开销将明显降低。实现过程最早支持 Servlet 技术的是 JavaSoft 的 Java Web Server。此后,一些其它的基于 Java 的 Web Server 开始支持标准的 Servlet API。Servlet 的主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。这个过程为:Servlet 看起来像是通常的 Java 程序。Servlet 导入特定的属于 Java Servlet API 的包。因为是对象字节码,可动态地从网络加载,可以说 Servlet 对 Server 就如同 Applet对 Client 一样,但是,由于 Servlet 运行于 Server 中,它们并不需要一个图形用户界面。从这个角度讲,Servlet 也被称为 FacelessObject。一个 Servlet 就是 Java 编程语言中的一个类,它被用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序。虽然 Servlet 可以对任何类型的请求产生响应,但通常只用来扩展 Web 服务器的应用程序。
收起全文
精华内容
下载资源
问答
  • servlet
    千次阅读
    2020-07-09 09:54:52

    servlet 异常处理

    Today we will look into Servlet Exception and Error Handling. Sometime back I wrote a post about Exception Handling in Java but when it comes to web application, we need more than normal exception handling in java.

    今天,我们将研究Servlet异常和错误处理。 有时候我写了一篇有关Java异常处理的文章,但是当涉及到Web应用程序时,我们需要的不仅仅是Java中的异常处理。

    Servlet异常 (Servlet Exception)

    If you notice, doGet() and doPost() methods throw javax.servlet.ServletException and IOException, let’s see what happens when we throw these exception from our application. I will write a simple servlet that will throw the ServletException.

    如果您注意到,doGet()和doPost()方法会抛出javax.servlet.ServletExceptionIOException ,让我们看看从应用程序中抛出这些异常时会发生什么。 我将编写一个简单的Servlet,它将抛出ServletException。

    package com.journaldev.servlet.exception;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/MyExceptionServlet")
    public class MyExceptionServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		throw new ServletException("GET method is not supported.");
    	}
    
    }

    Now when we invoke this servlet through browser with GET method, we get response like below image.

    现在,当我们使用GET方法通过浏览器调用此servlet时,将得到如下图所示的响应。

    Since browser understand only HTML, when our application throw exception, servlet container processes the exception and generate a HTML response. This logic is specific to servlet container. I am using tomcat and getting this error page. If you will use some other servers like JBoss or Glassfish, you might get different error HTML response.

    由于浏览器仅了解HTML,因此当我们的应用程序引发异常时,servlet容器将处理该异常并生成HTML响应。 此逻辑特定于servlet容器。 我正在使用tomcat并获取此错误页面。 如果您将使用其他一些服务器,例如JBoss或Glassfish,则可能会收到不同的错误HTML响应。

    The problem with this response is that it’s of no value to user. Also it’s showing our application classes and server details to user that makes no sense to user and it’s not good from security point of view.

    此响应的问题在于它对用户没有任何价值。 它还向用户显示了我们的应用程序类和服务器详细信息,这对用户没有意义,并且从安全角度来看也不是一件好事。

    Servlet错误 (Servlet Error)

    I am sure you must have seen 404 error when you are trying to hit a URL that doesn’t exists. Let’s see how our servlet container responds to 404 error. If we send request for an invalid URL, we get response HTML like below image.

    我敢肯定,当您尝试访问不存在的URL时,您一定已经看到404错误。 让我们看看我们的servlet容器如何响应404错误。 如果我们发送无效URL的请求,则会得到响应HTML,如下图所示。

    Again it’s a generic HTML generated by server on our application behalf and hold little to no value to the user.

    同样,它是服务器代表我们的应用程序生成的通用HTML,对用户几乎没有价值。

    Servlet异常和错误处理 (Servlet Exception and Error Handling)

    Servlet API provides support for custom Exception and Error Handler servlets that we can configure in deployment descriptor. The whole purpose of these servlets are to handle the Exception or Error raised by application and send useful HTML response to user. We can provide link to application home page or some details to let user know what went wrong.

    Servlet API提供对自定义异常和错误处理程序Servlet的支持,我们可以在部署描述符中对其进行配置。 这些servlet的全部目的是处理应用程序引发的异常或错误,并将有用HTML响应发送给用户。 我们可以提供指向应用程序主页的链接或一些详细信息,以使用户知道出了什么问题。

    So first of all we need to create a custom Exception and Error Handler servlet. We can have multiple exception and error handler servlets for the application but for simplicity I will create a single servlet and use it for both exceptions and errors.

    因此,首先我们需要创建一个自定义的Exception and Error Handler servlet。 我们可以为应用程序提供多个异常和错误处理程序servlet,但为简单起见,我将创建一个servlet,并将其用于异常和错误。

    AppExceptionHandler.java

    AppExceptionHandler.java

    package com.journaldev.servlet.exception;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/AppExceptionHandler")
    public class AppExceptionHandler extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doGet(HttpServletRequest request,
    			HttpServletResponse response) throws ServletException, IOException {
    		processError(request, response);
    	}
    
    	protected void doPost(HttpServletRequest request,
    			HttpServletResponse response) throws ServletException, IOException {
    		processError(request, response);
    	}
    
    	private void processError(HttpServletRequest request,
    			HttpServletResponse response) throws IOException {
    		// Analyze the servlet exception
    		Throwable throwable = (Throwable) request
    				.getAttribute("javax.servlet.error.exception");
    		Integer statusCode = (Integer) request
    				.getAttribute("javax.servlet.error.status_code");
    		String servletName = (String) request
    				.getAttribute("javax.servlet.error.servlet_name");
    		if (servletName == null) {
    			servletName = "Unknown";
    		}
    		String requestUri = (String) request
    				.getAttribute("javax.servlet.error.request_uri");
    		if (requestUri == null) {
    			requestUri = "Unknown";
    		}
    		
    		// Set response content type
    	      response.setContentType("text/html");
    	 
    	      PrintWriter out = response.getWriter();
    	      out.write("<html><head><title>Exception/Error Details</title></head><body>");
    	      if(statusCode != 500){
    	    	  out.write("<h3>Error Details</h3>");
    	    	  out.write("<strong>Status Code</strong>:"+statusCode+"<br>");
    	    	  out.write("<strong>Requested URI</strong>:"+requestUri);
    	      }else{
    	    	  out.write("<h3>Exception Details</h3>");
    	    	  out.write("<ul><li>Servlet Name:"+servletName+"</li>");
    	    	  out.write("<li>Exception Name:"+throwable.getClass().getName()+"</li>");
    	    	  out.write("<li>Requested URI:"+requestUri+"</li>");
    	    	  out.write("<li>Exception Message:"+throwable.getMessage()+"</li>");
    	    	  out.write("</ul>");
    	      }
    	      
    	      out.write("<br><br>");
    	      out.write("<a href=\"index.html\">Home Page</a>");
    	      out.write("</body></html>");
    	}
    }

    Let’s see how we can configure it in deployment descriptor and then we will understand it’s implementation and how it works.

    让我们看看如何在部署描述符中配置它,然后我们将了解它的实现及其工作方式。

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
      <display-name>ServletExceptionHandling</display-name>
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
      </welcome-file-list>
      
      <error-page>
      	<error-code>404</error-code>
      	<location>/AppExceptionHandler</location>
      </error-page>
      
      <error-page>
      <exception-type>javax.servlet.ServletException</exception-type>
      <location>/AppExceptionHandler</location>
      </error-page>
    </web-app>

    As you can see, it’s very easy to specify Exception handler servlets for the application using error-page element. Each error-page element should have either error-code or exception-type element. We define the exception handler servlet in location element.

    如您所见,使用error-page元素为应用程序指定异常处理程序servlet非常容易。 每个错误页面元素应具有错误代码异常类型元素。 我们在location元素中定义异常处理程序servlet。

    Based on above configuration, if the application throw 404 error or ServletException, it will be handled by AppExceptionHandler servlet.

    基于以上配置,如果应用程序抛出404错误或ServletException,它将由AppExceptionHandler Servlet处理。

    When such exception and error scenario appears, servlet container will invoke the corresponding HTTP method of the Exception Handler servlet and pass the request and response object. Notice that I have provided implementation of both doGet() and doPost() methods so that it can handle GET and POST requests and using a common method to process them.

    当出现此类异常和错误情况时,servlet容器将调用Exception Handler servlet的相应HTTP方法,并传递请求和响应对象。 请注意,我已经提供了doGet()和doPost()方法的实现,以便它可以处理GET和POST请求并使用通用方法来处理它们。

    Before servlet container invokes the servlet to handle the exception, it sets some attributes in the request to get useful information about the exception, some of them are javax.servlet.error.exception, javax.servlet.error.status_code, javax.servlet.error.servlet_name and javax.servlet.error.request_uri.

    在servlet容器调用servlet来处理异常之前,它会在请求中设置一些属性以获取有关异常的有用信息,其中一些属性是javax.servlet.error.exceptionjavax.servlet.error.status_codejavax.servlet。 error.servlet_namejavax.servlet.error.request_uri

    For exception, status code is always 500 that corresponds to the “Internal Server Error”, for other types of error we get different error codes such as 404, 403 etc.

    作为例外,状态码始终为500,与“内部服务器错误”相对应,对于其他类型的错误,我们将获得不同的错误代码,例如404、403等。

    Using the status code, our implementation presents different types of HTML response to the user. It also provides a hyperlink to the home page of the application.

    使用状态代码,我们的实现将不同类型HTML响应呈现给用户。 它还提供了到应用程序主页的超链接。

    Now when we will hit our servlet that is throwing ServletException, we will get a response like below image.

    现在,当我们点击抛出ServletException的servlet时,我们将得到如下图所示的响应。

    If we try to access an invalid URL that will result in 404 response, we will get response like below image.

    如果我们尝试访问无效的URL,这将导致404响应,我们将得到如下图所示的响应。

    Doesn’t it look good and helps user to easily understand what happened and provides them a way to go to the correct location. It also avoids sending application sensitive information to the user. We should always have exception handlers in place for our web application.

    它看起来不是很好,并且可以帮助用户轻松地了解发生了什么并为他们提供前往正确位置的方法。 它还避免了将应用程序敏感信息发送给用户。 我们应该始终为我们的Web应用程序配备异常处理程序。

    If you want to handle runtime exceptions and all other exceptions in a single exception handler, you can provide exception-type as Throwable.

    如果要在单个异常处理程序中处理运行时异常和所有其他异常,则可以将异常类型提供为Throwable。

    <error-page>
      <exception-type>java.lang.Throwable</exception-type>
      <location>/AppExceptionHandler</location>
    </error-page>

    If there are multiple error-page entries, let’s say one for Throwable and one for IOException and application throws FileNotFoundException then it will be handled by error handler of IOException.

    如果有多个错误页面条目,例如对于Throwable一项,对于IOException一项,应用程序抛出FileNotFoundException,那么它将由IOException的错误处理程序处理。

    You can also use JSP page as exception handler, just provide the location of jsp file rather than servlet mapping.

    您还可以将JSP页面用作异常处理程序,仅提供jsp文件的位置而不是servlet映射。

    That’s all for servlet exception handling in web application, I hope you liked it.

    这就是Web应用程序中servlet异常处理的全部,希望您喜欢它。

    Check out other articles in this series:

    查看本系列的其他文章:

    1. Java Web Application

      Java Web应用程序
    2. Java Servlet Tutorial

      Java Servlet教程
    3. Session Management in Java

      Java中的会话管理
    4. Servlet Filter

      Servlet过滤器
    5. Servlet Listeners

      Servlet侦听器
    6. Cookies in Servlets

      Servlet中的Cookie
    7. Servlet File Upload and Download Example

      Servlet文件上传和下载示例

    翻译自: https://www.journaldev.com/1973/servlet-exception-and-error-handling-example-tutorial

    servlet 异常处理

    更多相关内容
  • JavaWeb——Servlet Tomcat工作机制动画演示(点击动图可全屏观看) 什么是Servlet Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互...

                                 JavaWeb——Servlet

     

     

     

     

    Tomcat工作机制动画演示(点击动图可全屏观看)


     

    什么是Servlet


        

        Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的,一般情况下,人们将Servlet理解为后者。

        Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议Web服务器

     

     

    Servlet的工作模式


        

    • 客户端发送请求至服务器
    • 服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器
    • 服务器将响应返回客户端

     

    Servlet API 概览


        Servlet API 包含以下4个Java包:

    1.javax.servlet   其中包含定义servlet和servlet容器之间契约的类和接口。

    2.javax.servlet.http   其中包含定义HTTP Servlet 和Servlet容器之间的关系。

    3.javax.servlet.annotation   其中包含标注servlet,Filter,Listener的标注。它还为被标注元件定义元数据。

    4.javax.servlet.descriptor,其中包含提供程序化登录Web应用程序的配置信息的类型。

     

     

    Servlet 的主要类型


     

     

     

     

     

    Servlet 的使用方法


        

    Servlet技术的核心是Servlet,它是所有Servlet类必须直接或者间接实现的一个接口。在编写实现Servlet的Servlet类时,直接实现它。在扩展实现这个这个接口的类时,间接实现它。

     

    Servlet 的工作原理


     

        Servlet接口定义了Servletservlet容器之间的契约。这个契约是: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对象。

     

     

     

    Servlet 接口中定义的方法


        让我们首先来看一看Servlet接口中定义了哪些方法吧。

     

    public interface Servlet {
        void init(ServletConfig var1) throws ServletException;
    
        ServletConfig getServletConfig();
    
        void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    
        String getServletInfo();
    
        void destroy();
    }
    

        

    Servlet 的生命周期


    其中,init( ),service( ),destroy( )是Servlet生命周期的方法。代表了Servlet从“出生”到“工作”再到“死亡 ”的过程。Servlet容器(例如TomCat)会根据下面的规则来调用这三个方法:

    1.init( ),当Servlet第一次被请求时,Servlet容器就会开始调用这个方法来初始化一个Servlet对象出来,但是这个方法在后续请求中不会在被Servlet容器调用,就像人只能“出生”一次一样。我们可以利用init( )方法来执行相应的初始化工作。调用这个方法时,Servlet容器会传入一个ServletConfig对象进来从而对Servlet对象进行初始化

    2.service( )方法,每当请求Servlet时,Servlet容器就会调用这个方法。就像人一样,需要不停的接受老板的指令并且“工作”。第一次请求时,Servlet容器会先调用init( )方法初始化一个Servlet对象出来,然后会调用它的service( )方法进行工作,但在后续的请求中,Servlet容器只会调用service方法了。

    3.destory,当要销毁Servlet时,Servlet容器就会调用这个方法,就如人一样,到时期了就得死亡。在卸载应用程序或者关闭Servlet容器时,就会发生这种情况,一般在这个方法中会写一些清除代码。

        首先,我们来编写一个简单的Servlet来验证一下它的生命周期:

     

    public class MyFirstServlrt implements Servlet {
    
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
            System.out.println("Servlet正在初始化");
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            //专门向客服端提供响应的方法
            System.out.println("Servlet正在提供服务");
    
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
            System.out.println("Servlet正在销毁");
        }
    }      

    然后在xml中配置正确的映射关系,在浏览器中访问Servlet,第一次访问时,控制台输出了如下信息:

    然后,我们在浏览器中刷新3遍

    控制台输出的信息变成了下面这样:

    接下来,我们关闭Servlet容器

    控制台输出了Servlet的销毁信息,这就是一个Servlet的完整生命周期。

     

    Servlet 的其它两个方法


        getServletInfo( ),这个方法会返回Servlet的一段描述,可以返回一段字符串。

        getServletConfig( ),这个方法会返回由Servlet容器传给init( )方法的ServletConfig对象。

     

     

    ServletRequset接口


        Servlet容器对于接受到的每一个Http请求,都会创建一个ServletRequest对象,并把这个对象传递给Servlet的Sevice( )方法。其中,ServletRequest对象内封装了关于这个请求的许多详细信息。

    让我们来看一看ServletRequest接口的部分内容:

     

    public interface ServletRequest {
      
    
        int getContentLength();//返回请求主体的字节数
    
        String getContentType();//返回主体的MIME类型
    
        String getParameter(String var1);//返回请求参数的值
    
    }

    其中,getParameter是在ServletRequest中最常用的方法,可用于获取查询字符串的值

     

     

    ServletResponse接口


        javax.servlet.ServletResponse接口表示一个Servlet响应,在调用Servlet的Service( )方法前,Servlet容器会先创建一个ServletResponse对象,并把它作为第二个参数传给Service( )方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。

        让我们也来看看ServletResponse内部定义了哪些方法:

     

    public interface ServletResponse {
        String getCharacterEncoding();
    
        String getContentType();
    
        ServletOutputStream getOutputStream() throws IOException;
    
        PrintWriter getWriter() throws IOException;
    
        void setCharacterEncoding(String var1);
    
        void setContentLength(int var1);
    
        void setContentType(String var1);
    
        void setBufferSize(int var1);
    
        int getBufferSize();
    
        void flushBuffer() throws IOException;
    
        void resetBuffer();
    
        boolean isCommitted();
    
        void reset();
    
        void setLocale(Locale var1);
    
        Locale getLocale();
    }

     

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

     

        在向客户端发送响应时,大多数都是使用该对象向客户端发送HTML。

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

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

     

     

    ServletConfig接口


        当Servlet容器初始化Servlet时,Servlet容器会给Servlet的init( )方式传入一个ServletConfig对象。

    其中几个方法如下:

     

     

    ServletContext对象


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

    通过在ServletConfig中调用getServletContext方法,也可以获得ServletContext对象。

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

    ServletContext中的下列方法负责处理属性:

     

    Object getAttribute(String var1);
    
    Enumeration<String> getAttributeNames();
    
    void setAttribute(String var1, Object var2);
    
    void removeAttribute(String var1);

     

     

     

     

     

    GenericServlet抽象类 


        前面我们编写Servlet一直是通过实现Servlet接口来编写的,但是,使用这种方法,则必须要实现Servlet接口中定义的所有的方法,即使有一些方法中没有任何东西也要去实现,并且还需要自己手动的维护ServletConfig这个对象的引用。因此,这样去实现Servlet是比较麻烦的。

    void init(ServletConfig var1) throws ServletException;

        幸好,GenericServlet抽象类的出现很好的解决了这个问题。本着尽可能使代码简洁的原则,GenericServlet实现了Servlet和ServletConfig接口,下面是GenericServlet抽象类的具体代码:

     

    public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
        private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
        private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
        private transient ServletConfig config;
    
        public GenericServlet() {
        }
    
        public void destroy() {
        }
    
        public String getInitParameter(String name) {
            ServletConfig sc = this.getServletConfig();
            if (sc == null) {
                throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
            } else {
                return sc.getInitParameter(name);
            }
        }
    
        public Enumeration<String> getInitParameterNames() {
            ServletConfig sc = this.getServletConfig();
            if (sc == null) {
                throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
            } else {
                return sc.getInitParameterNames();
            }
        }
    
        public ServletConfig getServletConfig() {
            return this.config;
        }
    
        public ServletContext getServletContext() {
            ServletConfig sc = this.getServletConfig();
            if (sc == null) {
                throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
            } else {
                return sc.getServletContext();
            }
        }
    
        public String getServletInfo() {
            return "";
        }
    
        public void init(ServletConfig config) throws ServletException {
            this.config = config;
            this.init();
        }
    
        public void init() throws ServletException {
        }
    
        public void log(String msg) {
            this.getServletContext().log(this.getServletName() + ": " + msg);
        }
    
        public void log(String message, Throwable t) {
            this.getServletContext().log(this.getServletName() + ": " + message, t);
        }
    
        public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    
        public String getServletName() {
            ServletConfig sc = this.getServletConfig();
            if (sc == null) {
                throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
            } else {
                return sc.getServletName();
            }
        }
    }
    
        其中,GenericServlet抽象类相比于直接实现Servlet接口,有以下几个好处:
    

     

    1.为Servlet接口中的所有方法提供了默认的实现,则程序员需要什么就直接改什么,不再需要把所有的方法都自己实现了。

    2.提供方法,包围ServletConfig对象中的方法。

     

    3.将init( )方法中的ServletConfig参数赋给了一个内部的ServletConfig引用从而来保存ServletConfig对象,不需要程序员自己去维护ServletConfig了。

    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    但是,我们发现在GenericServlet抽象类中还存在着另一个没有任何参数的Init()方法:

     

    public void init() throws ServletException {
    }

    设计者的初衷到底是为了什么呢?在第一个带参数的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到底厉害在哪里吧。

     

     

    javax.servlet.http包内容


        之所以所HttpServlet要比GenericServlet强大,其实也是有道理的。HttpServlet是由GenericServlet抽象类扩展而来的,HttpServlet抽象类的声明如下所示:

     

    public abstract class HttpServlet extends GenericServlet implements Serializable 

    HttpServlet之所以运用广泛的另一个原因是现在大部分的应用程序都要与HTTP结合起来使用。这意味着我们可以利用HTTP的特性完成更多更强大的任务。Javax。servlet.http包是Servlet API中的第二个包,其中包含了用于编写Servlet应用程序的类和接口。Javax.servlet.http中的许多类型都覆盖了Javax.servlet中的类型。

     

     

    HttpServlet抽象类


        HttpServlet抽象类是继承于GenericServlet抽象类而来的。使用HttpServlet抽象类时,还需要借助分别代表Servlet请求和Servlet响应的HttpServletRequest和HttpServletResponse对象。

    HttpServletRequest接口扩展于javax.servlet.ServletRequest接口,HttpServletResponse接口扩展于javax.servlet.servletResponse接口。

     

    public interface HttpServletRequest extends ServletRequest
    public interface HttpServletResponse extends ServletResponse

    HttpServlet抽象类覆盖了GenericServlet抽象类中的Service( )方法,并且添加了一个自己独有的Service(HttpServletRequest request,HttpServletResponse方法。

    让我们来具体的看一看HttpServlet抽象类是如何实现自己的service方法吧:

        首先来看GenericServlet抽象类中是如何定义service方法的:

     

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    我们看到是一个抽象方法,也就是HttpServlet要自己去实现这个service方法,我们在看看HttpServlet是怎么覆盖这个service方法的:

     

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException("non-HTTP request or response");
        }
    
        this.service(request, response);
    }

        我们发现,HttpServlet中的service方法把接收到的ServletRequsest类型的对象转换成了HttpServletRequest类型的对象,把ServletResponse类型的对象转换成了HttpServletResponse类型的对象。之所以能够这样强制的转换,是因为在调用Servlet的Service方法时,Servlet容器总会传入一个HttpServletRequest对象和HttpServletResponse对象,预备使用HTTP。因此,转换类型当然不会出错了。

        转换之后,service方法把两个转换后的对象传入了另一个service方法,那么我们再来看看这个方法是如何实现的:

     

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
    
    }

     

        我们发现,这个service方法的参数是HttpServletRequest对象和HttpServletResponse对象,刚好接收了上一个service方法传过来的两个对象。

     

        接下来我们再看看service方法是如何工作的,我们会发现在service方法中还是没有任何的服务逻辑,但是却在解析HttpServletRequest中的方法参数,并调用以下方法之一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete。这7种方法中,每一种方法都表示一个Http方法。doGet和doPost是最常用的。所以,如果我们需要实现具体的服务逻辑,不再需要覆盖service方法了,只需要覆盖doGet或者doPost就好了。

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

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

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

     

     

    HttpServletRequest接口


        

        HttpServletRequest表示Http环境中的Servlet请求。它扩展于javax.servlet.ServletRequest接口,并添加了几个方法。

     

     

    String getContextPath();//返回请求上下文的请求URI部分
    Cookie[] getCookies();//返回一个cookie对象数组
    String getHeader(String var1);//返回指定HTTP标题的值
    String getMethod();//返回生成这个请求HTTP的方法名称
    String getQueryString();//返回请求URL中的查询字符串
    HttpSession getSession();//返回与这个请求相关的会话对象

    HttpServletRequest内封装的请求


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

        关于HTTP具体的详细解释,可以参考我的另一篇博文:JavaWeb——HTTP。

     

    通过request获得请求行


     

    假设查询字符串为:username=zhangsan&password=123

    获得客户端的请求方式:String getMethod()

    获得请求的资源:

    String getRequestURI()

    StringBuffer getRequestURL()

    String getContextPath() ---web应用的名称

    String getQueryString() ---- get提交url地址后的参数字符串

     

    通过request获得请求头


    long getDateHeader(String name)

    String getHeader(String name)

    Enumeration getHeaderNames()

    Enumeration getHeaders(String name)

    int getIntHeader(String name)

    referer头的作用:执行该此访问的的来源,做防盗链

     

    通过request获得请求体


    请求体中的内容是通过post提交的请求参数,格式是:

    username=zhangsan&password=123&hobby=football&hobby=basketball

    key ---------------------- value

    username                               [zhangsan]

    password                               [123]

    hobby                                          [football,basketball]                                       

    以上面参数为例,通过一下方法获得请求参数:

    String getParameter(String name)

    String[] getParameterValues(String name)

    Enumeration getParameterNames()

    Map<String,String[]> getParameterMap()

          注意:get请求方式的请求参数 上述的方法一样可以获得。

     

     

    Request乱码问题的解决方法


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

     

     

     

    解决post提交方式的乱码:request.setCharacterEncoding("UTF-8");

     解决get提交的方式的乱码:

    parameter = newString(parameter.getbytes("iso8859-1"),"utf-8");

     

    HttpServletResponse接口


     

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

     

     

    HttpServletResponse内封装的响应


     

     

     

    通过Response设置响应


     

     

     

    void addCookie(Cookie var1);//给这个响应添加一个cookie
    void addHeader(String var1, String var2);//给这个请求添加一个响应头
    void sendRedirect(String var1) throws IOException;//发送一条响应码,讲浏览器跳转到指定的位置
    void setStatus(int var1);//设置响应行的状态码

    addHeader(String name, String value)

    addIntHeader(String name, int value)

    addDateHeader(String name, long date)

    setHeader(String name, String value)

    setDateHeader(String name, long date)

    setIntHeader(String name, int value)

    其中,add表示添加,而set表示设置

     

    PrintWriter getWriter()

    获得字符流,通过字符流的write(String s)方法可以将字符串设置到response   缓冲区中,随后Tomcat会将response缓冲区中的内容组装成Http响应返回给浏览器端。

    ServletOutputStream getOutputStream()

    获得字节流,通过该字节流的write(byte[] bytes)可以向response缓冲区中写入字节,再由Tomcat服务器将字节内容组成Http响应返回给浏览器。

     

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

     

    Response的乱码问题


     

    原因:response缓冲区的默认编码是iso8859-1,此码表中没有中文。所以需要更改response的编码方式:

     

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

        上面通过调用两个方式分别改变服务端对于Response的编码方式以及浏览器的解码方式为同样的UTF-8编码来解决编码方式不一样发生乱码的问题。

    response.setContentType("text/html;charset=UTF-8")这个方法包含了上面的两个方法的调用,因此在实际的开发中,只需要调用一个response.setContentType("text/html;charset=UTF-8")方法即可。

     

    Response的工作流程


     

     

    Servlet的工作流程


     

     

    编写第一个Servlet


        首先,我们来写一个简单的用户名,密码的登录界面的html文件:

    <form action="/form" method="get">

    该html文件在最后点击提交按钮时,把表单所有数据通过Get方式发送到/form虚拟路径下:

     

     

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/form" method="get">
        <span>用户名</span><input type="text" name="username"><br>
        <span>密码</span><input type="password" name="password"><br>
        <input type="submit" name="submit">
    </form>
    
    </body>
    </html>

    访问一下我们刚才写的这个简单的登录界面:

        接下来,我们就开始写一个Servlet用来接收处理表单发送过来的请求,这个Servlet的名称就叫做FormServlet:

     

    public class FormServlet extends HttpServlet {
        private static final long serialVersionUID = -4186928407001085733L;
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             //设置响应的编码格式为UTF-8编码,否则发生中文乱码现象
            response.setContentType("text/html;charset=UTF-8");
            //1.获得请求方式
            String method = request.getMethod();
            //2.获得请求的资源相关的内容
            String requestURI = request.getRequestURI();//获得请求URI
            StringBuffer requestURL = request.getRequestURL();
            String webName = request.getContextPath();//获得应用路径(应用名称)
            String querryString = request.getQueryString();//获得查询字符串
    
            response.getWriter().write("<h1>下面是获得的字符串</h1>");
            response.getWriter().write("<h1>method(HTTP方法):<h1>");
            response.getWriter().write("<h1>"+method+"</h1><br>");
            response.getWriter().write("<h1>requestURi(请求URI):</h1>");
            response.getWriter().write("<h1>" + requestURI + "</h1><br>");
            response.getWriter().write("<h1>webname(应用名称):</h1>");
            response.getWriter().write("<h1>" + webName + "</h1><br>");
            response.getWriter().write("<h1>querrystring(查询字符串):</h1>");
            response.getWriter().write("<h1>" + querryString + "</h1>");
    
    
        }
    }

        该Servlet的作用是,接收form登录表单发送过来的HTTP请求,并解析出请求中封装的一些参数,然后在回写到response响应当中去,最后在浏览器端显示。

        最后一步,我们在XML中配置好这个Servlet的映射关系:

     

    </servlet-mapping>
        <servlet>
            <servlet-name>FormServlet</servlet-name>
            <servlet-class>com.javaee.util.FormServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>FormServlet</servlet-name>
            <url-pattern>/form</url-pattern>
        </servlet-mapping>

        接下来,启动tomcat,在浏览器中输入登录表单的地址:

    填入用户名为:root,密码为:123,最后点击提交:

    提交之后,表单数据将会发送到相应的Servlet进行处理,此时,浏览器的地址变成如下所示:

        我们会发现,在地址栏中,多了后面的“?username=root&password=123&提交=提交”字符串,这其实就是我们开始填写的参数,以Get的方法发送过去,所以查询字符串会直接加在链接后面,如果采用的是Post方式则不会出现在链接中,因此,登录表单为了安全性大多采用Post方式提交。

        我们来看看Servlet给我们返回了什么东西:

        正如我们在Servlet中写的那样,Servlet把HTTP请求中的部分参数给解析出来了。

    因此,可以再翻到上面的Servlet重新去理解一遍Servlet的工作原理,可能会有更清晰的认识。

     

     

    Servlet的局限性


     

        我们已经看到,Servlet如果需要给客户端返回数据,比如像下面这样的一个HTML文件:

     

    Servlet内部需要这样写输出语句:

     

    PrintWriter writer = response.getWriter();
    writer.write("<!DOCTYPE html>\n" +
            "<html>\n" +
            "\t<head>\n" +
            "\t\t<meta charset=\"UTF-8\">\n" +
            "\t\t<title>标题标签</title>\n" +
            "\t</head>\n" +
            "\t<body>\n" +
            "\t\t<!--标题标签-->\n" +
            "\t\t<h1>公司简介</h1><br />\n" +
            "\t\t<h2>公司简介</h2><br />\n" +
            "\t\t<h3>公司简介</h3><br />\n" +
            "\t\t<h4>公司简介</h4><br />\n" +
            "\t\t\n" +
            "\t\t<!--加入一条水平线-->\n" +
            "\t\t<hr />\n" +
            "\t\t\n" +
            "\t\t<h5>公司简介</h5><br />\n" +
            "\t\t<h6>公司简介</h7><br />\n" +
            "\t\t<h100>公司简介</h100>\n" +
            "\t</body>\n" +
            "</html>\n");

        即一行一行的把HTML语句给用Writer输出,早期简单的网页还能应付得住,但是随着互联网的不断发展,网站的内容和功能越来越强大,一个普通的HTML文件可能就达到好几百行,如果在采用使用Servlet去一行一行的输出HTML代码的话,将会非常的繁琐并且浪费大量的时间,且在当时,出现了PHP这种可以内嵌到HTML文件的动态语言,使得制作动态网页变得异常的简单和轻松,因此大量的程序员转上了PHP语言的道路,JAVA的份额急剧减小,当时JAVA的开发者Sun公司为了解决这个问题,也开发出了自己的动态网页生成技术,使得同样可以在HTML文件里内嵌JAVA代码,这就是现在的JSP技术,关于JSP技术的具体内容,我们将留到下一节进行讲解。

     

    ServletContextListener(Servlet全局监听器)


    首先要说明的是,ServletContextListener是一个接口,我们随便写一个类,只要这个类实现了ServletContextListener接口,那么这个类就实现了【监听ServletContext】的功能。那么,这个神奇的接口是如何定义的呢?我们来看一下这个接口的内部情况:

    package javax.servlet;

    import java.util.EventListener;

    public interface ServletContextListener extends EventListener {
       
    void contextInitialized(ServletContextEvent var1);


        void contextDestroyed(ServletContextEvent var1);
    }

    我们发现,在这个接口中只声明了两个方法,分别是void contextInitialized(ServletContextEvent var1)和void contextDestroyed(ServletContextEvent var1)方法,所以,我们很容易的就能猜测到,ServletContext的生命只有两种,分别是:

    1.ServletContext初始化。(应用start时)---------->Servlet容器调用void contextInitialized(ServletContextEvent var1)

    2.ServletContext销毁。(应用stop时)---------->Servlet容器调用 void contextDestroyed(ServletContextEvent var1)

    因此,我们大概能够猜到ServletContextListener的工作机制了,当应用启动时,ServletContext进行初始化,然后Servlet容器会自动调用正在监听ServletContext的ServletContextListener的void contextInitialized(ServletContextEvent var1)方法,并向其传入一个ServletContextEvent对象。当应用停止时,ServletContext被销毁,此时Servlet容器也会自动地调用正在监听ServletContext的ServletContextListener的void contextDestroyed(ServletContextEvent var1)方法。

    为了验证我们的猜测,我们来随便写一个类,并且实现ServletContextListener接口,即实现监听ServletContext的功能:

    import javax.servlet.ServletContextEvent;

    import javax.servlet.ServletContextListener;

    public class MyListener implements ServletContextListener {

        @Override

        public void contextInitialized(ServletContextEvent servletContextEvent) {

            System.out.println("ServletContextListener.contextInitialized方法被调用");

        }

        @Override

        public void contextDestroyed(ServletContextEvent servletContextEvent) {

            System.out.println("ServletContextListener.contextDestroyed方法被调用");

        }

    }

    然后,在web.xml中注册我们自己写的这个MyListener:

    <?xml version="1.0" encoding="UTF-8"?>

    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"

             version="4.0">

        <listener>

            <listener-class>MyListener</listener-class>

        </listener>

    </web-app>

    接下来,让我们启动一下Tomcat,看一看会发生什么吧!控制台打印信息如下:

     

    我们发现,当应用启动时,ServletContextListener.contextInitialized()方法被调用了。这其实是Servlet容器偷偷干的事情。那么,当我们停止Tomcat时,按照猜想,Servlet容器应该也会偷偷调用void contextDestroyed(ServletContextEvent var1)方法,来通知ServletContextListener监听器:ServletContext已经被销毁了。那么,事实是不是和我们猜想的一模一样呢?让我们来停止Tomcat的运行,看一看控制台的情况吧:

     

    我们发现,void contextDestroyed(ServletContextEvent var1)方法确实被Servlet容器调用了。因此,我们的猜想得到了证实。

    【进阶】ServletContextListener在Spring中的应用


    如果基础好一点的童鞋,或者已经学过Spring框架的同学,建议阅读下面的内容,没有学过Spring也没有关系,可以先学或者学完之后再回头来看一看,Spring容器是如何借用ServletContextListener这个接口来实例化的。

    首先让我们再来回顾一下ServletContext的概念,ServletContext翻译成中文叫做“Servlet上下文”或者“Servlet全局”,但是这个翻译我认为翻译的实在是有点牵强,也导致了许多的开发者不明白这个变量到底具体代表了什么。其实ServletContext就是一个“域对象”,它存在于整个应用中,并在在整个应用中有且仅有1份,它表示了当前整个应用的“状态”,你也可以理解为某个时刻的ServletContext代表了这个应用在某个时刻的“一张快照”,这张“快照”里面包含了有关应用的许多信息,应用的所有组件都可以从ServletContext获取当前应用的状态信息。ServletContext随着程序的启动而创建,随着程序的停止而销毁。通俗点说,我们可以往这个ServletContext域对象中“存东西”,然后也可以在别的地方中“取出来”。

    我们知道,Spring容器可以通过:

    ApplicationContext ctx=new ClassPathXmlApplicationContext("配置文件的路径");

    显示地实例化一个Spring IOC容器。也可以像下面一样,在web.xml中注册Spring IOC容器:

    <listener>

        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>

            classpath:applicationContext.xml

        </param-value>

    </context-param>

    其中的监听器类【org.springframework.web.context.ContextLoaderListener】实现了ServletContextListener接口,能够监听ServletContext的生命周期中的“初始化”和“销毁”。注意,这个【org.springframework.web.context.ContextLoaderListener】监听器类当然不是我们自己写的哦,是人家Spring团队写的,我们只要拿来用就行了。当然,别忘记导入相关的Jar包。(spring-web-4.2.4.RELEASE.jar

    那么,Spring团队给我们提供的这个监听器类是如何实现:当ServletContext初始化后,Spring IOC容器也能跟着初始化的呢?怀着好奇心,让我们再来看一看【org.springframework.web.context.ContextLoaderListener】的内部实现情况吧。

     

     

    package org.springframework.web.context;

    import javax.servlet.ServletContextEvent;

    import javax.servlet.ServletContextListener;

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

        public ContextLoaderListener() {

        }

        public ContextLoaderListener(WebApplicationContext context) {

            super(context);

        }

    --------------------------------------------------------重点关注下面这里哦!-----------------------------------------------------------------------

        public void contextInitialized(ServletContextEvent event) {

            this.initWebApplicationContext(event.getServletContext());

        }

    --------------------------------------------------------重点关注上面这里哦!-----------------------------------------------------------------------

        public void contextDestroyed(ServletContextEvent event) {

            this.closeWebApplicationContext(event.getServletContext());

            ContextCleanupListener.cleanupAttributes(event.getServletContext());

        }

    }

    我们发现,【org.springframework.web.context.ContextLoaderListener】这个类实现了ServletContextListener接口中的两个方法,其中,当ServletContext初始化后, public void contextInitialized(ServletContextEvent event)方法被调用,接下来执行initWebApplicationContext(event.getServletContext())方法,但是我们发现这个方法并没有在这个类中声明,因此,我们再看一下其父类中是如何声明的:

     

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
    
            throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
    
        } else {
    
            Log logger = LogFactory.getLog(ContextLoader.class);
    
            servletContext.log("Initializing Spring root WebApplicationContext");
    
            if (logger.isInfoEnabled()) {
    
                logger.info("Root WebApplicationContext: initialization started");
    
            }
    
    
    
            long startTime = System.currentTimeMillis();
    
    
    
            try {
    
                if (this.context == null) {
    
                    this.context = this.createWebApplicationContext(servletContext);
    
                }
    
    
    
                if (this.context instanceof ConfigurableWebApplicationContext) {
    
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
    
                    if (!cwac.isActive()) {
    
                        if (cwac.getParent() == null) {
    
                            ApplicationContext parent = this.loadParentContext(servletContext);
    
                            cwac.setParent(parent);
    
                        }
    
    
    
                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
    
                    }
    
                }
    
    
    
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    
                if (ccl == ContextLoader.class.getClassLoader()) {
    
                    currentContext = this.context;
    
                } else if (ccl != null) {
    
                    currentContextPerThread.put(ccl, this.context);
    
                }
    
    
    
                if (logger.isDebugEnabled()) {
    
                    logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
    
                }
    
    
    
                if (logger.isInfoEnabled()) {
    
                    long elapsedTime = System.currentTimeMillis() - startTime;
    
                    logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
    
                }
    
    
    
                return this.context;
    
            } catch (RuntimeException var8) {
    
                logger.error("Context initialization failed", var8);
    
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
    
                throw var8;
    
            } catch (Error var9) {
    
                logger.error("Context initialization failed", var9);
    
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
    
                throw var9;
    
            }
    
        }
    
    }

     

     

     

     

    分析到这一步,我们发现Spring容器在这个方法中被实例化了。接下来,就让我们整理一下整体的思路:

    当Servlet容器启动时,ServletContext对象被初始化,然后Servlet容器调用web.xml中注册的监听器的

    public void contextInitialized(ServletContextEvent event)

    方法,而在监听器中,调用了this.initWebApplicationContext(event.getServletContext())方法,在这个方法中实例化了Spring IOC容器。即ApplicationContext对象。

    因此,当ServletContext创建时我们可以创建applicationContext对象,当ServletContext销毁时,我们可以销毁applicationContext对象。这样applicationContext就和ServletContext“共生死了”。

     

    基础不牢?新手不友好?无人带路?关注《扬俊的小屋》公众号吧!


     

    参考资料


     

    【1】 维基百科-Servlet

    【2】Servlet、JSP和Spring MVC初学指南 【加】Buid Kurniawan 【美】Paul Deck 著 林仪明 俞黎敏 译 中国工信出版社 

     

    博客文章版权说明


     

     

     

    第一条 本博客文章仅代表作者本人的观点,不保证文章等内容的有效性。

    第二条 本博客部分内容转载于合作站点或摘录于部分书籍,但都会注明作/译者和原出处。如有不妥之处,敬请指出。

    第三条 征得本博客作者同意的情况下,本博客的作品允许非盈利性引用,并请注明出处:“作者:____转载自____”字样,以尊重作者的劳动成果。版权归原作/译者所有。未经允许,严禁转载

    第四条 对非法转载者,“扬俊的小屋”和作/译者保留采用法律手段追究的权利

    第五条 本博客之声明以及其修改权、更新权及最终解释权均属“扬俊的小屋”。

    第六条 以上声明的解释权归扬俊的小屋所有。

     

     

     

     

     

    展开全文
  • Servlet接口是Java Servlet API的核心抽象。所有Servlet类必须直接或间接的实现该接口,或者更通常做法是通过继承一个实现了该接口的类从而复用许多共性功能。目前有GenericServlet和HttpServlet这两个类实现了...
  • Servlet基础详解

    万次阅读 多人点赞 2022-02-14 17:02:51
    Servlet的基本概念及使用规范

    目录

    前言

    一、Servlet简介

    二、Servlet的作用

    三、Servlet处理的信息是什么

    四、Servlet的开发流程

    (一)、实现Servlet接口或者继承HttpServlet类

    (二)、重写HttpServlet类的doGet()和doPost()方法

    (三)、配置web.xml文件或者使用注解对servlet进行配置

    五、Servlet的生命周期

    (一)、通过web.xml文件来配置load-on-startup(启动时加载)优先级

    ​(二)、通过注解来配置load-on-startup(启动时加载)优先级

    六、Servlet获取参数的方法

    (一)、单个获取请求参数的方法

    ​(二)、获取键值对形式的请求参数的方法

    ​(三)、get特有的查询字符串的方法以及post以流的形式查询字符串的方法。

    七、Servlet响应页面和数据

    (一)、Servlet响应页面

    (二)、Servlet响应数据

    ​八、Web项目的初始化参数

    (一)、Servlet中的初始化参数如何配置及如何获取

    ​(二)、application在应用中初始化参数如配放置如何获取

    ​(三)、tomcat中初始化参数如何配置如何获取

    ​九、Servlet的线程安全问题

    总结



    前言

    Servlet是开发动态Web资源的一项技术,在整个开发架构中主要承担控制器(Controller)的作用,刚开始本章主要学习了解Servlet的基础概念及使用规范。


    一、Servlet简介

    Servlet是Server Applet的简称,翻译过来就是服务程序。是由sun公司提供的一门用于开发动态Web资源的技术。简单的讲,Servlet就是运行在服务器上的一个小程序,用来处理服务器接收到的请求。

    比如一般的网页程序,是由我们通过浏览器来访问实现的,在这个过程中,我们的浏览器发送访问请求,服务器接收到请求,并对浏览器的请求做出相应的处理,这就是我们熟悉的B/S模型(浏览器—服务器模型)。而Servlet就是对请求做出处理的组件,运行于支持java的应用服务器中。

    Servlet从狭义上说白了就是个接口,从广义上说就是实现了这个接口的所有的子类。

    附加:我们打开项目目录下的JSP文件查看其源码,可以发现JSP底层也是实现了Servlet接口的,所以JSP它本质上就是一个Servlet。

    二、Servlet的作用

    在Servlet刚刚出现的年代,Servlet的作用十分复杂,即承担了处理数据的作用,又承担了展示页面的作用,导致代码的耦合性过高,不易于维护。随着时间的推移,出现了MVC思想,也就是模型 - 界面 - 控制器思想,极大的简便了开发,也明确了Servlet的作用。

    附加:最经典的MVC模型是:JSP+JavaBean+Servlet开发模式。

     根据上图我们可以知道,Servlet在其中承担的作用是Controller(控制器),起到对数据进行操作的作用。

    三、Servlet处理的信息是什么

    如图:我们查找Servlet底层的实现类以及实现类的子类。

    除了自定义的实现类以外,还有GenericServlet实现类,该实现类中的service()方法没有实现接口中对应的方法,而是继续抽象下去。

    继续查找service()方法是在哪个类中实现的。随之就找到了HttpServlet这个子类。如图:

    HttpServlet是直接继承的GenericServlet,从上图所见,客户端发送到服务器的请求类型是多样化的,而Servlet作为控制器处理的就是这些请求报文(request)

    如下图:对客户端传过来的请求类型进行判断,更具传过来的类型执行对应的处理请求数据的方法。

    注意:我们的Web应用完全是基于http协议的。而http有请求报文(request)和响应报文(response),请求报文就是浏览器向服务器发送的数据形成的数据对象,同理,响应报文就是服务器向浏览器发送的数据形成的数据对象,而http有两个重要的方法,也就是上图中HttpServlet中的doPost()和doGet()方法。这两个方法对应的post和get,就是前端表单中使用的。比如你登录某网站首页时,提交用户名和密码,就是被http协议封装成请求报文的形式发送到服务器的,这样,servlet就能够读取请求报文的类容,并对报文进行处理。

    四、Servlet的开发流程

    (一)、实现Servlet接口或者继承HttpServlet类

    注:继承HttpServlet类更简洁更常用。

    (二)、重写HttpServlet类的doGet()和doPost()方法

    (三)、配置web.xml文件或者使用注解对servlet进行配置

    1、首先在maven配置文件pom.xml中导入Servlet - API的依赖

    2、使用web.xml配置servlet访问路径

    3、使用注解配置servlet访问路径

    注意:配置访问路径,注解和web.xml两者二选一即可,避免混淆!

    五、Servlet的生命周期

    我们通过去实现Servlet接口的方式观察Servlet的生命周期。如图:

    所谓生命周期,就是Servlet什么时候创建,调用了何种方法,最后在什么时候被销毁。我们之前学过的对象都是自己手动创建的,最后由JVM来销毁,而Servlet的整个生命周期,都是由tomcat服务器来控制的。

    由上图所示,程序运行后得出结论,Servlet的生命周期是:

    实例化 --》初始化 --》多次调用service服务 --》 tomcat容器关闭时销毁

    • 默认情况下Servlet是在第一次访问的时候实例化和初始化的
    • 第一次访问就会调用service()服务方法
    • 直到关闭服务器时,Servlet才会被销毁

    除了默认情况,我们也可以通过load-on-startup来控制Servlet在服务器启动的时候就加载(实例化+初始化)

    (一)、通过web.xml文件来配置load-on-startup(启动时加载)优先级

    (二)、通过注解来配置load-on-startup(启动时加载)优先级

    注意:配置load-on-startup启动时加载时,web.xml和注解两种方式,二选一即可。

    六、Servlet获取参数的方法

    这里主要解析Servlet获取get和post参数的方式,主要有三种:

    1. 使用getParameter()获取参数方法,单个获取。
    2. 使用getParameterMap()获取Map键值对参数的方法。
    3. get特有的查询字符串的方法以及post以流的形式查询字符串的方法。

    (一)、单个获取请求参数的方法

    (二)、获取键值对形式的请求参数的方法

    注意该方法获取到的请求参数是以Map键值对的形式存在的,并且在构造实体时,我们还需要将获取到的Map类型的参数通过BeanUtils工具类转换为需要的实体类类型进行构造。所以需要先导入BeanUtils的依赖。

    1、在maven配置文件中导入BeanUtils工具类的依赖

    2、获取参数,构造实体

    (三)、get特有的查询字符串的方法以及post以流的形式查询字符串的方法。

    这两种获取字符串的方式比较特别,get获取的是浏览器中URL地址栏访问路径?后面的参数,而post是从请求体中获取参数,这两者获取到的字符串参数都是URL编过码的,所以在获取参数时候都需要解码

    1、get请求特有的查询字符串方法

    2、post请求以流的形式获取请求体中的字符串参数

    在使用该方法前,我们需要先简单了解下tomcat的工作原理:

    如图所示,整个过程都是以流的形式传输的,所以在post请求中要获取请求体中的字符串参数,就需要先获取到这条输入流。

    注意:获取流查询字符串参数的方式要放在最前面执行,因为流是只能消耗一次,放在其它操作过流的方法后面该方法就获取不到数据了,因为流被消耗了。

    七、Servlet响应页面和数据

    (一)、Servlet响应页面

    响应页面,也就是收到前端的请求响应需要跳转的页面。Servlet响应页面有转发重定向两种方式(之前JSP的章节也讲过):

    1、转发 — 也就是携带客户端发送的请求转发跳转到下一个页面,使用的是request请求对象。

    //转发可以将请求中的数据带到新的页面。
    //getRequestDispatcher():表示获取请求转发器的方法
    //forward():表示将请求及响应对象一并转发到下一个新页面
    request.getRequestDispatcher("跳转页面路径").forward(request,response);

    2、重定向 — 服务器接收到前端请求,通过响应将要跳转的页面地址响应给客户端,客户端再重新发送请求跳转到目标页面。

    //重定向是将要跳转的页面路径交给前端重新发送请求跳转页面。
    //相当于客户端发送了两次请求,所以原来请求中的数据就没有了。
    response.sendRedirect("响应的页面路径")

    (二)、Servlet响应数据

    响应数据也是通过流的方式响应给前端,这里主要用到的是一条打印流(也就是之前JSP中的内置对象out).响应数据一般都是响应的json格式的数据,前端获取到json格式的数据就相当于拿到了一个对象,通过对象的方式去获取数据。

    1、获取到打印流响应普通数据(注意:有乱码要设置编码格式)

    2、响应json字符串格式的数据

    • 首先我们需要先在maven中导入json的依赖,如图:

    • 响应json格式数据

    八、Web项目的初始化参数

    在web项目中初始化参数有三种:

    1. 每个Servlet可以有自己的初始化参数。
    2. 一个应用程序application也有自己的初始化参数
    3. 一个tomcat也有自己的初始化参数。

    (一)、Servlet中的初始化参数如何配置及如何获取

    1、配置Servlet初始化参数

    2、获取Servlet初始化参数

    • 通过Servlet对象本身this获取Servlet初始化参数

    • 通过Servlet的初始化方法中的config配置对象获取Servlet初始化参数

    (二)、application在应用中初始化参数如配放置如何获取

    1、配置应用中的初始化参数

    2、获取application应用中的初始化参数

    (三)、tomcat中初始化参数如何配置如何获取

    配置tomcat的初始化参数,需要到tomcat的conf文件目录中找到context.xml文件源码进行配置。

    这里会使用到jndi规范(java naming directory interface译:java命名目录接口)进行配置。jndi也就是通过命名和资源关联起来的一种编程规范。

    1、配置tomcat的初始化参数

    2、获取tomcat的初始化参数

    九、Servlet的线程安全问题

    同一个Servlet在服务器中只会存在一个实例,不论是多少个访问,都调用的是同一个实例,也就是Servlet是单实例多线程的。这就存在着一定的线程安全问题,如果在Servlet中定义一个全局变量,那么程序运行这个变量的值很有可能会不是我们所预期的,所以在Servlet中要尽量避免使用全局变量。

    运行如下程序实例Servlet的线程安全问题:

    运行结果反应出Servlet中出现成员变量导致线程不安全:

    所以Servlet中尽量避免出现成员变量。


    总结

    Servlet的基础概念及使用规范,还有上篇讲的JSP技术都是为开发Web项目做准备。后续还会更新过滤器以及MVC和三层架构等……

    展开全文
  • javax.servlet.jar下载

    热门讨论 2013-10-17 16:10:36
    Files contained in javax.servlet.jar: META-INF/MANIFEST.MF javax/servlet/http/LocalStrings.properties javax.servlet.http.HttpSessionBindingListener.class javax.servlet....
  • javax.servlet.jar包下载

    2017-01-17 21:12:51
    javax.servlet.jar包,如果有报异常未找到该jar包的,可以尝试着将该jar包导入,可以解决该异常。
  • servlet和jsp学习指南

    千次下载 热门讨论 2015-07-18 21:45:28
    servlet和jsp学习指南
  • Servlet3与Servlet4

    千次阅读 2019-03-11 09:38:28
    Servlet3 Servlet 3.0 新特性 异步处理支持:有了该特性,Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程。在接收到请求之后,Servlet 线程可以将耗时的操作委派给另...

    Servlet3

    Servlet 3.0 新特性

    • 异步处理支持:有了该特性,Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程。在接收到请求之后,Servlet 线程可以将耗时的操作委派给另一个线程来完成,自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况,这将大大减少服务器资源的占用,并且提高并发处理速度。

    • 新增的注解支持:该版本新增了若干注解,用于简化 Servlet、过滤器(Filter)和监听器(Listener)的声明,这使得 web.xml 部署描述文件从该版本开始不再是必选的了。

    • 可插性支持:熟悉 Struts2 的开发者一定会对其通过插件的方式与包括 Spring 在内的各种常用框架的整合特性记忆犹新。将相应的插件封装成 JAR 包并放在类路径下,Struts2 运行时便能自动加载这些插件。现在 Servlet 3.0 提供了类似的特性,开发者可以通过插件的方式很方便的扩充已有 Web 应用的功能,而不需要修改原有的应用

    消息异步

    Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:

    1. Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理
    2. 调用业务接口的某些方法,以完成业务处理;
    3. 最后,根据处理的结果提交响应,Servlet 线程结束。

    Servlet2.x详细说明:

    其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。

    Servlet3优化改进:

    1. Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理
    2. 接着,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,此时 Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。如此一来, Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。

    web.xml
    servlet3为filter和servlet增加了异步支持标签,<async-supported>,默认是false,需要手动开启

    <servlet> 
        <servlet-name>DemoServlet</servlet-name> 
        <servlet-class>footmark.servlet.Demo Servlet</servlet-class> 
        <async-supported>true</async-supported> 
    </servlet>
    

    注解
    注解增加了属性asyncSupported支持异步

    @WebFilter(urlPatterns = "/demo",asyncSupported = true) 
    public class DemoFilter implements Filter{...}
    

    异步Demo如下:

    @WebServlet(urlPatterns = "/demo", asyncSupported = true)
    public class AsyncDemoServlet extends HttpServlet {
        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException, ServletException {
            resp.setContentType("text/html;charset=UTF-8");
            PrintWriter out = resp.getWriter();
            out.println("进入Servlet的时间:" + new Date() + ".");
            out.flush();
     
            //在子线程中执行业务调用,并由其负责输出响应,主线程退出
            AsyncContext ctx = req.startAsync();
            new Thread(new Executor(ctx)).start();
     
            out.println("结束Servlet的时间:" + new Date() + ".");
            out.flush();
        }
    }
     
    public class Executor implements Runnable {
        private AsyncContext ctx = null;
        public Executor(AsyncContext ctx){
            this.ctx = ctx;
        }
     
        public void run(){
            try {
                //等待十秒钟,以模拟业务方法的执行
                Thread.sleep(10000);
                PrintWriter out = ctx.getResponse().getWriter();
                out.println("业务处理完毕的时间:" + new Date() + ".");
                out.flush();
                ctx.complete();	//告诉context,这个子线程完毕
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    为了监听异步事件,Servlet3提供了AsyncListener事件监听器

    1. 异步线程开始时,调用 AsyncListener 的 onStartAsync(AsyncEvent event)方法;
    2. 异步线程出错时,调用 AsyncListener 的 onError(AsyncEvent event)方法;
    3. 异步线程执行超时,则调用 AsyncListener 的 onTimeout(AsyncEvent event)方法;
    4. 异步执行完毕时,调用 AsyncListener 的 onComplete(AsyncEvent event) 方法;

    要注册一个 AsyncListener,只需将准备好的 AsyncListener 对象传递给 AsyncContext 对象的 addListener() 方法即可

    AsyncContext ctx = req.startAsync(); 
    ctx.addListener(new AsyncListener() { 
        public void onComplete(AsyncEvent asyncEvent) throws IOException { 
            // 做一些业务操作
        } 
        ... 
    });
    

    注解支持

    @WebServlet

    @WebServlet 用于将一个类声明为 Servlet,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为 Servlet。该注解具有下表给出的一些常用属性(以下所有属性均为可选属性,但是 vlaue 或者 urlPatterns 通常是必需的,且二者不能共存,如果同时指定,通常是忽略 value 的取值):

    属性名类型描述
    nameString指定 Servlet 的 name 属性,等价于 <servlet-name>。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。
    valueString[]该属性等价于 urlPatterns 属性。两个属性不能同时使用。
    urlPatternsString[]指定一组 Servlet 的 URL 匹配模式。等价于<url-pattern>标签。
    loadOnStartupint指定 Servlet 的加载顺序,等价于 <load-on-startup> 标签。
    initParamsWebInitParam[]指定一组 Servlet 初始化参数,等价于 <init-param> 标签。
    asyncSupportedboolean声明 Servlet 是否支持异步操作模式,等价于 <async-supported> 标签。
    descriptionString该 Servlet 的描述信息,等价于 <description> 标签。
    displayNameString该 Servlet 的显示名,通常配合工具使用,等价于<display-name>标签。

    监听使用如下:

    @WebServlet(urlPatterns = {"/simple"}, asyncSupported = true, 
    loadOnStartup = -1, name = "SimpleServlet", displayName = "ss", 
    initParams = {@WebInitParam(name = "username", value = "tom")} 
    ) 
    public class SimpleServlet extends HttpServlet{}
    

    @WebInitParam

    该注解通常不单独使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是为 Servlet 或者过滤器指定初始化参数,这等价于 web.xml 中 和 的 子标签。@WebInitParam 具有下表给出的一些常用属性:

    属性名类型是否可选描述
    nameString指定参数的名字,等价于<param-name>
    valueString指定参数的值,等价于<param-value>
    descriptionString关于参数的描述,等价于 <description>

    @WebFilter

    @WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 ):

    属性名类型描述
    filterNameString指定过滤器的 name 属性,等价于 <filter-name>
    valueString[]该属性等价于 urlPatterns 属性。但是两者不应该同时使用。
    urlPatternsString[]指定一组过滤器的 URL 匹配模式。等价于<url-pattern>标签。
    servletNamesString[]指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值,或者是web.xml 中<servlet-name> 的取值。
    dispatcherTypesDispatcherType指定过滤器的转发模式。具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。
    initParamsWebInitParam[]指定一组过滤器初始化参数,等价于 <init-param>标签。
    asyncSupportedboolean声明过滤器是否支持异步操作模式,等价于<async-supported> 标签。
    descriptionString该过滤器的描述信息,等价于 <description>标签。
    displayNameString该过滤器的显示名,通常配合工具使用,等价于 <display-name>标签。

    @WebListener

    该注解用于将类声明为监听器,被 @WebListener 标注的类必须实现以下至少一个接口:

    • ServletContextListener
    • ServletContextAttributeListener
    • ServletRequestListener
    • ServletRequestAttributeListener
    • HttpSessionListener
    • HttpSessionAttributeListener

    这个接口只有一个默认value属性
    一个简单示例如下:

    @WebListener("This is only a demo listener") 
    public class SimpleListener implements ServletContextListener{...}
    

    如此,则不需要在 web.xml 中配置<listener> 标签了。它等价的 web.xml 中的配置形式如下:

    <listener> 
        <listener-class>footmark.servlet.SimpleListener</listener-class> 
    </listener>
    

    @MultipartConfig

    该注解主要是为了辅助 Servlet 3.0 中 HttpServletRequest 提供的对上传文件的支持。该注解标注在 Servlet 上面,以表示该 Servlet 希望处理的请求的 MIME 类型是 multipart/form-data。另外,它还提供了若干属性用于简化对上传文件的处理。具体如下:

    属性名类型是否可选描述
    fileSizeThresholdint当数据量大于该值时,内容将被写入文件。
    locationString存放生成的文件地址。
    maxFileSizelong允许上传的文件最大值。默认值为 -1,表示没有限制。
    maxRequestSizelong针对该 multipart/form-data 请求的最大数量,默认值为 -1,表示没有限制。

    可插性支持

    Servlet 3.0 引入了称之为“Web 模块部署描述符片段”的 web-fragment.xml 部署描述文件,该文件必须存放在 JAR 文件的 META-INF 目录下,该部署描述文件可以包含一切可以在 web.xml 中定义的内容。JAR 包通常放在 WEB-INF/lib 目录下,除此之外,所有该模块使用的资源,包括 class 文件、配置文件等,只需要能够被容器的类加载器链加载的路径上,比如 classes 目录等。

    • 编写一个类继承自 HttpServlet,将该类放在 classes 目录下的对应包结构中,修改 web.xml,在其中增加一个 Servlet 声明。这是最原始的方式;
    • 编写一个类继承自 HttpServlet,并且在该类上使用 @WebServlet 注解将该类声明为 Servlet,将该类放在 classes 目录下的对应包结构中,无需修改 web.xml 文件。
    • 编写一个类继承自 HttpServlet,将该类打成 JAR 包,并且在 JAR 包的 META-INF 目录下放置一个 web-fragment.xml 文件,该文件中声明了相应的 Servlet 配置。
    <?xml version="1.0" encoding="UTF-8"?>
    <web-fragment
        xmlns=http://java.sun.com/xml/ns/javaee
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
        metadata-complete="true">
        <servlet>
            <servlet-name>fragment</servlet-name>
            <servlet-class>footmark.servlet.FragmentServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>fragment</servlet-name>
            <url-pattern>/fragment</url-pattern>
        </servlet-mapping>
    </web-fragment>
    

    web-fragment.xml 包含了两个可选的顶层标签, 和 ,如果希望为当前的文件指定明确的加载顺序,通常需要使用这两个标签, 主要用于标识当前的文件,而 则用于指定先后顺序。

    <web-fragment...>
        <name>FragmentA</name>
        <ordering>
            <after>
                <name>FragmentB</name>
                <name>FragmentC</name>
            </after>
        <before>
            <others/>
        </before>
        </ordering>
        ...
    </web-fragment>
    

    ServletContext 的性能增强

    该对象支持在运行时动态部署 Servlet、过滤器、监听器,以及为 Servlet 和过滤器增加 URL 映射等。以 Servlet 为例,过滤器与监听器与之类似。ServletContext 为动态配置 Servlet 增加了如下方法:

    • ServletRegistration.Dynamic addServlet(String servletName,Class<? extends Servlet> servletClass)
    • ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
    • ServletRegistration.Dynamic addServlet(String servletName, String className)
    • <T extends Servlet> T createServlet(Class clazz)
    • ServletRegistration getServletRegistration(String servletName)
    • Map<String,? extends ServletRegistration> getServletRegistrations()

    其中前三个方法的作用是相同的,只是参数类型不同而已;通过 createServlet() 方法创建的 Servlet,通常需要做一些自定义的配置,然后使用 addServlet() 方法来将其动态注册为一个可以用于服务的 Servlet。两个 getServletRegistration() 方法主要用于动态为 Servlet 增加映射信息,这等价于在 web.xml( 抑或 web-fragment.xml) 中使用 <servlet-mapping> 标签为存在的 Servlet 增加映射信息。

    以上 ServletContext 新增的方法要么是在 ServletContextListener 的 contexInitialized 方法中调用,要么是在 ServletContainerInitializer 的 onStartup() 方法中调用。

    ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口,容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup() 方法处理,我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类,过滤掉不希望给 onStartup() 处理的类。

    HttpServletRequest 对文件上传的支持

    之前上次文件必须借助第三方jar来实现,比如Apache的IO包
    Servlet 3.0 已经提供了这个功能,而且使用也非常简单。

    • Part getPart(String name)
    • Collection getParts()

    前者用于获取请求中给定 name 的文件,后者用于获取所有的文件。每一个文件用一个 javax.servlet.http.Part 对象来表示。该接口提供了处理文件的简易方法,比如 write()、delete() 等。至此,结合 HttpServletRequest 和 Part 来保存上传的文件变得非常简单,如下所示:

    Part photo = request.getPart("photo"); 
    photo.write("/tmp/photo.jpg"); 
    // 可以将两行代码简化为 request.getPart("photo").write("/tmp/photo.jpg") 一行。
    

    开发者可以配合前面提到的 @MultipartConfig 注解来对上传操作进行一些自定义的配置,比如限制上传文件的大小,以及保存文件的路径等。

    Servlet4

    Servlet 4.0 的主要新功能为服务器推送和全新 API,该 API 可在运行时发现 servlet 的 URL 映射。

    服务器推送是最直观的 HTTP/2 强化功能,通过 PushBuilder 接口在 servlet 中公开。服务器推送功能还在 JavaServer Faces API 中实现,并在 RenderResponsePhase 生命周期内调用,以便 JSF 页面可以利用其增强性能。

    全新 servlet 映射发现接口 HttpServletMapping 使框架能够获取有关激活给定 servlet 请求的 URL 信息。这可能对框架尤为有用,这些框架需要这一信息来运行内部工作。

    在接下来的部分,我将概述服务器推送及其如何在 Java servlet 中运行,包括 JSF 2.3 中的服务器推送。我还将展示一个交换示例,重点介绍全新 servlet 映射发现功能。

    服务器推送

    服务器推送使服务器能预测客户端请求的资源需求。然后,在完成请求处理之前,它可以将这些资源发送到客户端。

    要了解服务器推送的好处,可以考虑一个包含图像和其他依赖项(比如 CSS 和 JavaScript 文件)的网页。客户端发出一个针对该网页的请求。服务器然后分析所请求的页面,确定呈现它所需的资源,并主动将这些资源发送到客户端的缓存。

    在执行所有这些操作的同时,服务器仍在处理原始网页请求。客户端收到响应时,它需要的资源已经位于缓存中。

    PushBuilder

    多数来自原始 HttpServletRequest 实例的请求标头,只添加到 PushBuilder 实例中。由于正确运行服务器推送并不需要某些标头,因此不包括以下标头:

    • 条件标头
    • Range 标头
    • Expect 标头
    • Authorization 标头
    • Referrer 标头
    @Override
    protected void doGet(HttpServletRequest request, 
                         HttpServletResponse response) 
                         throws ServletException, IOException {
     
       PushBuilder pushBuilder = request.newPushBuilder();
     
    }
    

    设置推送资源并推送

    PushBuilder pushBuilder = request.newPushBuilder();
     
    if (pushBuilder != null) {
       pushBuilder.path("images/hero-banner.jpg").push();
       pushBuilder.path("css/menu.css").push();
       pushBuilder.path("js/marquee.js").push();
    }
    

    HttpServletMapping 接口

    Servlet 4.0 的全新 servlet 映射发现 API 使服务器能够对 URL(可调用 servlet)执行运行时检查。例如,对 file.ext, /path 和 /path/file.ext 的请求将通过 URL 模式 /path/* 和 *.ext 激活 servlet。

    • getMatchValue() 返回部分 URI 路径,该路径会导致请求匹配。
    • getPattern() 返回 URL 模式的 String 表示形式。
    • getServletName() 返回 servlet 名称的 String 表示形式。
    • getMappingMatch() 返回匹配的类型,表示为 MappingMatch 枚举值,该枚举值将为以下值之一CONTEXT_ROOTDEFAULT、EXACTEXTENSIONPATH

    运行时映射发现:

    @WebServlet({"/path/*", "*.ext"})
    public class ServletMapping extends HttpServlet {
     
        protected void doGet(HttpServletRequest request,
                             HttpServletResponse response) 
                             throws IOException {
     
            HttpServletMapping mapping = request.getHttpServletMapping();
            String mapping = mapping.getMappingMatch().name();
            String value = mapping.getMatchValue();
            String pattern = mapping.getPattern();
            String servletName = mapping.getServletName();
       }
     
    }
    

    细微重构变化

    • Trailer 响应标头支持发送方在分块消息的末尾包含额外字段。这用于提供在发送消息主体时可能会动态生成的元数据,例如,消息完整性检查、数字签名或后期处理状态。
    • Servlet 4.0 添加了 GenericFilter 和 HttpFilter 抽象类,这些抽象类通过最低限度地实现生命周期方法 init() 和 destroy(),简化了编写过滤器。
    • Servlet 4.0 还集成了全新的 HTTP Trailer,支持发送方在分块消息的末尾包含额外的字段。

    ServletContext 接口采用了一些新方法:
    addJspFile() 可将带有给定 JSP 文件的 servlet 添加到 servlet 上下文中。
    getSessionTimeout() 和 setSessionTimeout() 可提供对会话超时的访问权限。
    getRequestCharacterEncoding() 和 setRequestCharacterEncoding() 可为当前的 servlet 上下文提供访问权限,并改变默认的请求字符编码。

    • HttpServletRequest 接口上的 isRequestedSessionIdFromUrl() 方法已被弃用。
      由于升级到 Java SE 8,默认方法已被添加到侦听器接口中。
    展开全文
  • javax.servlet 的JAR包

    千次下载 热门讨论 2012-10-29 14:12:43
    javax.servlet JAR包,解决找不到 import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; jar包问题
  • 零基础学Java Web开发 jSP+Servlet+Struts +Spring+Hibernate+Ajax零基础学Java Web开发 jSP+Servlet+Struts +Spring+Hibernate+Ajax零基础学Java Web开发 jSP+Servlet+Struts +Spring+Hibernate+Ajax零基础学Java ...
  • Servlet

    万次阅读 2018-08-03 16:31:30
    Servlet是单实例多线程运行方式,所以对象变量线程不安全,局部变量线程安全。 Servlet 生命周期、工作原理 Servlet的生命周期由Servlet容器管理; Servlet 生命周期: - Servlet 加载—&amp;amp;amp;amp;...
  • javax.servlet-api-3.1.0.jar

    千次下载 热门讨论 2014-09-30 16:20:46
    javax.servlet-api-3.1.0.jar,处理找不到servlet JAR的问题
  • javax.servlet jar包---解决找不到javax.servlet.*等问题

    万次下载 热门讨论 2013-07-29 23:17:32
    javax.servlet的jar包,解决找不到javax.servlet.*等问题的!欢迎下载!!
  • servlet-api.jar 3.0版本(tomcat 7.0里的)

    千次下载 热门讨论 2015-01-10 13:07:48
    最新的servlet-api.jar,完美结果java代码中注释(@)部分报错的问题
  • javax.servlet.jar与javax.servlet.jsp.jar

    千次下载 热门讨论 2013-05-23 11:43:29
    javax.servlet.jar与javax.servlet.jsp.jar
  • Servlet详细教程

    万次阅读 多人点赞 2018-04-15 21:09:44
    Servlet简介 servlet是Server Applet的简称,翻译过来就是服务程序.好吧,这么说你可能还是不太懂,简单的讲,这个servlet是运行在服务器上的一个小程序,用来处理服务器请求的.进一步讲,我们知道,一般的网页...
  • Servlet生命周期详解

    千次阅读 多人点赞 2021-01-23 23:20:29
    Servlet生命周期概述servlet生命周期的四个过程1、实例化servlet对象我们的OOP(面向对象)思想中,总是先创建对象,通过对象调用成员,那么servlet如何实例化呢?设置对应servlet的优先级(loadOnStartup)2、初始化...
  • servlet3与servlet4区别和实现

    千次阅读 2019-07-08 23:41:55
    一、servlet3 记得servlet3的特性有以下几点: 1、异步处理支持:有了该特性,Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程。在接收到请求之后,Servlet 线程可以将...
  • Servlet是一种基于Java技术的Web组件,用于生成动态内容,由容器管理。类似于其它Java技术组件,Servlet 是平台无关的Java类组成,并且由Java Web服务器加载执行。 通常由Servlet容器提供运行时环境。Servlet 容器...
  •   在Servlet2.x版本中,配置依然是通过web.xml的形式进行配置的,升级到Servlet3.x后,可以依赖注解式方式进行配置。   本文只对<servlet>、<servlet-mapping>进行分析解释。   XSD地址   可以...
  • Servlet JSP深入详解 基于Tomcat的Web开发

    千次下载 热门讨论 2015-07-17 09:22:25
    Servlet JSP深入详解 基于Tomcat的Web开发
  • @WebServlet注解(Servlet注解)

    千次阅读 2022-05-15 09:53:56
    文章目录@WebServlet注解前言一、@WebServlet 注解的属性二、@WebServlet 注解的使用1. 启用注解支持2.使用 @WebServlet 注解@WebServlet 注解 和 web.xml 的区别 前言 Servlet 中,web.xml 扮演的角色十分的重要,...
  • servlet类放在一个package首先,把这个servlet类放在一个Java package 。 您应该始终在一个包中公开可重用的Java类,否则它们对于包中的类(如服务器本身)是不可见的。 这样可以消除潜在的环境特定问题。 无包...
  • 这是是之前报的错 servlet服务的包就叫做servlet 从报错来看是没找到这个类,这是我改名字之后的,但是我改的类名字不是这个名字,我看了一个老哥说,不能带servlet字样,我试着重命名,结果报了一个异常,然后 ...
  • Servlet学习笔记-Servlet工作原理和过程

    千次阅读 多人点赞 2020-03-07 01:32:24
      Java Servlet技术简称Servlet技术,是Java开发Web应用的底层技术。由Sun公司于1996年发布,用来代替CGI——当时生成Web动态内容的主流技术。官方文档对Servlet的概述,请参考《Servlet的概述》。 2、关键词 ...
  • 如何编写一个Servlet

    千次阅读 2019-10-07 21:54:54
    Servlet 1.什么是servlet servlet本身就是一种java类,这种java类可以提供web形式的访问(Java EE 规范) 2.怎么按照JavaEE的规范编写一个servlet 关键字 作用 说明 Servlet 接口 有五个抽象...
  • 3. Servlet容器 ① Servlet容器的作用 ② Servlet容器的工作过程 ③ ServletServlet的工作过程 ⑤ Servlet变化过程 4. Spring容器 5. SpringMVC容器 6. SpringBoot容器 二、容器相关知识归纳 1. 各种容器的管理...
  • servlet基础 package com.tomcat.servlet; import javax.servlet.*; import java.io.IOException; public class MyServlet2 implements Servlet { @Override public void init(ServletConfig servletConfig) ...
  • springBoot报错 Servlet.service() for servlet

    千次阅读 2020-10-29 21:10:10
    Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Circular view path ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,294,009
精华内容 517,603
关键字:

servlet