• Java EE之Servlet


    1.创建Servlet类

    Servlet在Java EE API规范中的定义:
    Servlet是一个运行在Web服务器中的Java小程序。Servlet将会接收和响应来自Web客户端的请求,使用HTTP(超文本传输协议)进行通信。
    Servlet是所有Web应用程序的核心类。
    运行应用程序的Web容器将会有一个或多个内建的Servlet。这些Servlet将用于处理JavaServer Pages、显示目录列表和访问静态资源。

    所有的Servlet都实现了javax.serlvet.Servlet接口,但通常不是直接实现的。Servlet只是一个简单接口,它包含了初始化并销毁Servlet和处理请求的方法。

    在大多数情况下,Servlet都继承自javax.servlet.GenericServlet。GenericServlet仍然是一个不依赖于具体协议的Servlet,它只包含了一个抽象的service方法。
    作为响应HTTP请求的java.servlet.http.HttpServlet,它继承了GenericServlet,并实现了只接受HTTP请求的service方法。然后,它提供了响应每种HTTP方法类型的方法的空实现。

    HttpServlet的方法接受的是javax.servlet.http.HttpServletRequestjavax.servlet.http.HttpServletResponse参数,而不是javax.servlet.ServletRequestjavax.servlet.ServletResponse,这样它就可以轻松访问Servlet服务所处理的请求中的HTTP特定的特性。

    package com.wrox;
    
    import javax.servlet.http.HttpServlet;
    
    public class HelloServlet extends HttpServlet
    {
    
    }
    //为了可以编译该代码,需要将Java EE Servlet API库添加到编译类路径上。
    

    任何未重写的HTTP Servlet方法都将返回一个HTTP状态405作为响应。如果一个Servlet不处理任何请求,当然它就是无用的。

    //重写doGet()方法
    package com.wrox;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @SuppressWarnings("serial")
    public class HelloServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
            //下面没有调用PringtWriter的close方法。一般来说,在Java中只需要关闭自己创建
            //的资源即可。Web容器创建了该资源,所以它也会负责关闭该资源。
            response.getWriter().println("Hello, World!");
        }
    }
    

    注意,不需要担心任何原生HTTP请求或响应的细节。Web容器将会处理请求的解释,并从套接字(socket)中读取请求头和参数。在Servlet的方法返回之后,Web容器还将格式化响应头和主体,并写回套接字中。
    使用初始化方法和销毁方法

        @Override
        public void init() throws ServletException
        {
            System.out.println("Servlet " + this.getServletName() + " has started.");
        }
    
        @Override
        public void destroy()
        {
            System.out.println("Servlet " + this.getServletName() + " has stopped.");
        }
    

    GenericServlet实现了javax.servlet.Servlet接口中的init(ServletConfig config)方法,所以不需要在自己的init方法实现中调用super.init(servletConfig)

    可以使用init方法读取属性文件,或者使用JDBC连接数据库。init方法将在Servlet启动时调用。
    如果将Servlet配置为在Web应用程序部署和启动时自动启动,那么它的init方法也将会被调用。否则,init方法将在第一次请求访问它接收的Servlet时调用。
    同样地,destroy在Servlet不再接受请求之后立即调用。这通常发生在Web应用程序被停止或卸载,或者Web容器关闭时。总是应该使用destroy方法清理Servlet持有的资源。

    2.配置可部署的Servlet

    部署描述符将指示Web容器如何部署应用程序。尤其是它定义了应用程序中所有的监听器、Servlet和过滤器,以及应用程序所应使用的设置。

    <?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_3_1.xsd"
             version="3.1">
    
        <display-name>Hello World Application</display-name>
    
        <servlet>
            <servlet-name>helloServlet</servlet-name>
            <servlet-class>com.wrox.HelloServlet</servlet-class>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>helloServlet</servlet-name>
            <url-pattern>/greeting</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    上面的配置中<servlet><servlet-mapping>标签内的<servlet-name>标签应该一致。Web容器通过这种方式关联这两个配置。
    可以将多个URL映射到相同的Servlet:

        <servlet-mapping>
            <servlet-name>helloServlet</servlet-name>
            <url-pattern>/greeting</url-pattern>
            <url-pattern>/salutation</url-pattern>
            <url-pattern>/wazzup</url-pattern>
        </servlet-mapping>
    

    下面的<load-on-startup>1</load-on-startup>指示Web容器在应用程序启动之后立即启动Servlet。如果多个Servlet配置都包含了该标签,它们将按照标签内的值的大小顺序启动。如果两个或多个Servlet的<load-on-startup>配置相同,那么将按照它们在描述符文件中的出现顺序启动。

        <servlet>
            <servlet-name>helloServlet</servlet-name>
            <servlet-class>com.wrox.HelloServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
    

    3.了解doGet、doPost和其他方法

    3.1 在service方法执行的过程中

    service方法的实现是非常复杂的,而且随着Web容器的不同,service方法的实现也会随之变化。
    扩展HttpServlet的优点在于我们不需要担心其中的任何细节问题。
    通过使用HttpServlet在各种不同方法中定义的HttpServletRequestHttpServletResponse参数,我们可以读取由客户端发送的参数、接受通过表单上传的文件、读取包含在请求正文中的原始数据、读取请求头和操作响应头,并将响应正文返回到客户端。

    3.2 使用HttpServletRequest

    HttpServletRequest接口是对ServletRequest的扩展。
    HttpServletRequest最重要的功能是从客户端发送的请求中获取参数,请求参数有两种不同的形式:

    • 查询参数(URI参数)
    • application/x-www-form-urlencodedmultipart/form-data编码的请求正文
    public interface ServletRequest {
    	//...
    	//getParameter方法将返回参数的单个值,如果参数有多个值,就返回第一个值。
    	public String getParameter(String name);
    	
    	//getParameterNames方法返回所有可用参数的名字的枚举。
    	public Enumeration<String> getParameterNames();
    	
    	//getParameterValues方法返回参数的值的数组,如果参数只有一个值,就返回只有一个元素的数组。
    	public String[] getParameterValues(String name);
    	
    	//getParameterMap方法返回一个包含了所有参数名值对的 java.util.Map<String, String[]>
    	public Map<String, String[]> getParameterMap();
    	//...
    }
    

    有几个方法可用于帮助决定HTTP请求内容的类型、长度和编码。
    方法getContentType将返回请求的MIME(多用途互联网邮件扩展)内容类型,例如 application/x-www-form-urlencodedapplication/jsontext/plainapplication/zip等。
    MIME内容类型描述了数据的类型。
    方法getContentLength返回请求正文的长度,以字节为单位。
    方法getCharacterEncoding将返回请求内容的字符编码。

    public interface HttpServletRequest extends ServletRequest {
    	//...	
    	//返回客户端用于创建请求的完整URL,包含协议(http或https)、服务器名称、端口号和服务器路径,但不包括查询字符串
    	public StringBuffer getRequestURL();
    	
    	//它只返回URL中的服务器路径部分
    	public String getRequestURI();
    	
    	//它只返回用于匹配Servlet映射的URL部分
    	public String getServletPath();
    	
    	//返回指定名字的头数据
    	public String getHeader(String name);
    	
    	//返回请求头中所有头数据的名字的枚举
    	public Enumeration<String> getHeaderNames();
    	
    	//如果有某个特定的头的值一直是数字,那么可以调用该方法返回一个数字。
    	public int getIntHeader(String name);
    	
    	//对于可以表示有效时间戳的头数据,该方法将返回一个Unix时间戳。
    	public long getDateHeader(String name);
    	//...
    }
    

    3.3 使用HttpServletResponse

    HttpServletResponse接口继承了ServletResponse,HttpServletResponse可以设置响应头、编写响应正文、重定向请求、设置HTTP状态码以及将Cookies返回到客户端等任务。
    方法getOutputStream将返回一个javax.servlet.ServletOutputStream,而方法getWriter将返回一个java.io.PrintWriter,通过它们都可以向响应中输出数据。
    在向响应正文中输出数据时,可能需要设置内容类型或编码格式。可以通过setContentTypesetCharacterEncoding方法进行设置。如果计划在使用getWriter时调用setContentTypesetCharacterEncoding,那么必须在getWriter之前调用setContentTypesetCharacterEncoding,因为这样的getWriter方法返回的writer才能获得正确的字符编码设置。在getWriter调用之后调用的setContentTypesetCharacterEncoding将被忽略。
    如果在调用getWriter之前未调用setContentTypesetCharacterEncoding,返回的writer将使用容器的默认编码。

    方法sendRedirect将客户端重定向至另一个URL。

    4.使用参数和接受表单提交

    @WebServlet(
            name = "helloServlet",
            urlPatterns = {"/greeting", "/salutation", "/wazzup"},
            loadOnStartup = 1
    )
    public class HelloServlet extends HttpServlet
    {
        private static final String DEFAULT_USER = "Guest";
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
        	//获取名为"user"的参数
            String user = request.getParameter("user");
            if(user == null)
                user = HelloServlet.DEFAULT_USER;
    
            //将响应的内容类型设置为text/html,将字符编码设置为UTF-8
            response.setContentType("text/html");
            response.setCharacterEncoding("UTF-8");
    
            //从响应中获得一个PrintWriter,并输出一个兼容于HTML5的文档
            PrintWriter writer = response.getWriter();
            writer.append("<!DOCTYPE html>
    ")
                  .append("<html>
    ")
                  .append("    <head>
    ")
                  .append("        <title>Hello User Application</title>
    ")
                  .append("    </head>
    ")
                  .append("    <body>
    ")
                  .append("        Hello, ").append(user).append("!<br/><br/>
    ")
                  .append("        <form action="greeting" method="POST">
    ")
                  .append("            Enter your name:<br/>
    ")
                  .append("            <input type="text" name="user"/><br/>
    ")
                  .append("            <input type="submit" value="Submit"/>
    ")
                  .append("        </form>
    ")
                  .append("    </body>
    ")
                  .append("</html>
    ");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
        	//接收表单提交,将请求委托给doGet方法。
            this.doGet(request, response);
        }
    
        //...
    

    接受多值参数的例子:

    @WebServlet(
            name = "multiValueParameterServlet",
            urlPatterns = {"/checkboxes"}
    )
    public class MultiValueParameterServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
            response.setContentType("text/html");
            response.setCharacterEncoding("UTF-8");
    
            PrintWriter writer = response.getWriter();
            writer.append("<!DOCTYPE html>
    ")
                  .append("<html>
    ")
                  .append("    <head>
    ")
                  .append("        <title>Hello User Application</title>
    ")
                  .append("    </head>
    ")
                  .append("    <body>
    ")
                  .append("        <form action="checkboxes" method="POST">
    ")
                  .append("Select the fruits you like to eat:<br/>
    ")
                  .append("<input type="checkbox" name="fruit" value="Banana"/>")
                  .append(" Banana<br/>
    ")
                  .append("<input type="checkbox" name="fruit" value="Apple"/>")
                  .append(" Apple<br/>
    ")
                  .append("<input type="checkbox" name="fruit" value="Orange"/>")
                  .append(" Orange<br/>
    ")
                  .append("<input type="checkbox" name="fruit" value="Guava"/>")
                  .append(" Guava<br/>
    ")
                  .append("<input type="checkbox" name="fruit" value="Kiwi"/>")
                  .append(" Kiwi<br/>
    ")
                  .append("<input type="submit" value="Submit"/>
    ")
                  .append("        </form>")
                  .append("    </body>
    ")
                  .append("</html>
    ");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
            String[] fruits = request.getParameterValues("fruit");
    
            response.setContentType("text/html");
            response.setCharacterEncoding("UTF-8");
    
            PrintWriter writer = response.getWriter();
            writer.append("<!DOCTYPE html>
    ")
                  .append("<html>
    ")
                  .append("    <head>
    ")
                  .append("        <title>Hello User Application</title>
    ")
                  .append("    </head>
    ")
                  .append("    <body>
    ")
                  .append("        <h2>Your Selections</h2>
    ");
    
            if(fruits == null)
                writer.append("        You did not select any fruits.
    ");
            else
            {
                writer.append("        <ul>
    ");
                for(String fruit : fruits)
                {
                    writer.append("        <li>").append(fruit).append("</li>
    ");
                }
                writer.append("        </ul>
    ");
            }
    
            writer.append("    </body>
    ")
                  .append("</html>
    ");
        }
    }
    

    5.使用初始化参数配置应用程序

    5.1 使用上下文初始化参数

    在部署描述符中添加上下文初始化参数:

        <context-param>
            <param-name>settingOne</param-name>
            <param-value>foo</param-value>
        </context-param>
        <context-param>
            <param-name>settingTwo</param-name>
            <param-value>bar</param-value>
        </context-param>
    

    在Servlet代码的任何地方都可以轻松获得和使用这些参数。

    @WebServlet(
            name = "contextParameterServlet",
            urlPatterns = {"/contextParameters"}
    )
    public class ContextParameterServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
            ServletContext c = this.getServletContext();
            PrintWriter writer = response.getWriter();
    
            writer.append("settingOne: ").append(c.getInitParameter("settingOne"))
                  .append(", settingTwo: ").append(c.getInitParameter("settingTwo"));
        }
    }
    

    应用程序中的所有Servlet都将共享这些初始化参数。在所有的Servlet中它们的值都是相同的。有时需要使某个设置只作用于某一个Servlet,那么就需要使用Servlet初始化参数。

    5.2 使用Servlet初始化参数

    public class ServletParameterServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException
        {
        	//从ServletConfig对象中获取初始化参数
            ServletConfig c = this.getServletConfig();
            PrintWriter writer = response.getWriter();
    
            writer.append("database: ").append(c.getInitParameter("database"))
                  .append(", server: ").append(c.getInitParameter("server"));
        }
    }
    

    部署描述符中的配置:

        <servlet>
            <servlet-name>servletParameterServlet</servlet-name>
            <servlet-class>com.wrox.ServletParameterServlet</servlet-class>
            <init-param>
                <param-name>database</param-name>
                <param-value>CustomerSupport</param-value>
            </init-param>
            <init-param>
                <param-name>server</param-name>
                <param-value>10.0.12.5</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>servletParameterServlet</servlet-name>
            <url-pattern>/servletParameters</url-pattern>
        </servlet-mapping>
    

    考虑到多线程安全的问题:永远不要在静态或实例变量中存储请求或响应对象。任何属于请求的对象和资源都只应该被用作本地变量和方法参数。

    参考:《Java Web高级编程》第3章

  • 相关阅读:
    Object的create、assign、getPrototypeOf与拷贝
    vue中使用axios最详细教程
    COJ1249(竞争性酶抑制剂和同工酶)
    COJ1127(芝麻开门)
    COJ1219(建食堂)
    COJ1236(删数游戏)
    COJ1247(有髓鞘神经纤维动作电位传导)
    POJ1159(Palindrome)
    POJ1080(Human Gene Functions)
    Uva10034(Freckles)
  • 原文地址:https://www.cnblogs.com/gzhjj/p/9132380.html
Copyright © 2020-2023  润新知