Servlet再探-service()、中文问题、生命周期、跳转、自启动、request常见方法、response用法、上传文件

本文主要参考: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. 设置响应编码

设置响应编码有两种方式

  1. response.setContentType("text/html;charSet=UTF-8");
  2. 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进行上传的功能开发。

  1. 需要用到两个第三方的jar包,commons-io-1.4.jar和commons-fileupload-1.2.2.jar,放在WEB-INF/lib下
  2. 前部分代码是固定写法,用来做一些准备工作。 直到遍历出Item,一个Item就是对应一个浏览器提交的数据,通过item.getInputStream可以打开浏览器上传的文件的输入流。
  3.  客户提交的文件名有可能是一样的,所以在服务端保存文件的时候,不能使用客户提交的文件名。这里使用的是一种粗糙的解决文件名重复的办法,即使用时间戳。 
  4. 读取输入流中的数据,保存在服务端的目录下 e:/project/j2ee/web/uploaded,这个目录是通过getRealPath获取到的。 如果项目部署在其他地方,那么会自动做相应的变化。注: 为什么要放这里? 因为后续网页上显示的时候是通过http://127.0.0.1/uploaded/xxx.jpg 路径来获取图片。
  5.  根据临时生成的文件名,创建一个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. 上传测试

成功!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值