本文主要参考:http://how2j.cn/k/servlet/servlet-eclipse/558.html
service()
1. doGet()
当浏览器使用get方式提交数据的时候,servlet需要提供doGet()方法。
get方式:
- form默认的提交方式
- 通过一个超链访问某个地址
- 在地址栏直接输入某个地址
- ajax指定使用get方式的时候
2. doPost()
当浏览器使用post方式提交数据的时候,servlet需要提供doPost()方法
post方式:
- 在form上显式设置method="post"的时候
- ajax指定post方式的时候
3. service()
LoginServlet继承了HttpServlet,同时也继承了一个方法:
service(HttpServletRequest , HttpServletResponse )
实际上,在执行doGet()或者doPost()之前,都会先执行service()。
由service()方法进行判断,到底该调用doGet()还是doPost()。可以发现,service(),doGet(),doPost()三种方式的参数列表都是一样的。
所以,有时候也会直接重写service()方法,在其中提供相应的服务,就不区分到底是get还是post了。
将前面的登录的LoginServlet,改为service方法,也可以达到相同的效果。
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 LoginServlet extends HttpServlet {
protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
String name = request.getParameter("name");
String password = request.getParameter("password");
String html = null;
if("admin".equals(name) && "123".equals(password)) {
html = "<div style='color:green'>success</div>";
}else {
html = "<div style='color:red'>fail</div>";
}
PrintWriter pw = response.getWriter();
pw.println(html);
}
}
中文问题
1. 获取中文的参数
为了成功获取中文参数,需要做如下操作
1.1 login.html中加上:
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
这句话的目的是告诉浏览器,等下发消息给服务器的时候,使用UTF-8编码
1.2 login.html
form的method修改为post
1.3 在servlet进行解码和编码
byte[] bytes = name.getBytes("ISO-8859-1");
name = new String(bytes,"UTF-8");
先使用ISO-8859-1解码,然后使用UTF-8编码。这样就可以得到正确的中文参数了。
这样需要对每一个提交的数据都进行编码和解码处理,如果觉得麻烦,可以使用一句话代替:
request.setCharacterEncoding("UTF-8");
并且把这句话放在request.getParameter()之前。
2. 返回中文的响应
在Servlet中,加上
response.setContentType("text/html;charset=UTF-8");
生命周期
1. 生命周期
一个Servlet的声明周期由实例化、初始化、提供服务、销毁、被回收 几个步骤组成。
2. 实例化
当用户通过浏览器加入一个路径,这个路径对应的servlet被调用的时候,该Servlet就会被实例化。为LoginServlet显式提供一个构造方法LoginServlet(),然后通过浏览器访问,就可以观察到“LoginServlet构造方法被调用”,无论访问多少次LoginServlet,LoginServlet构造方法只会执行一次,所以Servlet是单例的。
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet {
public LoginServlet(){
System.out.println("LoginServlet 构造方法 被调用");
}
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//略
}
}
3. 初始化
LoginServlet继承了HttpServlet,同时也继承了init(ServletConfig)方法。init方法是一个实例方法,所以会在构造方法执行后执行。无论访问多少次LoginServlet,init初始化只会执行一次。
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet {
public LoginServlet() {
System.out.println("LoginServlet 构造方法 被调用");
}
public void init(ServletConfig config) {
System.out.println("init(ServletConfig)");
}
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 略
}
}
4. 提供服务
接下来就是执行service()方法,然后通过浏览器传来的信息进行判断,是调用doGet()还是doPost()方法。在service()中就会编写我们的业务代码,在本例中就是判断用户输入的账号和密码是否正确。
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
String html = null;
if ("admin".equals(name) && "123".equals(password))
html = "<div style='color:green'>success</div>";
else
html = "<div style='color:red'>fail</div>";
PrintWriter pw = response.getWriter();
pw.println(html);
}
5. 销毁
接着是销毁destroy(),在如下几种情况下,会调用destroy():
5.1 该Servlet所在的web应用重新启动,在server.xml中配置该web应用的时候到了
<Context path="/j2ee" docBase="e:\\project\\j2ee\\web" debug="0" reloadable="false" />
如果把reloadable="false"改为reloadable="true"就表示有任何类发生的更新,web应用会自动重启。当web应用自动重启的时候,destroy()方法就会被调用。
5.2 关闭
关闭tomcat的时候destroy()方法会被调用,但是这个一般都发生很快,不易被发现。
public class LoginServlet extends HttpServlet {
public void destroy() {
System.out.println("destroy()");
}
}
6. 被回收
当该Servlet被销毁后,就满足垃圾回收的条件了。当下一次垃圾回收GC来临的时候,就有可能被回收。
跳转
页面跳转是开发一个web应用经常会发生的事情。比如登录成功或失败后,分别会跳转到不同的页面。
跳转的方式有两种,服务端跳转和客户端跳转。
1.准备两个页面success.html fail.html
首先在web目录下准备两个页面 success.html,fail.html
分别用于显示登录成功 或者登录失败
- 如果登录成功了,就服务端跳转到success.html
- 如果登录失败了,就客户端跳转到fail.html
2. 在服务端跳转
在Servlet中进行服务端跳转的方式:
request.getRequestDispatcher("sucess.html").forward(request,response);
服务端可以看到浏览器的地址依然是/login路径,并不会变成success.html
3. 客户端跳转
在Servlet中进行客户端跳转的方式:
response.sendRedirect("fail.html");
可以看到,浏览器地址发生了变化。
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
if ("admin".equals(name) && "123".equals(password)) {
request.getRequestDispatcher("success.html").forward(request, response);
}
else{
response.sendRedirect("fail.html");
}
}
}
4. 服务端跳转与客户端跳转图示
自启动
在web.xml中,配置Hello Servlet的地方,增加一句:
<load-on-startup>10</load-on-startup>
取值范围是1-99,表明Servlet会随着Tomcat的启动而初始化。同时,同时,为HelloServlet提供一个init(ServletConfig) 方法,验证自启动。
1. request常见方法
- request.getRequestURL(): 浏览器发出请求时的完整URL,包括协议 主机名 端口(如果有)"
- request.getRequestURI(): 浏览器发出请求的资源名部分,去掉了协议和主机名"
- request.getQueryString(): 请求行中的参数部分,只能显示以get方式发出的参数,post方式的看不到
- request.getRemoteAddr(): 浏览器所处于的客户机的IP地址
- request.getRemoteHost(): 浏览器所处于的客户机的主机名
- request.getRemotePort(): 浏览器所处于的客户机使用的网络端口
- request.getLocalAddr(): 服务器的IP地址
- request.getLocalName(): 服务器的主机名
- request.getMethod(): 得到客户机请求方式一般是GET或者POST
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 LoginServlet extends HttpServlet {
protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
//request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
String password = request.getParameter("password");
System.out.println("浏览器发出请求时的完整URL,包括协议、主机名、端口(如果有):"+request.getRequestURL());
System.out.println("浏览器发出请求时的资源部分,去除了协议和主机名::"+request.getRequestURI());
System.out.println("请求行中的参数部分:"+request.getQueryString());
System.out.println("浏览器所处的客户机的IP地址:"+request.getRemoteAddr());
System.out.println("浏览器所处的客户机的主机名:"+request.getRemoteHost());
System.out.println("浏览器所处的客户机使用的网络端口:"+request.getRemotePort());
System.out.println("服务器的IP地址:"+request.getLocalAddr());
System.out.println("服务器的主机名:"+request.getLocalName());
System.out.println("得到客户机请求方式:"+request.getMethod());
String html = null;
if("admin".equals(name) && "123".equals(password)) {
//request.getRequestDispatcher("success.html").forward(request, response);
html = "<div style='color:green'>登陆成功</div>";
}else {
html = "<div style='color:red'>登录失败fail</div>";
//response.sendRedirect("fail.html");
}
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
pw.println(html);
}
}
2. 获取参数
- request.getParameter(): 是常见的方法,用于获取单值的参数
- request.getParameterValues(): 用于获取具有多值的参数,比如注册时候提交的 "hobits",可以是多选的。
- request.getParameterMap(): 用于遍历所有的参数,并返回Map类型。
3. 获取头信息
- request.getHeader() 获取浏览器传递过来的头信息。
比如getHeader("user-agent") 可以获取浏览器的基本资料,这样就能判断是firefox、IE、chrome、或者是safari浏览器 - request.getHeaderNames() 获取浏览器所有的头信息名称,根据头信息名称就能遍历出所有的头信息。
本例中,修改HelloServlet,使其获得头信息,访问HelloServlet获取如下头信息:
- host: 主机地址
- user-agent: 浏览器基本资料
- accept: 表示浏览器接受的数据类型
- accept-language: 表示浏览器接受的语言
- accept-encoding: 表示浏览器接受的压缩方式,是压缩方式,并非编码
- connection: 是否保持连接
- cache-control: 缓存时限
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet{
public void init(ServletConfig config){
System.out.println("init of Hello Servlet");
}
public void doGet(HttpServletRequest request, HttpServletResponse response){
Enumeration<String> headerNames= request.getHeaderNames();
while(headerNames.hasMoreElements()){
String header = headerNames.nextElement();
String value = request.getHeader(header);
System.out.printf("%s\t%s%n",header,value);
}
try {
response.getWriter().println("<h1>Hello Servlet!</h1>");
response.getWriter().println(new Date().toLocaleString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4. 服务端传参
setAttribute和getAttribute可以用来在进行服务端跳转的时候,在不同的Servlet之间进行数据共享。
response用法
1.设置响应内容
通过response设置响应已经用的比较多了,在前面的Servlet学习中都是用到:
PrintWriter pw = response.getWriter();
通过response.getWriter();获取一个PrintWriter对象。可以使用println(),append(),writrer(),format()等等方法设置返回给浏览器的html内容。
2. 设置响应格式
response.setContentType("text/html");
"text/html"是即格式的,在request获取头信息中对应的request.getHeader("accept");
"text/html"是存在的,表示浏览器可以识别这种格式,如果换一个其他的格式,比如"text/lol",浏览器不能识别,那么打开此servlet就会弹出一个下载的对话框。这样的手段液常常用于实现下载功能。
3. 设置响应编码
设置响应编码有两种方式
- response.setContentType("text/html;charSet=UTF-8");
- response.setCharacterEncoding("UTF-8");
这两种方式都需要在response.getWriter调用之前执行才能生效。
他们的区别在于:
1. response.setContentType("text/html;charset=UTF-8");
不仅发送到浏览器的内容会使用UTF-8编码,而且还通知浏览器使用UTF-8编码方式进行显示。所以总能正常显示中文。
2. response.setCharacterEncoding("UTF-8");
仅仅是发送的浏览器的内容是UTF-8编码的,置于浏览器使用哪种编码方式显示不管。所以当浏览器的显示编码方式不是UTF-8的时候,就会看到乱码,需要手动再进行一次设置。
public void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
response.setContentType("text/html; charset=UTF-8");
// response.setCharacterEncoding("UTF-8");
PrintWriter pw = response.getWriter();
pw.println("<h1>第一次 使用 Servlet</h1>");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4. 301或者302客户端跳转
客户端跳转有两种跳转
- 302表示临时跳转
- 301表示永久性跳转
302就是前面在客户端跳转章节用到过的:response.sendRedirect("fail.html");
301要使用另外的手段:response.setStatus(301);response.setHeader("Location","fail.html");
5. 设置不使用缓存
使用缓存可以加快页面的加载,降低服务端的负担。但是也可能看到过时的信息,可以使用以下手段通知浏览器不使用缓存。
response.setDateHeader("Expires",0 );
response.setHeader("Cache-Control","no-cache");
response.setHeader("pragma","no-cache");
上传文件
1. 首先准备上传页面upload.html
上传页面有两点需要注意:
a. form的method必须是post的,get不能上传文件。还要加上entype="multipart/form-data"表示提交的数据是二进制文件。
<form action="uploadPhoto" method="post" enctype="multipart/form-date">
b.需要提供type="file"的字段进行上传
<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<form action="uploadPhoto" method="post" enctype="multipart/form-data">
英雄名称:<input type="text" name="heroName" /> <br>
上传头像 : <input type="file" name="filepath" /> <br>
<input type="submit" value="上传">
</form>
2. 接着准备UploadPhotoServlet
在UploadPhotoServlet进行上传的功能开发。
- 需要用到两个第三方的jar包,commons-io-1.4.jar和commons-fileupload-1.2.2.jar,放在WEB-INF/lib下
- 前部分代码是固定写法,用来做一些准备工作。 直到遍历出Item,一个Item就是对应一个浏览器提交的数据,通过item.getInputStream可以打开浏览器上传的文件的输入流。
- 客户提交的文件名有可能是一样的,所以在服务端保存文件的时候,不能使用客户提交的文件名。这里使用的是一种粗糙的解决文件名重复的办法,即使用时间戳。
- 读取输入流中的数据,保存在服务端的目录下 e:/project/j2ee/web/uploaded,这个目录是通过getRealPath获取到的。 如果项目部署在其他地方,那么会自动做相应的变化。注: 为什么要放这里? 因为后续网页上显示的时候是通过http://127.0.0.1/uploaded/xxx.jpg 路径来获取图片。
- 根据临时生成的文件名,创建一个html img元素,然后通过response返回浏览器
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadPhotoServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) {
String filename = null;
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置上传文件的大小限制为1M
factory.setSizeThreshold(1024 * 1024);
List items = null;
try {
items = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (!item.isFormField()) {
// 根据时间戳创建头像文件
filename = System.currentTimeMillis() + ".jpg";
//通过getRealPath获取上传文件夹,如果项目在e:/project/j2ee/web,那么就会自动获取到 e:/project/j2ee/web/uploaded
String photoFolder =request.getServletContext().getRealPath("uploaded");
File f = new File(photoFolder, filename);
f.getParentFile().mkdirs();
// 通过item.getInputStream()获取浏览器上传的文件的输入流
InputStream is = item.getInputStream();
// 复制文件
FileOutputStream fos = new FileOutputStream(f);
byte b[] = new byte[1024 * 1024];
int length = 0;
while (-1 != (length = is.read(b))) {
fos.write(b, 0, length);
}
fos.close();
} else {
System.out.println(item.getFieldName());
String value = item.getString();
value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
System.out.println(value);
}
}
String html = "<img width='200' height='150' src='uploaded/%s' />";
response.setContentType("text/html");
PrintWriter pw= response.getWriter();
pw.format(html, filename);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3. 配置web.xml
<servlet>
<servlet-name>UploadPhotoServlet</servlet-name>
<servlet-class>UploadPhotoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadPhotoServlet</servlet-name>
<url-pattern>/uploadPhoto</url-pattern>
</servlet-mapping>
4. 复制jar包
再强调一遍:把右侧的两个jar包commons-io-1.4.jar,commons-fileupload-1.2.2.jar 复制到WEB-INF/lib 目录下,不然Tomcat启动后闪退!!!
5. 在项目中导入jar包
6. 上传测试
成功!