1.概述

在本文中,我们将研究Java Web开发的核心方面-Servlets。

2. Servlet和容器

简而言之,Servlet是处理请求,处理请求并通过处理请求返回响应的一个类。

例如,我们可以使用Servlet通过HTML表单收集来自用户的输入,从数据库中查询记录,并动态创建网页。

需要注意的是,我们并不能能直接的控制Servlet, Servlet受一个称为Servlet容器的Java应用程序的控制。

当在Web服务器上运行的应用程序收到请求时*,*服务器会将请求移交给Servlet容器,然后由Servlet容器将其传递给目标Servlet。

3.Maven依赖

要在我们的Web应用程序添加 javax.servlet-api支持。servlet-api依赖项是必需的:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

最新的Maven依赖关系可以在这里找到。

4. Servlet生命周期

​ 让我们看一下定义Servlet生命周期的方法集。

4.1 init()方法

init方法被设计为仅调用一次。如果servlet实例不存在,则Web容器:

  1. 加载servlet类
  2. 创建servlet类的实例
  3. 通过调用init方法初始化它

init方法成功完成之后,servlet可以接受任何请求。

如果init方法抛出ServletException或在Web服务器定义的规定时间内未完成,那么Servlet容器将不会通过这个Servlet处理请求。

public void init() throws ServletException {
    // 初始化
}

4.2. service()

仅在servlet的init方法成功完成之后才可以调用此方法*。*

Servlet容器调用service()方法来处理来自客户端的HTTP请求(GET,POST,PUT,DELETE等)。

public void service(ServletRequest request, ServletResponse response) 
  throws ServletException, IOException {
    // ...
}

4.3. destroy()

由Servlet容器调用以使Servlet终止服务。

仅当servlet的service方法中的所有线程都已退出或经过超时后,才能调用此方法。

容器调用此方法后,它将不再在Servlet上再次调用service方法。

public void destroy() {
    // 
}

5.Servlet示例

现在,让我们设置一个使用表单处理信息的完整示例。

首先,让我们定义一个带有/helloServlet映射的servlet ,它简单的返回一个字符串"Hello Servlet":

@WebServlet(urlPatterns = {"/helloServlet"})
public class HelloServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init servlet");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("service servlet");
        res.getWriter().print("Hello Servlet");
        res.getWriter().flush();
        res.getWriter().close();
    }

    @Override
    public void destroy() {
        System.out.println("destroy servlet");
    }
}

如上所示,用@WebServlet注解的类必须扩展javax.servlet.http.HttpServlet类。而且*@ WebServlet*注解仅从Java EE 6起可用。

*@WebServlet*注解由容器在部署时处理,并且指定的servlet在特定的URL的模式下可用。

值得注意的是,通过使用注解定义URL模式,我们可以避免在Servlet映射中使用名为web.xml的 XML部署描述符。

如果希望使用不带注解的Servlet,则可以改用传统的web.xml

<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">
 
    <servlet>
       <servlet-name>HelloServlet</servlet-name>
       <servlet-class>com.ripjava.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/helloServlet</url-pattern>
    </servlet-mapping>
 
</web-app>

最后,为确保一切正常,我们编写一个测试用例:

public class HelloServletTest {
    private Server server;

    @BeforeEach
    public  void startJetty() throws Exception
    {
        server = new Server(8080);
        server.setStopAtShutdown(true);

        ServletHandler handler = new ServletHandler();
        handler.addServletWithMapping(HelloServlet.class, "/helloServlet");
        server.setHandler(handler);
        server.start();
    }

    @Test
    public void test_HelloServlet() throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet request = new HttpGet("http://localhost:8080/helloServlet");
        CloseableHttpResponse response = httpClient.execute(request);


        HttpEntity entity = response.getEntity();

        assertEquals("Hello Servlet", EntityUtils.toString(entity));
    }

    @AfterEach
    public void tearDown() throws Exception {
        server.stop();
    }
}

这里我们使用Jetty作为Servlet容器。

我们执行测试后,控制台输出如下:

init servlet
service servlet
。。。
destroy servlet

6. Servlet,HttpServlet和JSP

重要的是要了解Servlet技术不限于HTTP协议。

Servlet是通用接口,而HttpServlet是该接口的扩展, 添加了HTTP特定的支持-如doGetdoPost等。

最后,Servlet技术也是许多其他Web技术的主要驱动程序,例如JSP(JavaServer Pages),Spring MVC等。

7.总结

在本文中,我们介绍了Java Web应用程序中Servlet的基础。

与往常一样,可以在GitHub上获得代码示例。