
第1章
Java EE基础
本章目标
1. 理解Java EE容器和组件的概念
2. 理解Web项目文件和目录结构
3. 掌握Servlet的应用
Java EE(Java Platform Enterprise Edition)是一种企业级的Java版本,我们可以通过它构建企业级应用。严格讲,它现在官方的名字应该是Jakarta EE(Jakarta Enterprise Edition),不过在现实中,这个名字似乎没有流行起来,当前还是普遍称其为Java EE,所以本书依旧使用Java EE这个名称。
1.1 Java EE概述
Java EE是SUN公司提出来的企业版Java开发中间件,它主要用于企业级互联网系统的搭建。Java语言凭借其平台无关性、可移植性、健壮性、支持多线程和安全性等优势迅速成为构建企业互联网平台的主流技术。随着Java EE被广泛使用,也衍生出了许多优秀的框架,比如当前最流行的Spring框架,还有Struts、Hibernate和MyBatis等,使得Java EE的开发更加简单和快速。
Java EE的本质是一种容器加组件技术,这句话里包含了两个概念——容器和组件。容器是用来管理组件行为的一个集合工具,组件的行为包括与外部环境的交互、组件的生命周期、组件之间的合作依赖关系和运行等。组件是开发者编写或者引入的第三方程序代码,只要开发者按照容器所定义的规范开发组件,组件就可以在容器中运行了。Java EE中的主要组件包括JSP、Servlet和EJB(Enterprise Java Bean)等,主要的开发语言是Java。容器和组件的关系如图1-1所示。

图1-1 容器和组件的关系
1.1.1 Java EE容器
Java EE容器不是需要用户开发的内容,它是依照Java EE规范提供的集合工具,只要满足Java EE规范的组件都可以在该容器中运行。从这句话中,可以知道组件是用户开发的,而容器是某个公司、组织或者个人依照Java EE规范提供的。Java EE容器可以分为Web容器、EJB容器和应用容器,它们都依照Java EE制定的规范实现。下面来了解这三种容器。
• Web容器:它包含一个Servlet容器,Servlet容器可以运行Java EE的核心组件Servlet,而实际上JSP最终会被Web容器翻译为Servlet,再通过Servlet容器运行。此外,Web容器还可以运行HTML等文件。实现Web容器规范的服务器有多种,如Tomcat、Jetty、Wildfly(前身为JBoss)和GlassFish等。
• EJB容器:它是Java EE提出的一个企业级Java Bean的规范,能在它内部运行的组件是EJB,但是请注意,在默认情况下,Tomcat只提供Web容器,不提供EJB容器,所以在Tomcat中无法运行EJB,除非引入插件。Wildfly和GlassFish等服务器则提供了EJB容器,可以在它们当中运行EJB。但是EJB存在诸多的问题,当前已经被大部分企业抛弃,基于实用原则,本书不再讨论它。
• 其他Java应用容器:解决某类问题的一些厂商提供的容器,比如Java NIO,它是一种支持字节组件的容器。
从客观的角度来看,在企业应用中,Web容器是使用最广泛的容器,本书主要讨论这个容器。当前,EJB已经没有太大的讨论价值,其他的Java容器,虽然有比较广泛的应用,但是都是解决某一问题的,针对性较强。后续我们会以Tomcat为例讨论Web容器,其实Jetty、Wildfly和GlassFish都是类似的,因为它们都遵循Java EE容器的规范。
1.1.2 Java EE组件
Java EE组件是运行在Java EE容器中的程序片段,该程序以Java为主要的开发语言,可以和Java的其他技术融合。在不同的Java EE容器中存在不同的组件,比如Web容器和EJB容器中的组件是不同的。在Web容器中,主要的组件是Servlet和JSP,由于当前在企业中流行前端和后端分离,所以JSP技术已经走向了被淘汰的边缘,而Servlet是我们的研究重点,从本质来说,可以认为JSP也是一种Servlet技术,因为Web容器会先将JSP翻译为Servlet,然后去执行。而EJB容器中主要的组件是EJB,它们又可以分为会话Bean、实体Bean和消息驱动Bean,只是它已经是被淘汰的技术,所以本书不再讨论。其他Java应用容器的组件,则需要根据用户需要引入。
1.2 开发环境简介
为了更好地进行开发,本书在介绍Spring微服务之前会选用Eclipse作为开发环境,在介绍Spring微服务时会选用IntelliJ IDEA作为开发环境,它们都是现今最为流行的IDE。不过在此之前我们需要先介绍一下Tomcat、Maven和Java EE的Web项目。
1.2.1 Tomcat简介
Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心部分,由Apache、Sun和其他公司及个人共同开发。
Tomcat支持HTTP,并且支持Web容器规划的实现,它支持HTTP技术,如HTML、CSS等,同时支持JSP和Servlet等Java EE技术。我们可以通过访问Apache官方网站,下载Tomcat,笔者的计算机系统是Windows 10的64位版本,因此选择的Tomcat是64位的Tomcat 9.0.30。下载并解压缩后,就准备好了Tomcat。
为了启动Tomcat,我们需要配置好JDK环境变量(JAVA_HOME和PATH,这比较简单,网上也有很多教程,这里就不再讨论了)。接着可以打开Tomcat目录下的bin文件夹,如果在Windows环境下可以看到startup.bat文件,则双击它启动Tomcat。启动完成后,在浏览器输入访问地址http://localhost:8080,可以看到图1-2所示的页面。

图1-2 Tomcat控制台
当看到这个页面时,说明Tomcat已经启动。这里有必要研究一下Tomcat目录下的各个文件夹的作用,首先看它下面有哪些文件夹,如图1-3所示。

图1-3 Tomcat目录下的文件夹
下面,我们通过表1-1进行说明。
表1-1 Tomcat目录下的文件夹说明

Tomcat的编码经常需要修改,在默认情况下,如果使用Windows系统,则可以在Tomcat的控制台中看到日志存在很多乱码,可以打开conf文件夹下的配置文件logging.properites,然后修改其中的配置项,如下:

这样重启Tomcat就不会有乱码了。在运行过程中,我们还可以设置Tomcat的运行编码,打开conf目录下的server.xml文件,找到<Connector>元素进行修改,如下:

这样可以让Tomcat在UTF-8的环境下运行,当然也可以根据需要配置为GB2312等。
1.2.2 Maven
Maven是一种常见的构建工具,它是可以通过一小段描述信息进行项目的构建、报告和文档管理的工具软件。我们首先从Apache网站上下载Maven压缩包,然后解压缩到本地,接下来设置环境变量MAVEN_HOME指向Maven目录,最后在环境变量PATH中添加Maven的bin目录。在默认情况下,Maven会根据依赖从外国网站上下载对应的依赖包,这个过程会十分缓慢。为了改变这种情况,可以在Maven目录的conf文件下找到配置文件setting.xml,再找到<mirrors>元素,这是一个添加镜像的元素,在它下面添加子元素,内容如下:

将Maven的下载路径指向阿里巴巴的镜像后,Maven就会去国内的阿里巴巴的镜像下载对应的依赖包,从而大大加快项目的构建速度。我们可以使用Eclipse或者IntelliJ IDEA构建Maven项目或者模块,在此之前,最好将Maven的用户设置指向修改过的setting.xml文件。这样就可以构建Maven项目了,注意,选择骨架(maven-archetypes-webapp)构建项目,会更方便我们构建Web项目。
1.2.3 Web项目结构
这里笔者用Eclipse构建Maven的Web项目chapter1,然后观察Web项目的目录,如图1-4所示。

图1-4 Maven Web项目的目录
这里的/src/main目录下有三个文件夹:java、resources和webapp。其中java文件夹主要放置我们开发的Java类;resources文件夹存放各种配置文件;webapp文件夹主要放置Web项目所需要的各类文件,如HTML、JSP和JavaScript文件等。/src/test目录下的java文件夹则主要放置测试类。这里需要对/src/main目录下的webapp文件夹进行进一步说明,从图1-4中可以看到在WEB-INF文件夹下存在一个web.xml文件,这是一个Java EE Web项目的配置文件,只是在新的Servlet 3.0以后的容器规范下,它不是必需的,而index.jsp是IDE为我们构建的一个JSP样例。
1.2.4 Web项目发布包
这里需要研究一下Web项目打包后的结构。我们可以使用IDE将项目打包为war文件,然后解压缩它,其目录结构如图1-5所示。

图1-5 Web项目包目录结构
在图1-5中,MANIFEST.MF文件是一个说明文档,一般不常用。WEB-INF文件夹是核心,它下面还有classes和lib两个文件夹,classes是Web项目开发的Java文件,是经过编译生成的class文件,lib文件夹放置第三方依赖包,web.xml是Web项目的配置文件。index.jsp则是一个JSP页面,是默认的欢迎页。把chapter1整个复制到Tomcat的webapps目录下,就可以发布项目了。将war包直接放到Tomcat的webapps目录下也可以发布项目,一般来说,这样的方式适合在项目正式发布的时候使用。
1.3 Web容器的组件——Servlet
上述,我们通过Tomcat来获取Web容器,而Web容器包含Servlet容器,那么我们肯定想在Tomcat中运行我们开发的程序。在Servlet容器中,Servlet是最基础的组件,这里并没有讨论JSP(Java Server Page),这是因为严格来说,也可以把JSP当作Servlet,JSP存在的意义只是方便我们编写动态页面,使Java语言能和HTML相互结合。现在已经很少直接使用JSP了,前后端分离已经成为大势,基于实用原则,本书就不再介绍JSP页面。本节的主要内容是Servlet的应用,它是理解Java EE的核心内容,也是我们学习的核心内容。按照Servlet 3.0以后的规范,组件都可以使用注解来配置,而不必使用web.xml配置,随着Spring Boot的流行,使用注解开发的方式成为主流,因此本章也会以注解为主,配置为辅介绍Servlet的应用。注意,Servlet是运行在Servlet容器中的组件,而Tomcat实现的Web容器包含Servlet容器。
1.3.1 Servlet入门实例
我们暂时不讨论Servlet中复杂的内容,先通过实例让大家对Servlet有一个基本的认识,开发Servlet一般需要继承HttpServlet这个抽象类,实现一个服务方法。为此编写代码清单1-1,定义MyServlet。
代码清单1-1:自定义Servlet——MyServlet

注意标识在类上面的@WebServlet,它标识这个类是Servlet,将会被Servlet容器识别为Servlet,配置Servlet的名称和拦截的路径为/my。MyServlet则继承了HttpServlet,且覆盖了它定义的doGet方法。在IDE配置好Tomcat后,就可以运行项目了。在浏览器中访问http://localhost:8080/chapter1/my,可以看到响应的内容,如图1-6所示。

图1-6 MyServlet的响应内容
图1-6说明MyServlet已经装配到Servlet容器中。这里首先需要认识MyServlet中doGet方法的两个参数:
• 请求参数(HttpServletRequest):它代表请求,可以从请求中获取对应的参数和与之相关的信息。
• 响应参数(HttpServletResponse):它代表如何响应用户的请求,我们可以通过它写入响应信息,也可以设置应答的类型和其他相关的信息,比如图1-6中的Hello Servlet就是通过它写入的响应信息。
关于这两个参数,在本章后续还会介绍,这里只需要有个印象。本章介绍的MyServlet比较简单,还远远没有达到应用的层级,为此我们有必要进行更深入的学习。
1.3.2 Servlet的生命周期
Servlet的生命周期是Servlet学习的核心内容之一,也是初学者常常犯糊涂的地方。Servlet是Servlet容器中的一个接口,在它的基础上,还会定义GenericServlet和HttpServlet两个抽象类,它们的关系如图1-7所示。

图1-7 Servlet相关接口和类
这里的GenericServlet和HttpServlet就是我们关注的核心类,它们都是抽象类,定义了许多重要的方法,代码清单1-2展示了HttpServlet的部分重要方法。
代码清单1-2:HttpServlet的重要方法节选

这里的方法分为三大类:一是初始化方法,分别是两个init方法,其中一个带参数,另一个不带参数;二是服务方法,这是Servlet的核心方法,包括service、doGet和doPost方法;三是销毁方法,也就是destroy方法。这里的方法在注释中都有清晰的说明,请自行参考。
这里需要注意,Servlet容器是如何初始化和使用Servlet提供服务的,以及这些方法的调用顺序等问题,这些都是Servlet的核心内容,请大家务必掌握好。Servlet的请求响应过程如图1-8所示。

图1-8 Servlet的请求响应过程
注意第一次请求和其他请求之间的不同。在第一次请求时,Servlet容器会构建Servlet实例,然后调用其初始化的init方法,接着调用服务方法(比如doGet、doPost等),从而响应请求。第二次和之后的请求则是直接调用服务方法,不会再调用init方法初始化Servlet。可见在这个过程中,Servlet只有一个实例,而不是多个实例,而init方法也只会调用一次。那么,销毁(destroy)方法又是如何呢?让我们看图1-9。

图1-9 Servlet的销毁方法
销毁方法会在Servlet容器正常关闭或者在Servlet实例超时的时候被调用,这样在Servlet容器中就会销毁Servlet的实例了。
上述是Servlet的生命周期,初学者必须掌握好,有时候可以通过配置进行改变。为了掌握好Servlet的生命周期,我们修改MyServlet的代码进行测试,如代码清单1-3所示。
代码清单1-3:测试Servlet生命周期

这里覆盖了init、doGet和destroy方法,并且输入了内容,以方便我们监测,重启项目,按照以下步骤测试。
在浏览器中访问http://localhost:8080/chapter1/my,可以看到日志打出:

这说明Servlet容器开始初始化MyServlet,并第一次执行doGet方法。接着刷新请求两次,可以看到日志打出:

可见init方法并未被调用,而是直接调用了doGet方法。最后正常关闭Tomcat,可以看到日志打出:

可见在Servlet容器正常关闭的时候,才会调用destroy方法销毁Servlet实例。而整个过程是按照图1-8和图1-9执行的。此外我们可以使用代码清单1-2中带有参数的init方法传递参数,我们需要学习Servlet的参数配置等内容,如代码清单1-4所示。
代码清单1-4:配置Servlet


请注意代码中@WebServlet配置项的使用:其中,asyncSupported的默认值为false,代表不使用异步线程运行Servlet,这里配置为true,代表支持多线程异步;配置项loadOnStartup设置为1,如果这里设置为大于0,那么Servlet实例会在启动项目时就初始化到Servlet容器中,而不是在第一次请求时才初始化;配置项initParams设置Servlet的参数。这里编写了带有参数的init方法,这个方法中的参数类型为ServletConfig,它代表Servlet的配置,在方法中,还获取了这些参数。重启Tomcat服务器,无须对MyServlet进行请求,就可以看到init方法的调用和参数的打印了。
1.3.3 HttpServletRequest的应用
应该说,HttpServletRequest和HttpServletResponse这两个类在Servlet的开发中是最常用的。对于HttpServletRequest类,我们一般称之为请求对象,为了学习它的使用方法,这里构建一个RequesServlet,其内容如代码清单1-5所示。
代码清单1-5:HttpServletRequest的使用


这里的代码分为7段:第①段是获取请求基本信息;第②段是获取请求头;第③段是获取请求参数;第④段是获取请求上下文,并设置上下文属性;第⑤段是设置请求属性;第⑥段是设置Session属性;第⑦段是跳转到JSP页面。每一段的注释都有清晰的说明,请自行参考。这段代码涉及JSP页面中三个重要内置对象的使用:request、session和application,在使用它们的时候需要特别注意的是作用域。request请求对象的作用域是当次用户请求有效;session的作用域是浏览器和服务器会话期间有效;而application的作用域是Web项目在Servlet容器中存活期间有效。其中,session是服务器在和浏览器通信期间为了保存会话数据而开辟的内存空间,它可以记录和浏览器之间的会话数据,记录和浏览器会通过一个sessionId进行关联,我们还可以像代码中那样通过setMaxInactiveInterval方法设置超时时间,比如我们常见的购物车往往就保存在session中。应该说JSP内还有一个内置对象page,只是这个对象只对当前页面有效,使用率很低,所以不再讨论它的功能。在代码的第⑦段,还会跳转到一个JSP页面,为此我们需要提供这个页面,并且将其放在webapp目录下,其内容如代码清单1-6所示。
代码清单1-6:RequesServlet请求跳转页面(request-result.jsp)

这里使用了JSP的三个内置对象request、session和application来获取属性值,需要注意它们的作用域。此时启动项目,在浏览器访问http://localhost:8080/chapter1/request/url?param1=value1¶m2=value2,就可以看到结果了,如图1-10所示。

图1-10 测试Servlet请求流程和JSP内置对象
注意到浏览器中的地址并未显示JSP,但是会展示JSP的内容,同时各个JSP内置对象也都可以正常工作了。此外还可以观察Tomcat日志平台,它会打印出:

可见除了页面,其他的信息也获取成功了。
1.3.4 HttpServletResponse的应用
HttpServletResponse也被我们称为响应对象,主要用于响应请求。相对来说,它没有HttpServletRequest那么复杂,它主要的作用是设置响应头和响应体。在前后端分离为主要趋势的今天,页面主要通过AJAX(Asynchronous Javascript And XML)获取数据,而手机应用也是接近的,所以后端更多的响应的类型会以JSON数据集为主,这时需要通过HttpServletResponse设置响应类型和编码。下面举例说明,如代码清单1-7所示。
代码清单1-7:HttpServletResponse的使用


这里首先获取两个请求参数,然后将其放到一个Map结构中,接着将其转换为JSON。之后通过HttpServletResponse对象设置响应类型、编码、响应头等内容,最后获取PrintWriter对象,输出JSON信息。启动Tomcat,在浏览器中访问http://localhost:8080/chapter1/response/my?param1=value1¶m2=value2,就可以看到图1-11所示的内容了。

图1-11 使用JSON数据集作为Servlet的请求响应
可见请求体也以JSON数据集的形式展示了出来。我们可以再查看响应头,笔者使用的是Chrome浏览器,点击F12键,就可以看到控制台了,图1-12就是笔者监控的结果。

图1-12 监控请求头
从图1-12中可以看到,设置的响应码和响应头都成功了。当然有时候,我们也需要跳转到JSP,使用HttpServletResponse对象也是可以的,下面通过代码清单1-8进行举例。
代码清单1-8:使用HttpServletResponse进行跳转

注意到代码中设置了请求属性、会话(Session)属性和ServletContext属性,之后通过sendRedirect方法跳转,这里的跳转需要给出相对全路径。由于跳转到response-result.jsp,所以下面需要编写它,并放在webapp目录下,其内容如代码清单1-9所示。
代码清单1-9:Servlet响应JSP页面(response-result.jsp)

启动项目,在浏览器中访问http://localhost:8080/chapter1/resp/jsp/my?param1= value1¶m2=value2¶ms3=value3,可以看到图1-13所示的内容。

图1-13 使用JSP作为Servlet的响应页面
这里需要注意两点:第一,我们请求的路径和浏览器中显示的最终路径并不相同,浏览器显示的是一个JSP路径,这点和HttpServletRequest的跳转不一样;第二,更重要的是可以看到param1这个参数为null,因为HttpServletResponse的跳转并不传递请求上下文,所以不能读取Servlet中HttpServletRequest设置的属性,只能查看Session的属性和ServletContext的属性。
1.3.5 过滤器的使用
过滤器(Filter)的作用是在Servlet执行的过程前后执行一些逻辑,比如可以控制对Servlet的访问权限控制。在Servlet规范中,需要使用注解@WebFilter标识过滤器,同时需要实现Filter(javax.servlet.Filter)接口,为此我们先来了解一下Filter接口的定义,如代码清单1-10所示。
代码清单1-10:Filter接口

和Servlet类似,Filter的init方法是一个初始化方法,它会在项目启动时先于Servlet的init方法执行,对过滤器进行初始化,在接口中有了默认的空实现;doFilter是过滤器的逻辑方法,它存在一个filterChain的参数,通过它的doFilter方法可以放行请求;而destroy方法是销毁过滤器时执行的方法,它在过滤器超时或者Servlet容器正常关闭时会调用,但是它会在Servlet的destroy方法之后执行,和init方法一样默认是空实现。
下面我们通过代码清单1-11来学习过滤器的使用。
代码清单1-11:过滤器实例


先看过滤器上标注的@WebFilter,它标识这个类是一个过滤器,同时,配置项filterName是过滤器名称,urlPatterns是限制拦截的路径,而servletNames是拦截的Servlet名称,initParams则是过滤器的配置参数。init方法从配置中读取配置参数。Filter的destroy方法在Servlet容器关闭时,会在Servlet的destroy方法之后执行。doFilter是过滤器的核心逻辑,在代码中的主要作用是获取参数param1,如果为空,则输出拦截请求的信息,然后直接返回不再执行下面的逻辑,否则就使用:

这行代码表示放行请求到具体的Servlet或者JSP上去。
启动项目,然后在浏览器访问http://localhost:8080/chapter1/request/url,可以看到图1-14所示的内容。

图1-14 测试过滤器
可见,没有参数param1,请求被过滤器拦截了。访问http://localhost:8080/chapter1/request/url?param1=value1¶m2=value2,可以看到图1-15所示的内容。

图1-15 测试过滤器放行请求
此时因为存在参数param1,所以过滤器放行了请求。最后正常关闭Tomcat,可以看到这样的日志顺序。


日志的注释是笔者自己加的,为的是让大家更好地理解过滤器和Servlet的执行顺序。从日志可以看到过滤器的init方法会先于Servlet的init方法执行;过滤器的逻辑也会先于Servlet的逻辑执行;但是过滤器的destroy方法会在Servlet的destroy方法之后执行。
1.3.6 监听
在Servlet的规范中存在多种监听(Listener),比如监听Servlet上下文属性的ServletContextAttributeListener、监听请求的ServletRequestListener和监听Session属性操作的HttpSessionAttributeListener等,最常用的当属ServletContextListener,所以本节就用它举例说明监听的使用。ServletContextListener是Web项目在Servlet容器中的监听器,允许我们在Web项目启动之前和之后的上下文织入自己的逻辑。先来看ServletContextListener接口定义,如代码清单1-12所示。
代码清单1-12:ServletContextListener接口定义

contextInitialized方法会在Servlet上下文初始化后执行,而contextDestroyed会在Servlet上下文销毁之后执行,我们可以在Servlet上下文初始化之前构建一些资源,或者在Servlet上下文销毁之后释放一些资源。
下面,我们开发一个监听器,来展示它的使用方法,如代码清单1-13所示。
代码清单1-13:监听器实例

重启项目,可以看到contextInitialized方法在过滤器的init方法之前执行。如果正常关闭Servlet容器,则contextDestroyed方法在过滤器的destroy方法之后执行。
1.3.7 Servlet容器初始化器
在我们开发的过程中,往往会引入第三方包,而当中有些类可能是我们需要使用的。此时Servlet容器初始化器(ServletContainerInitializer)允许我们将一些第三方的类加载到Servlet容器中,具体需要加载哪些类型,可以通过注解@HandlesTypes来指定。
我们假设存在第三方的一个类——OuterServiceImpl,它是OuterService接口的实现类,如代码清单1-14所示。
代码清单1-14:第三方类OuterServiceImpl

此时我们可以通过实现抽象类ServletContainerInitializer的onStartup方法,并且通过@HandlesTypes指定引入的类型,这样就可以使用第三方的类了,如代码清单1-15所示。
代码清单1-15:通过Serlvet容器初始化器加载第三方类

这个Servlet初始化器上标注了@HandlesTypes,并且指定了加载类型为OuterService,同时初始化器实现了ServletContainerInitializer的onStartup方法,该方法会在Servlet上下文构建时执行。onStartup方法有两个参数:一个是set,它是一个@HandlesTypes所指定类型的集合,该集合包含所指定类型的实现类或者子类;另一个是servletContext,它是Servlet的上下文。方法的逻辑在代码注释中也写清楚了,请自行参考。
有了这个初始化器,还需要在项目的resources目录下构建子目录/META-INF/servics,然后在其下面构建文件javax.servlet.ServletContainerInitializer,以文本形式打开它,编写其内容如下:

显然这就指向了我们自己开发的Servlet初始化器。
启动项目,可以看到日志在所有监听器、过滤器和Servlet初始化之前打印出:

可见我们开发的Servlet容器初始化器(WebContainerInitializer)已经正常工作了。
1.3.8 使用Cookie
Cookie是服务器写入用户本地浏览器的数据,因为用户可以禁用或者删除Cookie,所以使用Cookie并非十分可靠。下面我们通过CookieServlet来学习Cookie的使用,如代码清单1-16所示。
代码清单1-16:使用Cookie


这段代码中的writeCookie方法是将Cookie写入浏览器,而showCookie方法是显示Cookie。具体执行哪个方法是通过参数action控制的,当其为write时,就会执行writeCookie方法,当其为show时,就会执行showCookie方法。启动项目后,在浏览器中先访问http://localhost:8080/chapter1/cookie/test?action=write,再访问http://localhost:8080/chapter1/cookie/test?action=show,就可以看到图1-16所示的内容了。

图1-16 Cookie的使用
从图1-16可见,Cookie写入和显示都已经成功了。
1.3.9 提交表单
上述谈到Servlet的doGet方法,而实际上,还可以使用doPost、doPut等方法,其中最常用的是doPost方法。一般来说,GET请求从服务端获取信息,它的安全性较差,且对提交数据的类型和长度有所限制,同时,参数在URL是可见的,它的优势在于速度较快。POST请求是浏览器向服务端提交数据,数据类型和长度都不受限,同时参数可以放在表单中,不在URL中显示出来,安全度也较高,但是性能相对低一些。由于表单涉及商业数据,比较重要,因此提交表单的操作一般会使用POST请求。
下面我们开发一张JSP,通过它来提交表单,如代码清单1-17所示。
代码清单1-17:JSP表单

这里注意<form>的属性配置,因为action配置的是提交到的地址,而method默认为get,所以这里修改为post,意味着将表单以POST请求的方式提交到服务端。接着开发后端处理表单的Servlet,如代码清单1-17所示。
代码清单1-18:开发Servlet接收POST请求

这里的PostServlet与之前开发的Servlet不同的是,不再覆盖doGet方法,而是覆盖doPost方法,这就意味着它只能接收POST类型的请求。这里将请求匹配地址设置为/post,也就是会接收表单的提交。
启动项目后,访问http://localhost:8080/chapter1/form.jsp,可以看到图1-17的测试POST请求内容。

图1-17 测试POST请求
在图1-17中,填写了表单的内容,点击“提交”按钮,可以看到图1-18所示的内容。

图1-18 提交表单后的结果
从图1-18中可以看到,我们已经成功地通过POST请求,将表单提交到Servlet中处理。
1.3.10 使用web.xml
除了可以使用Servlet 3.0规范给出的各种注解,我们还可以使用web.xml配置Servlet、监听器和过滤器等内容。在此之前,我们把开发过的WebContextListener上标注的@WebListener、ServletFilter上标注的@WebFilter和MyServlet上标注的@WebServlet删除或注释掉,这样Servlet容器就不能识别它们了。接着我们可以通过web.xml配置它们,如代码清单1-19所示。
代码清单1-19:使用web.xml配置Web项目


这段代码有点长,不过结果还算清晰,具体的含义已经在注释中进行了说明,请大家自行参考。