完整的Java Servlet教程

Servlet是符合Java Servlet API的Java类,该Java Servlet API允许Java类响应请求。尽管servlet可以响应任何类型的请求,但最常见的是将它们编写为响应基于Web的请求。必须将servlet部署到Java servlet容器中才能使用。尽管许多开发人员使用诸如Java Server Pages(JSP)Java Server Faces(JSF)之类的servlet框架,但这两种技术都通过servlet容器将页面编译为后台的Java servlet。也就是说,Java Servlet技术基础知识对于任何Java Web开发人员都可能非常有用。

在本教程中,我们将涵盖以下主题,以全面了解Java Servlet技术。

目录

编写您的第一个Servlet 
Servlet生命周期方法
用@WebServlet注解开发Servlet将Servlet 
打包并将其部署到Tomcat Server中
在Servlet响应中编写动态内容
处理Servlet请求和响应
侦听Servlet容器事件
通过Servlet初始化参数
为特定的URL请求添加Servlet过滤器
下载a使用Servlet的二进制文件使用
RequestDispatcher.forward()
将请求
转发到另一个Servlet使用HttpServletResponse.sendRedirect()将请求重定向到另一个Servlet使用Servlet 编写和读取Cookie

让我们开始逐步了解servlet。

编写您的第一个Servlet

我们的第一个servlet是非常简单的servlet,它具有很少的代码,因此您只能专注于重要的事情。

package com.how2codex.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyFirstServlet extends HttpServlet {
    private static final long serialVersionUID = -1915463532411657451L;
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
    {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            // Write some content
            out.println("<html>");
            out.println("<head>");
            out.println("<title>MyFirstServlet</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h2>Servlet MyFirstServlet at " + request.getContextPath() + "</h2>");
            out.println("</body>");
            out.println("</html>");
        } finally {
            out.close();
        }
    }
    
    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        //Do some other work
    }
    @Override
    public String getServletInfo() {
        return "MyFirstServlet";
    }
}

要在Web容器上方注册上述servlet,您将为您的应用程序创建一个入口web.xml文件。

<?xml version="1.0"?>
<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
            version="3.0">
            
    <welcome-file-list>
        <welcome-file>/MyFirstServlet</welcome-file>
    </welcome-file-list>
    
    <servlet>
        <servlet-name>MyFirstServlet</servlet-name>
        <servlet-class>com.how2codex.servlets.MyFirstServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyFirstServlet</servlet-name>
        <url-pattern>/MyFirstServlet</url-pattern>
    </servlet-mapping>
    
</web-app>

Servlet上面没有做一些重要的事情,您可能需要学习。

  1. MyFirstServlet扩展了HttpServlet。这是强制性的,因为所有servlet必须是扩展的通用servlet javax.servlet.GenericServlet或扩展的HTTP servlet javax.servlet.http.HttpServlet
  2. 覆盖doGet()doPost()方法。这些方法在HttpServlet类中定义。每当GET或POST请求到来时,都会将其映射到其各自的方法,例如,如果您发送了
  3. 对此Servlet发出HTTP GET请求,然后doGet()调用方法。
    还有一些其他有用的方法,您可以重写它们以在运行时控制应用程序,例如getServletInfo()
  4. HttpServletRequest并且HttpServletResponse是所有doXXX()方法的默认参数。我们将在后面的部分中了解有关这些对象的更多信息。

这就是您应该知道的简单servlet的全部内容。

Servlet生命周期方法

无论何时在您的应用程序中,都会加载并使用一个servlet。在该Servlet的初始化和销毁​​过程中发生了一系列事件。这些称为servlet的生命周期事件(或方法)。让我们详细了解它们。

三种方法对于Servlet的生命周期至关重要。这些init()service()destroy()。它们由每个servlet实现,并在运行时在特定时间调用。

1)在Servlet生命周期的初始化阶段,Web容器通过调用init()方法并传递实现javax.servlet.ServletConfig接口的对象来初始化Servlet实例。此配置对象允许Servlet访问Web应用程序的web.xml文件中定义的名称-值初始化参数。这是在servlet实例的生命周期只调用一次

初始化方法的定义如下所示:

public void  init() throws ServletException {
    //custom initialization code
}

2)初始化后,Servlet实例可以为客户端请求提供服务。在Web容器调用这个servlet为每个请求的service()方法。该service()方法确定发出的请求的类型,并将其分派给适当的方法以处理该请求。Servlet的开发人员必须提供这些方法的实现。如果对不是由Servlet实现的方法提出了请求,则将调用父类的方法,通常会导致将错误返回给请求者。

几乎在所有情况下都无需覆盖此方法。

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
    long lastModified = getLastModified(req);
    if (lastModified == -1) {
    // servlet doesn't support if-modified-since, no reason
    // to go through further expensive logic
    doGet(req, resp);
    } else {
    long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
    if (ifModifiedSince < (lastModified / 1000 * 1000)) {
        // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
        maybeSetLastModified(resp, lastModified);
        doGet(req, resp);
    } else {
        resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
    }
    }
} else if (method.equals(METHOD_HEAD)) {
    long lastModified = getLastModified(req);
    maybeSetLastModified(resp, lastModified);
    doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
    doPost(req, resp);
    
} else if (method.equals(METHOD_PUT)) {
    doPut(req, resp);  
    
} else if (method.equals(METHOD_DELETE)) {
    doDelete(req, resp);
    
} else if (method.equals(METHOD_OPTIONS)) {
    doOptions(req,resp);
    
} else if (method.equals(METHOD_TRACE)) {
    doTrace(req,resp);
    
} else {
    //
    // Note that this means NO servlet supports whatever
    // method was requested, anywhere on this server.
    //
    String errMsg = lStrings.getString("http.method_not_implemented");
    Object[] errArgs = new Object[1];
    errArgs[0] = method;
    errMsg = MessageFormat.format(errMsg, errArgs);
    
    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

3)最后,Web容器调用destroy()方法,该方法会使Servlet退出服务。如果要在Servlet超出范围之前关闭或销毁某些文件系统或网络资源,则应调用此方法。destroy()与init()一样,该方法在servlet的生命周期中仅被调用一次。

public void destroy() {
    //
}

通常,在大多数情况下,您不需要在servlet中覆盖它们中的任何一个。

阅读更多: Web服务器如何工作?

使用@WebServlet注解开发Servlet

如果您不太喜欢xml配置,而是特别喜欢注解,那么Servlets API也可以。您可以使用@WebServlet以下示例中的注解,然后无需在web.xml中进行任何输入。容器将自动将您的servlet注册到运行时,并照常进行处理

package com.how2codex.servlets;
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(name = "MyFirstServlet", urlPatterns = {"/MyFirstServlet"})
public class MyFirstServlet extends HttpServlet {
    private static final long serialVersionUID = -1915463532411657451L;
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
    {
        //Do some work
    }
    
    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        //Do some other work
    }
}

将Servlet打包和部署到Tomcat服务器

如果您正在使用任何IDE(例如eclipse),则打包和部署应用程序只是一个步骤。Right click on project > Run As > Run As Server。如果尚未配置服务器,则准备好进行滚动。

如果您不使用任何IDE,那么您需要做一些额外的工作,例如从命令提示符下编译应用程序,使用ANT创建war文件等。但是我非常有信心今天的每个人都使用一些IDE进行开发,因此我将不要在本节中浪费更多时间。

当您在tomcat中部署我们的第一个servlet并在浏览器中访问URL“ http:// localhost:8080 / servletexamples / MyFirstServlet ”时,您将获得以下响应。

servlet示例

在Servlet响应中编写动态内容

Java servlet之所以如此有用的原因之一是因为它们允许将动态内容显示在网页上。内容可以取自服务器本身,数据库,另一个网站或许多其他可从Web访问的资源。Servlet不是静态网页。他们充满活力,可以说是他们最大的优势。

让我们以一个servlet为例,该servlet负责向用户显示当前日期和时间,以及其名称和一些自定义消息。让我们为其编写代码。

package com.how2codex.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
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(name = "CalendarServlet", urlPatterns = {"/CalendarServlet"})
public class CalendarServlet extends HttpServlet {
    private static final long serialVersionUID = -1915463532411657451L;
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
    {
        
        Map<String,String> data = getData();
        
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            // Write some content
            out.println("<html>");
            out.println("<head>");
            out.println("<title>CalendarServlet</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h2>Hello " + data.get("username") + ", " + data.get("message") + "</h2>");
            out.println("<h2>The time right now is : " + new Date() + "</h2>");
            out.println("</body>");
            out.println("</html>");
        } finally {
            out.close();
        }
    }
    
    //This method will access some external system as database to get user name, and his personalized message
    private Map<String, String> getData()
    {
        Map<String, String> data = new HashMap<String, String>();
        data.put("username""Guest");
        data.put("message",  "Welcome to my world !!");
        return data;
    }
}

当您在tomcat中的servlet上方运行并在浏览器中单击 URL“ http:// localhost:8080 / servletexamples / CalendarServlet ”时,将得到以下响应。

servlet中的动态内容

处理Servlet请求和响应

Servlet使创建符合请求和响应生命周期的Web应用程序变得容易。它们具有提供HTTP响应的能力,并且还可以在同一代码体内处理业务逻辑。处理业务逻辑的能力使servlet比标准HTML代码强大得多。

在实际的应用程序中,HTML Web表单包含发送到Servlet的参数。然后,该Servlet以某种方式处理这些参数,并发布客户端可以看到的响应。对于HttpServlet对象,客户端是Web浏览器,响应是Web页面。<form>操作属性指出应使用哪些属性来处理表单中包含的值。

要获取请求参数,请调用HttpServletRequest对象的getParameter()方法,并传递要获取的输入参数的ID。

String value1 = req.getParameter("param1");
String value1 = req.getParameter("param2");

一旦获得值,就可以根据需要对其进行处理。然后,如上节所述,为客户准备响应。使用HttpServletResponse对象将该响应发送回客户端。

请求和响应处理的基本用法可以这样完成:

@Override
protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException
{
    
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    
    boolean success = validateUser(username, password);
    
    try {
        // Write some content
        out.println("<html>");
        out.println("<head>");
        out.println("<title>LoginServlet</title>");
        out.println("</head>");
        out.println("<body>");
        if(success) {
            out.println("<h2>Welcome Friend</h2>");
        }else{
            out.println("<h2>Validate your self again.</h2>");
        }
        
        out.println("</body>");
        out.println("</html>");
    } finally {
        out.close();
    }
}

要发送内容,您将必须使用PrintWriter从获取的对象HttpServletResponse。写入其中的任何内容都将写入 OutputStream ,并且数据将发送回客户端。

侦听Servlet容器事件

有时了解某些事件在应用程序服务器容器中何时发生很有用。此概念在许多不同的情况下都可能有用,但是最常用于启动时初始化应用程序或关闭后清除应用程序。Servlet侦听器可以向应用程序注册,以指示何时启动或关闭它。因此,通过侦听此类事件,servlet可以在它们发生时执行一些操作。

若要创建一个基于容器事件执行操作的侦听器,必须开发一个实现该ServletContextListener接口的类。需要实现的方法是contextInitialized()contextDestroyed()。这两种方法都接受a ServletContextEvent作为参数,并且每次分别初始化或关闭servlet容器时,都会分别自动调用它们。

若要向容器注册侦听器,可以使用以下技术之一:

1)利用@WebListener注解。
2)在web.xml应用程序部署描述符中注册侦听器。
3)使用上addListener()定义的方法ServletContext

请注意,ServletContextListener它不是Servlet API中的唯一列表器。还有更多例如

  • javax.servlet.ServletRequestListener
  • javax.servlet.ServletRequestAttrbiteListener
  • javax.servlet.ServletContextListener
  • javax.servlet.ServletContextAttributeListener
  • javax.servlet.HttpSessionListener
  • javax.servlet.HttpSessionAttributeListener

可以由您的列表器类根据您想听所有事件的选择来实现它们;例如,HttpSessionListener每次创建或销毁新用户会话时,都会收到通知。

传递Servlet初始化参数

当今的大多数应用程序都需要设置一些配置参数,您可以在应用程序/控制器启动时将其传递给它们。Servlet还可以接收初始化参数,在为第一个请求提供服务之前,它们可以用来完全构造它们。

显然,您可以在servlet本身中对配置值进行硬编码,但是更改其中的任何一个将需要您重新重新编译整个应用程序,而且没人愿意这样做。

<web-app>
    <servlet>
        <servlet-name>SimpleServlet</servlet-name>
        <servlet-class>com.how2codex.servlets.SimpleServlet</servlet-class>
        
        <!-- Servlet init param -->
        <init-param>
            <param-name>name</param-name>
            <param-value>value</param-value>
        </init-param>
    </servlet>
</web-app>

设置后,可以通过调用getServletConfig().getInitializationParameter()并传递参数名称在代码内使用参数,如以下代码行所示:

String value = getServletConfig().getInitParameter("name");

为特定的URL请求添加Servlet过滤器

Web过滤器对于预处理请求和访问给定URL时调用某些功能很有用。与其直接调用在给定URL上存在的servlet,否则将在servlet之前调用任何包含相同URL模式的过滤器。这在许多情况下可能是有用的,这对于执行日志记录,身份验证或在没有用户交互的情况下在后台进行的其他服务而言可能是最有用的。

筛选器必须实现该javax.servlet.Filter接口。此接口中包含的方法包括init(),destroy()和doFilter()。容器调用init()和destroy()方法。doFilter()方法用于实现过滤器类的任务。如果要链接过滤器,或者给定URL模式存在多个过滤器,则将按照在web.xml部署描述符中对其进行配置的顺序来调用它们。

要将web.xml文件配置为包括过滤器,请使用<filter>和<filter-mapping> XML元素及其关联的子元素标记。

<filter>
    <filter-name>LoggingFilter</filter-name>
    <filter-class>LoggingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LogingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
如果要使用注解为特定的servlet配置过滤器,则可以使用@WebFilter注解。

使用Servlet下载二进制文件

对于几乎所有Web应用程序来说,下载文件都是一项基本任务。要下载文件,servlet必须提供与要下载的文件相匹配的相同类型的响应。它还必须在响应标头中指出要包含附件,如下所示。

String mimeType = context.getMimeType( fileToDownload );
response.setContentType( mimeType != null ? mimeType : "text/plain" );
response.setHeader( "Content-Disposition""attachment; filename=\"" + fileToDownload + "\"" );

您可以通过调用ServletContext.getResourceAsStream()方法并传递文件路径来获取对要下载(存储在文件系统中)文件的引用。这将返回一个InputStream可用于读取文件内容的对象。然后创建一个字节缓冲区,该缓冲区将在读取文件时用于从文件中获取数据块。最后的实际任务是读取文件内容并将其复制到 OutputStream 。这是使用while循环完成的,它将继续从from读取数据,InputStream直到处理完所有内容为止。使用循环将数据块读入并写入 OutputStream 。此后,将ServletOutputStream调用对象的flush方法以清除内容并释放资源。

让我们看一下示例代码

private void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileToDownload) throws IOException
    {
        final int BYTES = 1024;
        int length = 0;
        
        ServletOutputStream outStream = response.getOutputStream();
        ServletContext context = getServletConfig().getServletContext();
        String mimeType = context.getMimeType( fileToDownload );
        response.setContentType( mimeType != null ? mimeType : "text/plain" );
        response.setHeader( "Content-Disposition""attachment; filename=\"" + fileToDownload + "\"" );
        InputStream in = context.getResourceAsStream("/" + fileToDownload);
        
        byte[] bbuf = new byte[BYTES];
        while ((in != null) && ((length = in.read(bbuf)) != -1)) {
            outStream.write(bbuf, 0, length);
        }
        outStream.flush();
        outStream.close();
    }

使用RequestDispatcher.forward()将请求转发到另一个servlet

有时,您的应用程序要求servlet应该将请求移交给其他servlet,以完成需要完成的任务。此外,应在不将客户端重定向到另一个URL的情况下移交请求,即浏览器中的URL不应更改。

这样做的功能就内置在中ServletContext,因此一旦获得对的引用ServletContext,则只需调用该getRequestDispatcher()方法即可获取可用于分派请求的RequestDispatcher对象。调用该getRequestDispatcher()方法时,传递一个String,其中包含要将您的请求传递到的servlet的名称。RequestDispatcher获取对象后,通过将HttpServletRequestHttpServletResponse对象传递给它来调用其前向方法。转发方法执行移交请求的任务。

RequestDispatcher rd = servletContext.getRequestDispatcher("/NextServlet");
rd.forward(request, response);

使用HttpServletResponse.sendRedirect()将请求重定向到另一个servlet

尽管在某些情况下,您不希望像上一节中所看到的那样通知用户servlet重定向已发生,但是在某些情况下,我们实际上希望发生这种情况。当您访问应用程序中的特定URL时,您想将浏览器重定向到另一个URL。

为此,您将需要调用HttpServletResponseobject的sendRedirect()方法。

httpServletResponse.sendRedirect("/anotherURL");
与servlet链接相反,这种简单的重定向不会将HttpRequest对象传递到目标地址。

使用Servlet编写和读取Cookie

许多应用程序希望将用户浏览历史记录的当前状态存储在客户端计算机中,以便当用户再次返回到应用程序时,他从离开的地方开始。通常为此要求使用cookie。您可以看到cookie作为存储在客户端计算机上的基于键值对的数据。当在浏览器中访问应用程序时,应用程序将能够读取或写入这些值。

要创建cookie,只需实例化一个新javax.servlet.http.Cookie对象并为其分配名称和值即可。一旦实例化了cookie,就可以设置将有助于配置cookie的属性。在此食谱的示例中,调用了cookie setMaxAge()setHttpOnly()方法,设置了cookie的生存时间,并确保防止cookie受到客户端脚本的影响。

从Servlet 3.0 API开始,将cookie标记为仅HTTP的功能已变得可用。这样可以保护cookie免受客户端脚本攻击,从而使cookie更加安全。
Cookie cookie = new Cookie("sessionId""123456789");
cookie.setHttpOnly(true);
cookie.setMaxAge(-30);
response.addCookie(cookie);

这里的响应是HttpServletResponse传递给doXXX()方法的实例。

要回读服务器父项上的cookie信息,请使用以下代码:

Cookie[] cookies = request.getCookies();
for(Cookie cookie : cookies)
{
    //cookie.getName();
    //cookie.getValue()
}

这就是有关servlets技术的本教程的全部内容。随意删除评论/反馈。

 

saigon has written 1445 articles

Leave a Reply