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。

———————————————————————————————————-

前面我们介绍到了form表单,其中action属性是把表单数据提交的路径,那么数据在服务器端是怎么处理的呢?我们一起来学习一下java servlet开发实现数据在服务器端的处理。

servlet是程序必须运行在java web服务器中,主要有tomcat, jboss,jetty中,这里我们使用比较常用的tomcat作为java web服务器。下面简单介绍一下tomcat的安装:
1、tomcat下载:http://pan.baidu.com/s/1mho8L1u
2、找一个合适的目录把下载的文件解压就好了,比如我的就是在:”D:Program Files”,目录为“apache-tomcat-8.0.21”
3、与eclipse集成:windows->preferences->Server->Runtime Environment

tomcat配置3

选中Apache-Apache Tomcat 8.0 然后Next

tomcat配置2

4、运行web工程
在项目上点击右键Run As – Run On Server-Apache- tomcat v8.0 Server

tomcat配置3

Next->Finish
5、通过浏览器查看前面开发的界面
http://localhost:8080/learntestweb/helloworld.html
在浏览器中显示了”Hello World!”,查看本机的ip,把localhost替换成本机ip在局域网内也可以访问的。
这里的”learntestweb”是站点路径,后面的是链接路径。

上面的访问虽然通过浏览器访问服务器路径获取的页面,但是仍然是静态页面,下面我们一起使用servlet输出”当前时间+hello world”。
新创建类com.sunhaojie.learntestweb.web.HelloWorldServlet

[code lang=”java”]
public class HelloWorldServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:SS");
Date curDate = new Date();
String curDateStr = dateFormat.format(curDate);
String helloWorld = curDateStr + " : hello world!";
resp.getWriter().println(helloWorld);
}
}
[/code]

其中doGet和doPost就是对应了form提交方法中的get和post方法。
“resp. getWriter()”获取输出流,println把数据输出到客户端,这里不用调用输出流的close方法。
在web.xml中添加配置

[code lang=”java”]
<servlet>
<servlet-class>com.sunhaojie.learntestweb.web.HelloWorldServlet</servlet-class>
<servlet-name>helloWorld</servlet-name>
</servlet>

<servlet-mapping>
<servlet-name>helloWorld</servlet-name>
<url-pattern>/helloWorld</url-pattern>
</servlet-mapping>
[/code]

这个配置是把url路径映射到类上,这里是把路径“/helloWorld”映射到”com.sunhaojie.learntestweb.web.HelloWorldServlet”上。

我们使用上一章的学生信息表,把学生信息提交到服务器端(因为上传文件比较复杂,并且不常用,这里先不介绍了)。把form.html复制为studentForm.html,然后把file表单删除,并修改action=“/learntestweb/submitStudentInfo”,在web.xml添加submitStudentInfo路径的映射

[code lang=”xml”]
<servlet>
<servlet-class>com.sunhaojie.learntestweb.web.SubmitStudentInfoServlet</servlet-class>
<servlet-name>submitStudentInfo</servlet-name>
</servlet&gt;

<servlet-mapping>
<servlet-name>submitStudentInfo</servlet-name>
<url-pattern>/submitStudentInfo</url-pattern>
</servlet-mapping>
[/code]

创建SubmitStudentInfoServlet类,把提交的信息复制给StudentVo类,并保存。
代码如下:

[java lang=”java”]
public class StudentVo {
private String name;
private String sex;
private String year;
private String month;
private String day;
private String[] hobbys;
private String password;
private String info;
}
public class SubmitStudentInfoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@SuppressWarnings("unchecked")
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String name = req.getParameter("name");
String sex = req.getParameter("sex");
String year = req.getParameter("year");
String month = req.getParameter("month");
String day = req.getParameter("day");
String[] hobbys = req.getParameterValues("hobby");
String password = req.getParameter("password");
String info = req.getParameter("info");
StudentVo studentVo = new StudentVo();
studentVo.setName(name);
studentVo.setSex(sex);
studentVo.setYear(year);
studentVo.setMonth(month);
studentVo.setDay(day);
studentVo.setHobbys(hobbys);
studentVo.setPassword(password);
studentVo.setInfo(info);
List&lt;StudentVo&gt; studentVoList = (List&lt;StudentVo&gt;) req.getSession().getAttribute("studentList");
if (studentVoList == null) {
studentVoList = new ArrayList&lt;StudentVo&gt;();
req.getSession().setAttribute("studentList", studentVoList);
}
studentVoList.add(studentVo);
resp.setContentType("text/html; charset=UTF-8");
resp.getWriter().print(("当前学生信息总数为:" + studentVoList.size()));
}
}
[/java]

知识点:
1、req.setCharacterEncoding(“UTF-8”);设置入参编码方式,为了处理中文乱码。
2、req.getParameter(“xxx”),获取key为xxx的参数值,这里是form表单中name属性为xxx的值
3、req.getParameterValues(“xxx”),获取key为xxx的参数值数组,用于key相同的多值传输数据。
4、req.getSession(). getAttribute(“xxx”);获取回话session中的属性xxx的值,我们后面再看一下session。
还有一个setAttribute是为session的属性赋值。
5、resp.setContentType设置内容类型,这里为了处理中文乱码问题。
大家多次提交发现学生信息的总数再添加,那么我们使用一个另外的浏览器再次访问(或者关掉当前浏览器,重新打开)发现学生信息数从0开始了。我们提交到同一个服务器,数据应该都在浏览器直接共享才是我们要的效果,否则提交到服务器端没有意义啦。下面我们修改一下功能:

[code lang=”java”]
List&lt;StudentVo&gt; studentVoList = (List&lt;StudentVo&gt;) req.getServletContext().getAttribute("studentList");
if (studentVoList == null) {
studentVoList = new ArrayList&lt;StudentVo&gt;();
req.getServletContext().setAttribute("studentList", studentVoList);
}
[/code]

把getSession替换成getServletContext,重启服务,不同的浏览器提交学生信息个数都在增加。

下面我们来普及几个概念:
request,请求,前面我们学习Socket时,讲到了socket有一个输入流,一个输出流,用于客户端和服务器端之间通信。这里的request就可以理解为输入流的封装,实现了客户端和服务器端的http交互。
response,应答,输出流的封装。
session,每次点击,提交数据都是一次交互,客户端(这里可以理解为浏览器)和服务器端的一段时间内的全部的交互称为回话(session),session有过期时间,两次请求超出了服务器设置(可变更)的session过期时间,第二次请求就会创建新的回话。
cookie,客户端按照域名存储的键值对信息,可以持久化到硬盘。键值对中有一个key sessionId(也可以变更,但是作用类似)是实现session的关键数据。当客户端访问服务器端时,服务器端会查看是否存在sessionId如果存在,则会把本次请求和session关联,如果不存在则创建新的session,并把sessionId赋值给请求的cookie,客户端会把cookie信息在本地保存。大部分的sessionId的cookie信息时内存存储,没有持久化到硬盘,所以重启浏览器会导致创建新的session。
servletContext,实例容器,每个web服务器存在一个servletContext,启动时创建,关闭时销毁,所以我们重启服务后学生信息个数被清空。

我们如果想查看已经提交的学生信息,使用输出流打印一个页面,字符串的拼接太复杂了,所以我们引入一个模板引擎velocity可以更方便的生成页面信息。
添加velocity模板支持,在工程的WEB-INFO下的lib目录(如果不存在,则新建lib目录)中添加以下jar包。
http://pan.baidu.com/s/1eQOwYQ2 velocity-tools-2.0.jar
http://pan.baidu.com/s/1mhfLNz6 velocity-1.7.jar
http://pan.baidu.com/s/1o6P2BJs commons-logging-1.1.jar
http://pan.baidu.com/s/1geqLub5 commons-lang-2.2.jar
http://pan.baidu.com/s/1boiMc0n commons-digester-1.8.jar
http://pan.baidu.com/s/1sk5tVTb commons-collections-3.2.jar
http://pan.baidu.com/s/1i4lX8Nz commons-beanutils-1.7.0.jar
在WEB-INFO下创建文件velocity.properties,内容为:

[code lang=”xml”]
resource.loader = webapp
webapp.resource.loader.class = org.apache.velocity.tools.view.servlet.WebappLoader
webapp.resource.loader.path=/WEB-INF/vm/
input.encoding=utf-8
output.encoding=utf-8
[/code]

创建业务处理类StudentListServlet

[java lang=”java”]
public class StudentListServlet extends VelocityViewServlet {
@Override
protected Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context ctx) {
List&lt;StudentVo&gt; studentList = (List&lt;StudentVo&gt;) request.getServletContext().getAttribute("studentList");
ctx.put("studentList", studentList);
return getTemplate("studentList.vm");
}
}
[/java]

这里注意继承的是velocity提供的Servlet基类,其中Context.put(“key”, value);表示在模板中可以获取的数据,key为第一个入参。
创建vm模板页面studentList.vm 用于展示学生信息列表

[code lang=”xml”]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>学生信息列表</title>
</head>
<body>
<div align="center">
<h2>学生信息表</h2>
<table border="1" width="50%">
<tr>
<th width="10%">姓名</th>
<th width="10%">性别</th>
<th width="10%">生日</th>
<th width="15%">爱好</th>
<th width="15%">初始密码</th>
<th width="45%">自我介绍</th>
</tr>
#foreach( $item in $studentList)
<tr>
<td>$item.name</td>
<td>
#if("male" == $item.sex)

#elseif("female" == $item.sex)

#else
未知
#end
</td>
<td>${item.year}-${item.getMonth()}-${item.day}</td>
<td>
#foreach( $h in $item.hobbys)
$h
#end
</td>
<td>$item.password</td>
<td>$item.info</td>
</tr>
#end
</table>
</div>
</body>
</html>
[/code]

1、$xxxx或者${xxxx}获取在Context.put的数据,xxxx为第一个入参的值。
2、${xxxx.yyy}获取xxxx对象中的yyy属性,我们也可以使用${xxxx.get()}调用方法获取。
3、foreach方法,foreach( $item in $studentList)遍历集合studentList,$item表示一个元素。结尾使用#end
4、ifelse,分支流程控制,#end结尾
web.xml配置

[code lang=”xml”]
<servlet>
<servlet-name>StudentListServlet</servlet-name>
<servlet-class>com.sunhaojie.learntestweb.web.StudentListServlet</servlet-class>
<init-param>
<param-name>org.apache.velocity.properties</param-name>
<param-value>/WEB-INF/velocity.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>StudentListServlet</servlet-name>
<url-pattern>/studentList</url-pattern>
</servlet-mapping>
[/code]

init-param可以理解为构造函数的入参,获取vilocity配置。
浏览器访问:http://localhost:8080/learntestweb/studentList 可以展示出来服务器中保存的学生信息数据了。

页面端的入门开发基本讲完了,服务器端重启数据丢失的问题怎么解决呢?

小练习:
开发学生信息编辑功能,已注册的用户可以更新信息。

以下是工程中使用的代码:
web.xml

[code lang=”xml”]
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>learntestweb</display-name>

<servlet>
<servlet-class>com.sunhaojie.learntestweb.web.HelloWorldServlet</servlet-class>
<servlet-name>helloWorld</servlet-name>
</servlet>

<servlet-mapping>
<servlet-name>helloWorld</servlet-name>
<url-pattern>/helloWorld</url-pattern>
</servlet-mapping>

<servlet>
<servlet-class>com.sunhaojie.learntestweb.web.SubmitStudentInfoServlet</servlet-class>
<servlet-name>submitStudentInfo</servlet-name>
</servlet>

<servlet-mapping>
<servlet-name>submitStudentInfo</servlet-name>
<url-pattern>/submitStudentInfo</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>StudentListServlet</servlet-name>
<servlet-class>com.sunhaojie.learntestweb.web.StudentListServlet</servlet-class>
<init-param>
<param-name>org.apache.velocity.properties</param-name>
<param-value>/WEB-INF/velocity.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>StudentListServlet</servlet-name>
<url-pattern>/studentList</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>helloworld.html</welcome-file>
</welcome-file-list>
</web-app>
[/code]

velocity.properties:

[code lang=”xml”]
resource.loader = webapp
webapp.resource.loader.class = org.apache.velocity.tools.view.servlet.WebappLoader
webapp.resource.loader.path=/WEB-INF/vm/
input.encoding=utf-8
output.encoding=utf-8
[/code]

studentForm.html

[code lang=”html”]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>学生信息录入表单</title>
</head>
<body>
<div align="center">
<h1>学生信息提交</h1>
<form action="/learntestweb/submitStudentInfo" method="post">
<table border="1">
<tr>
<td align="right" width="30%"><label for="nameTextId">姓名:</label></td>
<td align="left"><input type="text" name="name" id="nameTextId"/></td>
</tr>
<tr>
<td align="right"><label>性别:</label></td>
<td align="left"><input type="radio" name="sex" value="male" /> 男 <input type="radio" name="sex" value="female" /> 女 </td>
</tr>
<tr>
<td align="right"><label>出生日期:</label></td>
<td align="left">
<select name="year">
<option selected="selected">—-年—-</option>
<option >1990</option>
<option >1991</option>
<option >1992</option>
<option >1993</option>
<option >1994</option>
</select>
<select name="month">
<option selected="selected">—-月—-</option>
<option >01</option>
<option >02</option>
<option >03</option>
<option >04</option>
<option >05</option>
<option >06</option>
<option >07</option>
<option >08</option>
<option >09</option>
<option >10</option>
<option >11</option>
<option >12</option>
</select>
<select name="day">
<option selected="selected">—-日—-</option>
<option >01</option>
<option >02</option>
<option >03</option>
<option >04</option>
<option >05</option>
<option >06</option>
<option >07</option>
<option >08</option>
<option >09</option>
<option >10</option>
<option >11</option>
<option >12</option>
<option >13</option>
<option >14</option>
<option >15</option>
<option >16</option>
<option >17</option>
<option >18</option>
<option >19</option>
<option >20</option>
<option >21</option>
<option >22</option>
<option >23</option>
<option >24</option>
<option >25</option>
<option >26</option>
<option >27</option>
<option >28</option>
<option >29</option>
<option >30</option>
<option >31</option>
</select>
</td>
</tr>
<tr>
<td align="right"><label>兴趣爱好:</label></td>
<td align="left">
<input type="checkbox" name="hobby" value="basketball" />篮球
<input type="checkbox" name="hobby" value="football" />足球
<input type="checkbox" name="hobby" value="swim" />游泳
</td>
</tr>
<tr>
<td align="right"><label>初始密码:</label></td>
<td align="left"><input type="password" name="password"/></td>
</tr>
<tr>
<td align="right"><label>自我介绍:</label></td>
<td align="left"><textarea rows="5" cols="20" name="info"></textarea></td>
</tr>
<tr>
<td align="center" colspan="2">
<input type="reset" value="重置"/> <input type="submit" value="提交" /> <input type="button" value="按钮" />
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
[/code]

studentList.vm

[code lang=”html”]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>学生信息列表</title>
</head>
<body>
<div align="center">
<h2>学生信息表</h2>
<table border="1" width="50%">
<tr>
<th width="10%">姓名</th>
<th width="10%">性别</th>
<th width="10%">生日</th>
<th width="15%">爱好</th>
<th width="15%">初始密码</th>
<th width="45%">自我介绍</th>
</tr>
#foreach( $item in $studentList)
<tr>
<td>$item.name</td>
<td>
#if("male" == $item.sex)

#elseif("female" == $item.sex)

#else
未知
#end
</td>
<td>${item.year}-${item.month}-${item.day}</td>
<td>
#foreach( $h in $item.hobbys)
$h
#end
</td>
<td>$item.password</td>
<td>$item.info</td>
</tr>
#end
</table>
</div>
</body>
</html>
[/code]

[code lang=”java”]
package com.sunhaojie.learntestweb.web;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName HelloWorldServlet
* @Description 输出当前时间和hello world的字符串
*
* @author sunhaojie 3113751575@qq.com
* @date 2016年2月23日 上午10:49:11
*/
public class HelloWorldServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:SS");
Date curDate = new Date();
String curDateStr = dateFormat.format(curDate);
String helloWorld = curDateStr + " : hello world!";
resp.getWriter().println(helloWorld);
}
}
package com.sunhaojie.learntestweb.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sunhaojie.learntestweb.vo.StudentVo;

/**
* @ClassName SubmitStudentInfoServlet
* @Description 学生信息提交
*
* @author sunhaojie 3113751575@qq.com
* @date 2016年2月23日 下午2:02:08
*/
public class SubmitStudentInfoServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}

@SuppressWarnings("unchecked")
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String name = req.getParameter("name");
String sex = req.getParameter("sex");
String year = req.getParameter("year");
String month = req.getParameter("month");
String day = req.getParameter("day");
String[] hobbys = req.getParameterValues("hobby");
String password = req.getParameter("password");
String info = req.getParameter("info");
System.out.println(name);
StudentVo studentVo = new StudentVo();
studentVo.setName(name);
studentVo.setSex(sex);
studentVo.setYear(year);
studentVo.setMonth(month);
studentVo.setDay(day);
studentVo.setHobbys(hobbys);
studentVo.setPassword(password);
studentVo.setInfo(info);

List&lt;StudentVo&gt; studentVoList = (List&lt;StudentVo&gt;) req.getServletContext().getAttribute("studentList");
if (studentVoList == null) {
studentVoList = new ArrayList&lt;StudentVo&gt;();
req.getServletContext().setAttribute("studentList", studentVoList);
}
studentVoList.add(studentVo);

resp.setContentType("text/html; charset=UTF-8");
resp.getWriter().print(("当前学生信息总数为:" + studentVoList.size()));
}
}

package com.sunhaojie.learntestweb.web;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.tools.view.VelocityViewServlet;
import com.sunhaojie.learntestweb.vo.StudentVo;
/**
* @ClassName StudentListServlet
* @Description 学生信息列表
*
* @author sunhaojie 3113751575@qq.com
* @date 2016年2月23日 下午4:33:28
*/
public class StudentListServlet extends VelocityViewServlet {
@Override
protected Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context ctx) {
List&lt;StudentVo&gt; studentList = (List&lt;StudentVo&gt;) request.getServletContext().getAttribute("studentList");
ctx.put("studentList", studentList);
return getTemplate("studentList.vm");
}
}
[/code]