Servlet学习

知识点

1.Servlet简介

 Servlet是sun公司提供的一门用于开发动态web资源的技术。
 Sun公司在其API中提供了一个servlet接口
 Servlet 是运行在 Web 服务器中的小型 Java 程序(即:服务器端的小应用程序)。
 servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。
 若想开发一个动态web资源(即开发一个Java程序向浏览器输出数据),要完成2个步骤:
a. 编写一个Java类,实现servlet接口。
b. 把开发好的Java类部署到web服务器中。

2.Servlet的执行过程

 Servlet作用处理请求
 当浏览器访问一个http://localhost:8080/hello/hello 路径,就向tomcat发送一个请求
在这里插入图片描述

3.Servlet生命周期(重要)

 Servlet生命周期四个状态:实例化–>初始化–>服务->销毁

 出生:(实例化–>初始化)第一次访问Servlet就出生(默认情况下)
 活着:(服务)应用活着,servlet就活着
 死亡:(销毁)应用卸载了servlet就销毁。
在这里插入图片描述
Servlet的创建时机
 默认情况下Servlet在第一次使用Servlet时才创建
 可以在web.xml中设置load-on-startup为2,Servlet就会启动时调用构造方法和初始化方法
在这里插入图片描述

4. Servlet实现的三种方式

  1. 实现javax.servlet.Servlet接口
public class HelloServlet2  implements Servlet{

	public HelloServlet2() {
		System.out.println("实例化-创建对象");
	}
	
	@Override
	public void destroy() {
		System.out.println("销毁destroy");
	}

	@Override
	public ServletConfig getServletConfig() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getServletInfo() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void init(ServletConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
		System.out.println("初始化init");
	}

	@Override
	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("服务Service...");
		//返回结果给客户端-----
		response.getWriter().write("hello Servlet");
	}

}
  1. 继承javax.servet.GenericServlet类(适配器模式)
public class HelloServlet3  extends GenericServlet{

	@Override
	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		
		response.getWriter().write("hello servlet....");
	}

	

}
  1. 继承javax.servlet.http.HttpServlet类(模板方法设计模式)
public class HelloServlet  extends HttpServlet{
	
	private int money = 100;
	/**
	 * Get请求
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		System.out.println(this);
		System.out.println(Thread.currentThread());
		money -= 20;
		//响应客户端
		response.getWriter().write("hello servlet -----------" + money);
	}

}

了解servlet结构
在这里插入图片描述

5.Servlet映射细节-通配符

 掌握Servlet映射规则
 通配符* 代表任意字符串,如图:

url-pattern: *.do 以*.字符串的请求都可以访问 注:不要加/
url-pattern: /* 任意字符串都可以访问
url-pattern: /action/* 以/action开头的请求都可以访问
匹配规则:
优先级:从高到低
绝对匹配–> /开头匹配 --> 扩展名方式匹配
如果url-pattern的值是/,表示执行默认映射。所有资源都是servlet
在这里插入图片描述

6.Servlet的线程安全问题(Servlet是单例)

 Servlet是单例:多线程每次访问都是同一个对象
 解决线程安全问题的最佳办法,不要写全局变量,尽量不要,而写局部变量。
在这里插入图片描述

7.Servlet的注解映射

7.1Servlet2.5以前映射配置

在这里插入图片描述

7.2Servlet 3.0 注解配置(简洁)

注意:在配置映射时,路径要加斜杠,不然会报错
在这里插入图片描述

8.ServletContext(单例对象,类似于Map)

Context:上下文
ServletContext: 代表的是整个应用。一个应用只有一个ServletContext对象。是单例对象 。

8.1 作用:

域对象:在一定范围内(当前应用),使多个Servlet共享数据。

8.2常用方法:

 void setAttribute(String name,object value);//向ServletContext对象的map中添加数据
 Object getAttribute(String name);//从ServletContext对象的map中取数据
 void rmoveAttribute(String name);//根据name去移除数据
在这里插入图片描述
在这里插入图片描述

8.3获取全局配置信息(用getInitParameter()方法)

第一步:在web.xml中配置一个context-param
在这里插入图片描述
第二步:获取web.xml中的配置
在这里插入图片描述

8.4获取资源路径(用getRealPath(String path)方法)

String getRealPath(String path);
 根据资源名称得到资源的绝对路径.
 可以得到当前应用任何位置的任何资源。

public class Lesson03Servlet1 extends HttpServlet{

	/**
	 * 通过浏览器地址栏访问的路径都是get请求
	 */
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
	/*	  ServletContext的String getRealPath(String path)方法;
		1.根据资源名称得到资源的绝对路径.
		2.可以得到当前应用任何位置的任何资源*/
		
		//解决乱码
		resp.setContentType("text/html");
		resp.setCharacterEncoding("UTF-8");
		
		/**
		 * String path = "src/com/gyf/web/lesson03/info.properties";
		 * 如果是web项目,查找文件时,要从类路径找
		 */
		
		String path = this.getServletContext().getRealPath("WEB-INF/classes/com/gyf/web/lesson03/info.properties");
		System.out.println(path);
		
		//在Servlet来获取info.properties数据
		//1.创建属性对象
		Properties pro = new Properties();
		
		//2.关连属性文件的路径
		pro.load(new FileInputStream(path));
		
		System.out.println(pro.getProperty("username"));
		
		//响应客户端
		resp.getWriter().write(path);
		resp.getWriter().write("-----");
		resp.getWriter().write(pro.getProperty("username"));
	}
}

  • 如果a.properties在src目录下,不需要写包名路径
    在这里插入图片描述

9.Servlet的转发

转发图解
在这里插入图片描述
代码实现
在这里插入图片描述

10.HTTP

10.1简介

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。

10.2HTTP请求流程

在这里插入图片描述

10.2HTTP消息头

 HTTP消息头是指在超文本传输协议( Hypertext Transfer Protocol ,HTTP)的请求和响应消息中,协议头部分的那些组件。
 HTTP消息头用来准确描述正在获取的资源、服务器或者客户端的行为
 HTTP消息头定义了HTTP事务中的具体操作参数。
 消息头包括请求时的消息头(请求头)和响应时的消息头(响应头)
在这里插入图片描述
在这里插入图片描述

11.HttpServletResponse(重点)

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。

11.1响应正文(主体)

getWrite(); 字符输出流
getOutputStream(); 字节输出流

@WebServlet("/Lesson06Servlet1")
public class Lesson06Servlet1 extends HttpServlet{
	 @Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//HttpServletRequest接口,tomcat中实现类org.apache.catalina.connector.RequestFacade 在lib/catalina.jar
			//HttpServletResponse接口,tomcat中实现类org.apache.catalina.connector.ResponseFacade
			
			System.out.println(request);
			System.out.println(response);
			
			//通过response对象响应正文的两种方式
			/**
			 * 1.通过PrintWriter给客户端响应数据
			 * PrintWriter:操作的数据是字符
			 * PrintWriter response.getWriter()
			 * 
			 * 2.通过OutputStream给客户端响应数据
			 * OutputStream:操作字节,一般可以用来下载文件
			 */
			//response.getWriter().write("hello,how are you!");
			ServletOutputStream sos = response.getOutputStream();
			byte[] buf = {97,95,86};
			sos.write(buf);//写一个字节数组
	 }
	
}

11.2响应状态码

常见的状态码参照:http://tool.oschina.net/commons?type=5

404 请求失败,请求所希望得到的资源未被在服务器上发现
200 请求已成功,请求所希望的响应头或数据体将随此响应返回。
302 重定向
500 服务抛出异常

11.3自动刷新功能

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.setContentType("text/html;charset=UTF-8");
		//response.setHeader("Refresh", "1");//每隔一秒刷新一次
		
		response.setHeader("Refresh", "5;URL=index.html");//3秒后转到另一页面
		response.getWriter().write("注册成功!3秒后会自动跳转,苦没有中转点击<a href='index.html'>这里</a>");
	}

11.4response细节

getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据

getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。 会抛异常。

Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。

Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。

12.HttpServletRequest(重点)

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。

12.1request常用方法

获得客户端信息

 getRequestURL方法返回客户端发出请求时的完整URL。
 getRequestURI方法返回请求行中的资源名部分。
 getQueryString 方法返回请求行中的参数部分。
 getRemoteAddr方法返回发出请求的客户机的IP地址
 getRemoteHost方法返回发出请求的客户机的完整主机名
 getRemotePort方法返回客户机所使用的网络端口号
 getLocalAddr方法返回WEB服务器的IP地址。
 getLocalName方法返回WEB服务器的主机名
 getMethod得到客户机请求方式

获得客户端请求头

 getHead(name)方法
 getHeaders(String name)方法 (很少用)
 getHeaderNames方法

@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
			//设置编码格式
		 	response.setContentType("text/html");
		 	response.setCharacterEncoding("UTF-8");
		 
			//获取请求头
		   
			System.out.println(request.getHeader("User-Agent"));
			
			//获取所有请求头的名字
			/** Enumeration 集合*/
			Enumeration<String> e = request.getHeaderNames();
			//遍历元素
			while (e.hasMoreElements()) {
				String headName = e.nextElement();
				System.out.println(headName);
			}
			response.getWriter().write("响应成功");
	 }

获得客户端请求参数(客户端提交的数据)

 getParameter(name)方法
 getParameterValues(String name)方法
 getParameterNames方法
 getParameterMap方法 //做框架用,非常实用
 getInputStream

12.2.Request的应用-获取表单数据(重点)

获取表单数据一

目标:掌握通过getParameter和getParameterValues获取请求参数
掌握POST和GET请求解决乱码的方案
掌握表单提交通过都用POST方法
在这里插入图片描述

<form action="/hello/LoginServlet" method="post">
用户名:<input type="text" name="username"><br>---码:<input type="password" name="password"><br>---别:<input type="radio" name="gender" value="男"><input type="radio" name="gender" value="女"><br>---好:<input type="checkbox" name="hobby" value="写代码">写代码
<input type="checkbox" name="hobby" value="看书">看书
<input type="checkbox" name="hobby" value="听课">听课<br>---注:<textarea rows="5" cols="20" name="remark"></textarea><br>
<input type="submit" value="提交">
</form>

LoginServlet:POST请求

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		//设置字符编码,解决乱码问题,只对post有效
		request.setCharacterEncoding("utf-8");
		//接收表单请求参数
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String gender = request.getParameter("gender");
		String[] hobby = request.getParameterValues("hobby");
		String remark = request.getParameter("remark");
		
		System.out.println(username);
		System.out.println(password);
		System.out.println(gender);
		System.out.println(Arrays.toString(hobby));
		System.out.println(remark);
	}

在这里插入图片描述

获取表单数据二

掌握 通过getParameterMap方法,封装参数到一个模型
这代码写在doPost中

模型

public class User {

	private String username;
	private String password;
	private String gender;
	private String[] hobby;
	private String remark;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String[] getHobby() {
		return hobby;
	}
	public void setHobby(String[] hobby) {
		this.hobby = hobby;
	}
	public String getRemark() {
		return remark;
	}
	public void setRemark(String remark) {
		this.remark = remark;
	}
	@Override
	public String toString() {
		return "User [username=" + username + ", password=" + password + ", gender=" + gender + ", hobby="
				+ Arrays.toString(hobby) + ", remark=" + remark + "]";
	}
	
	
}

servlet

@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
		//post请求时,中文会乱码,解决方法setCharacterEncoding,只针对post请求有效
		request.setCharacterEncoding("UTF-8");
		
		//创建用户对象
		User user = new User();
		
		//获取请求参数
		/**
		 * key			value
		 * username		{"gyf"};
		 * password     {"123"};
		 * hobby		{"studying","money"}
		 */
		Map<String, String[]> map = request.getParameterMap();
		try {
			for(Entry<String, String[]> entry : map.entrySet()){
				String pname = entry.getKey();
				/*if(pname.equals("username")){
					user.setUsername(entry.getValue()[0]);
				}else if(pname.equals("hobby")){
					user.setHobby(entry.getValue());
				}*/
				
				//反射
				//创建属性描述器
				
				PropertyDescriptor pd = new PropertyDescriptor(entry.getKey(), User.class);
				
				//获取写入方法对象
				
				Method method = pd.getWriteMethod();
				
				//通过反射调用方法【怎么获取方法的参数类型】
				Class[] clzs = method.getParameterTypes();
				if(clzs.length == 0){
					return;
				}
				
				String clzName = clzs[0].toString();
				String[] values = entry.getValue();
				if(clzName.contains("[Ljava.lang.String")){
					System.out.println("数组...");
					method.invoke(user, (Object)values);
				}else{
					System.out.println("非数组...");
					method.invoke(user, values[0]);
				}
				
				/*String[] values = entry.getValue(); 这种方式有bug
				if(values.length == 1){
					method.invoke(user, values[0]);
				}else{
					method.invoke(user, (Object)values);
				}
				*/
			}
		
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println(user);
	}
获取表单数据三(调用第三方jar包,简单实用)

课程目标
1.掌握通过beanutils来封装请求参数
2.了解什么是Bean

JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取
在这里插入图片描述

获取原始表单数据(存在URL编码、解码问题)

通过InputStream来获取最原始的表单数据(用于doPost)
在这里插入图片描述

实现请求转发

 请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
 request对象提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
 request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理。

方法

 setAttribute方法
 getAttribute方法
 removeAttribute方法
 getAttributeNames方法
在这里插入图片描述

实现请求重定向

重定向机制的运作流程

1、用户在浏览器端输入特定URL,请求访问服务器端的某个组件
2、服务器端的组件返回一个状态码为302的响应结果。
3、当浏览器端接收到这种响应结果后,再立即自动请求访问另一个web组件
4、浏览器端接收到来自另一个web组件的响应结果。

HttpServeltResponse的sendRedirect(String location)用于重定向
在这里插入图片描述

13.会话

会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。

保存会话数据的两种技术

  1. Cookie

Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。

  1. Session

Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的HttpSession对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务

14.Cookie API(重点)

14.1javax.servlet.http.Cookie

 javax.servlet.http.Cookie类用于创建一个Cookie
 response接口中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。
 request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。

14.2Cookie类的方法:

 public Cookie(String name,String value)
 setValue与getValue方法
 setMaxAge与getMaxAge方法 (秒)
 setPath与getPath方法
 setDomain与getDomain方法
 getName方法

14.3Cookie类的一些属性

在这里插入图片描述

14.4客户端和服务端的请求,响应图解

在这里插入图片描述

14.5Cookie的maxAge方法

设置cookies的存活时间
在这里插入图片描述
maxAge:cookie的缓存时间。默认是-1,默认存在浏览器的缓存中。单位是秒
 负数:表示cookie的数据存在浏览器缓存中
 0:表示删除cookie
 正数:缓存在持久化磁盘上的时间

14.6Cookie细节

 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
 如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。

15.Session API(重点)

 Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。

15.1Session原理图

在这里插入图片描述

 每个浏览器存储自己的数据到Session中
 Session的数据是不能被其它浏览器共享的
 Session一般可用于判断用户是否登录

15.2Session的疑问

  • 服务器是如何实现一个session为一个用户浏览器服务的?

 服务器会为每个浏览器分配一个session ID,然后把Session ID通过Cookie的形式存储在客户端
在这里插入图片描述

15.3HttpSession常用方法

 把数据保存在HttpSession对象中,该对象也是一个域对象。

方法

 void setAttribute(String name,Object value);
 Object getAttribute(String name);
 void removeAttribute(String name);
 HttpSession.getId()
 setMaxInactiveInterval(int interval) 设置session的存活时间
 invalidate() 使此会话无效

15.4Session的状态

Session的状态三种:

创建:当浏览器第一次访问服务器动态资源就创建
活着:服务器应用运行时
死亡:
Session.invalidate();强制销毁
超时:默认30分钟
setMaxInactiveInterval(int )单位秒

15.5在Web.xml中配置Session的有效时间

在这里插入图片描述

15.6Session的持久化

持久化的优点:

节约内存空间;
确保在服务器重启或单个Web应用重启后,能回复重启前的会话;

持久化状态转化

Session在其生命周期中,可能会在运行时状态和持久化状态之间转换。

  • 搁置

会话从运行时状态变为持久化状态的过程称为 —— 搁置;
在以下情况下,Session会被搁置:
当服务器重启或单个Web应用终止时,Web应用中的Session会被搁置;
会话处于不活动状态的时间太长,达到了特定的限定值;
Web应用中处于运行状态的会话数目太多,达到了特定的限制值,部分Session被搁置

  • 激活
    会话从持久化状态变为运行时状态的过程称为激活;
    在以下情况下,Session会被激活:
    当服务器重启或单个Web应用重启时,Web应用中的Session会被激活
    处于Session中的客户端想Web应用发出HTTP请求,相应的Session会被激活

持久化对象系列化接口(注意)

当存在Session中的对象,重启tomcat时,如果有session对象,会出现下面的问题
表示对象不能被序列化,需要让Book实现序列化接口Serialize
在这里插入图片描述

练习题

1.用Servlet向浏览器输出“hello servlet”(Servlet快速入门案例)

第一步:
 创建一个hello的web工程
 然后写一个java类,实现servlet接口

在这里插入图片描述
第二步:
 在web.xml中配置Servlet映射
在这里插入图片描述
第三步:
 把hello工程部署到tomcat中运行
 然后浏览器访问http://localhost:8080/hello/Hello
在这里插入图片描述

2.向客户端输出中文

@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		// 设置响应类型和字符编码为UTF-8, 这样支持汉字显示
//		resp.setContentType("text/html");
//		resp.setCharacterEncoding("UTF-8");
		
		//设置客户编码类型
		resp.setHeader("Content-Type", "text/html;charset=utf-8");
		//不设置编译类型,默认编码发送数据 ISO-8859-1(没有中国二字编码),此时会发生乱码
		resp.getWriter().write("hello你好");
	}
 

3.文件下载

@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		String filename = "Java基础考试题卷.docx";
		
		//下载文件
		String path = "C:/Users/guoyongfeng/Desktop/upload/" + filename;
		FileInputStream fis = new FileInputStream(path);
		
		//设置请求头,文件名需要UTF-8编码
		filename = URLEncoder.encode(filename, "UTF-8");
		resp.setHeader("Content-disposition", "attachment;filename=" + filename);
		byte[] bs = new byte[1024];
		ServletOutputStream sos = resp.getOutputStream();
		int len = 0;
		while((len = fis.read(bs)) != -1){
			sos.write(bs, 0, len);
		}
		
		fis.close();
	}

4.手写验证码

 输出随机图片(CAPTCHA图像):Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的测试)
 相关主要类(JDK 查看API)
BufferedImage:内存图像
Graphics:画笔
ImageIO:输出图像

Servlet

@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		
		//创建内存对象
		BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
	
		//图像
		Graphics g = image.getGraphics();
		
		//设置背景
		g.setColor(Color.YELLOW);
		
		//填充
		g.fillRect(0, 0, WIDTH, HEIGHT);
		
		//设置边框
		g.setColor(Color.BLUE);
		
		//加干扰线
		for(int i=0;i<4;i++){
			int xStart = new Random().nextInt(WIDTH);
			int yStart = new Random().nextInt(HEIGHT);
			int xEnd = new Random().nextInt(WIDTH);
			int yEnd = new Random().nextInt(HEIGHT);
			g.drawLine(xStart, yStart, xEnd, yEnd);
		}
		
		int x = 5;
		//画随机数
		for(int i = 0; i< 4;i++){
			g.drawString(new Random().nextInt(9) + "", x, 20);
			x+=20;
		}
		
		//输出图片 设置响应格式
		resp.setContentType("image/jpeg");
		ImageIO.write(image, "JPEG",resp.getOutputStream());
	}

html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
 	function refreshCode(){
 		var imgTag = document.getElementById('code');
 		
 		console.log(imgTag.src);
 		
 		imgTag.src = '/day09_20180319/VerificationCode2?'+new Date().getTime();
 	}

</script>
</head>
<body>
<form action="">
	用户名:<input type="text"><br>
	密码:<input type="password"><br>
	验证码:<input type="text">
	<img id="code" alt="" src="/day09_20180319/VerificationCode2">
	<a href="javascript:refreshCode();">看不清,点击换个验证码</a>
</form>
</body>
</html>

在这里插入图片描述

5.工具类实现验证码

 @Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		//利用第三方jar包生成验证码   ValidateCode.jar
		 ValidateCode vc = new ValidateCode(120, 40, 4, 4);
		 
		 vc.write(response.getOutputStream());
	}

在这里插入图片描述

6.Cookie应用场景-记录上次访问时间

servlet

//Cookie应用场景-记录上次访问时间
@WebServlet("/Test01Servlet1")
public class Test01Servlet1 extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		//Cookie应用场景 :记录上次访问时间
		//设置响应时乱码问题
		response.setHeader("content-type", "text/html;charset=UTF-8");
		
		//4.获取请求头的Cookie
				Cookie[] cookies =  request.getCookies();
				if (cookies != null) {
					for(Cookie cookie : cookies){
						if ("zuihoufangwen".equals(cookie.getName())) {//只响应名称为zuihoufangwen的cookie
							System.out.println(cookie.getName()+":"+cookie.getValue());
							response.getWriter().write("最后访问的时间:"+cookie.getValue());
						}
					}
				}
				
		
		
		//1.创建一个Cookie对象
		Cookie cookie = new Cookie("zuihoufangwen", System.currentTimeMillis()+"");//技巧:整型+""变字符串
		
		//2.设置cookie的存活时间【单位是秒】
		 cookie.setMaxAge(30);//30秒
		
		//3.把Cookie返回给客户端【通过响应头传给客户端】
		 response.addCookie(cookie);
		 System.out.println(cookie);
		 
	}
}

客户端
在这里插入图片描述

7.Cookie应用场景-记住用户名

在这里插入图片描述
Step 01:创建一个login.jsp页面

<form action="/ServletReview/Test02Servlet1" method="post">
	<table border="1">
		<tr>
			<td>用户名:</td>
			<td><input id="username"  type="text" name="username"></td>
		</tr>
		<tr>
			<td>密码:</td>
			<td><input type="password" name="password"></td>
		</tr>
		<tr>
			<td colspan="2">
				<input  id="remember" type="checkbox" name="remember" value="true">记住用户名
			</td>
		</tr>
		<tr>
			<td colspan="2">
				<input type="submit" value="登录">
			</td>
		</tr>
	</table>
</form>

Step 02: 创建一个Servlet,然后设置Cookies

//Cookie应用场景-记住用户名
@WebServlet("/Test02Servlet1")
public class Test02Servlet1 extends HttpServlet {
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		/* 思路:
		 * 1.创建一个login.jsp页面
		 * 2.创建一个Servlet,设置Cookies
		 * 3.在login.jsp页面写js,实现记住用户名和密码
		 * */
		
		//设置响应编码格式,防止乱码
		response.setHeader("content-type", "text/html;charset=UTF-8");
		
		//1.获取表单请求参数 
				String username =  request.getParameter("username");
				String password =  request.getParameter("password");
				String remember =  request.getParameter("remember");
				
				//2.简单判断是否登录成功
				boolean success = "root".equals(username) && "root".equals(password);
				
				if(success){
					response.getWriter().write("登录成功");
				}else{
					response.getWriter().write("登录失败");
				}
				
				//3.把remember和username存在cookie中
				if("true".equals(remember) && success == true){
					
					Cookie ck1 = new Cookie("remember", remember);
					ck1.setMaxAge(60 * 60);//存活1个小时
					response.addCookie(ck1);
					
					Cookie ck2 = new Cookie("username", username);
					ck2.setMaxAge(60 * 60);//存活1个小时
					response.addCookie(ck2);
				}else{
					//消除cookie
					Cookie[] cks = request.getCookies();
					if(cks != null){
						for(Cookie c : cks){
							c.setMaxAge(0);//删除cookie
							response.addCookie(c);
						}
					}
					
				}
	}
}

Step 03: JSP页面通过写JS,显示登录名称

<script type="text/javascript">
	var cookies = document.cookie.split(';');
	//通过key获取cookie的值
	function getCK(mkey) {
		
		for(var i = 0;i<cookies.length;i++){
			var kv = cookies[i].split('=');
			//key有可能空字符串
			if( kv[0].trim() == mkey){
				return kv[1].trim();
			}
		}
		 
		return '';
	}
	
	window.onload = function(){
		var remember = getCK('remember');
		var username = getCK('username');
		
		console.log(remember + "---");
		console.log(username);
	 	if(remember == 'true'){//记住密码
			//console.log('记住密码');
			var userNameInput = document.getElementById('username');
			userNameInput.value = username;
			
			var rememberInput = document.getElementById('remember');
			rememberInput. checked="checked";
		}
	}
</script>

8.Cookie应用场景-查看书的浏览记录

在这里插入图片描述
实现步骤
Step1:写个书的模型(javaBean)

public class Book {
	private Integer id;
	private String name;
	private String auther;
	private String price;
	public Book() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Book(Integer id, String name, String auther, String price) {
		super();
		this.id = id;
		this.name = name;
		this.auther = auther;
		this.price = price;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAuther() {
		return auther;
	}
	public void setAuther(String auther) {
		this.auther = auther;
	}
	public String getPrice() {
		return price;
	}
	public void setPrice(String price) {
		this.price = price;
	}
	@Override
	public String toString() {
		return "Book [id=" + id + ", name=" + name + ", auther=" + auther + ", price=" + price + "]";
	}
	
	
}

Step2:写个DBUtil 模仿数据库数据

public class DBUtil {
	private static Map<Integer, Book> bookMap = new HashMap<Integer, Book>();
	
	static{
		bookMap.put(1, new Book(1, "Java基础", "刘勇", "25.3"));
		bookMap.put(2, new Book(2, "C基础", "刘慧", "35.3"));
		bookMap.put(3, new Book(3, "PHP基础", "刘申", "45.3"));
		bookMap.put(4, new Book(4, "JS基础", "李勇", "55.3"));
		bookMap.put(5, new Book(5, "R基础", "吴勇", "65.3"));
	}
	
	/**
	 * 得到所有的书
	 * @return
	 */
	public static Map<Integer, Book> getAllBook() {
		return bookMap;
	}
	
	/**
	 * 根据ID查询书
	 * @param id
	 * @return
	 */
	public static Book findBookById(Integer id) {
		return bookMap.get(id);
	}
}

Step3:写个showAllBookServelt

@WebServlet("/ShowAllBookServlet")
public class ShowAllBookServlet extends HttpServlet{
	
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		
		//消除响应乱码
		response.setHeader("content-type", "text/html;charset=UTF-8");//没写,客户端显示图书列表乱码
		
		//显示书的数据给浏览器
		PrintWriter pw = response.getWriter();
		pw.write("图书列表:");
		//获取存书的Map
		Map<Integer, Book> books= DBUtil.getAllBook();
		
		//得到键值对对象
		Set<Entry<Integer, Book>> entries = books.entrySet();
		System.out.println(request.getContextPath());
		//遍历集合
		for(Entry<Integer, Book> entry : entries){
			//取值
			Book book = entry.getValue();
			String aStr = "<br><a href='http://localhost:9090"+request.getContextPath()+"/ViewBookServlet?id="+book.getId()+"'>"+book.getName()+"</a>";
			pw.write(aStr);
		}
		
		//显示历史的浏览记录
		// 获取书的所有id
		Cookie[] cks = request.getCookies();
		pw.write("<br><br><br>历史浏览的书:<br>");
		
		if (cks!=null) {
			for(Cookie ck : cks){
				if(ck.getName().equals("lishishuId")){
					//1-5-4
					String lishishuId = ck.getValue();
					String[] ids = lishishuId.split("-");
					
					//通过id找到书
					for(String id : ids){
						Book book = DBUtil.findBookById(Integer.parseInt(id));
						pw.write("<br>" + book.getName());
					}
				}
			}
		}
	}
}

Step4:写个ViewBookServlet

@WebServlet("/ViewBookServlet")
public class ViewBookServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		/**
		 * 常识:从浏览器/客户端获取的所有数据都是字符串类型
		 */ 
		//消除响应乱码
		response.setHeader("content-type", "text/html;charset=UTF-8");
		
		//获取书的id
		String str_id = request.getParameter("id");
		
		//把字符串转成id
		int id = Integer.parseInt(str_id);
		
		PrintWriter pw = response.getWriter();
		
		pw.write("书的ID:"+id);
		
		
		//取出对应的书
		Book book = DBUtil.findBookById(id);
		pw.write("<br>"+book.toString());
		
		//把浏览的书的ID存在cookie中,也就通过cookie形式返回给客户端
		//获取客户端的Cookies
		Cookie[] cks = request.getCookies();
		if (cks == null) {
			System.out.println("未传过cookie");
			//存一个Cookie,三步走
			Cookie ck = new Cookie("lishishuId", str_id);
			ck.setMaxAge(60*60);
			response.addCookie(ck);
			
		} else {
			System.out.println("已传过cookie");
			//拼接bookid
			for(Cookie ck:cks){
				if ("lishishuId".equals(ck.getName())) {
					String lishishuId = ck.getValue();
					System.out.println("以前访问过的书的ID:"+lishishuId);
					
					if (lishishuId.equals(str_id)) {
						ck.setMaxAge(60*60);
						response.addCookie(ck);
						return;
					}
					//如果第一个是5,不执行下面代码
					//1-5-4-5 这种要把前面 -5去除
					//5-4-5
					//5
					if (lishishuId.startsWith(str_id)) {
						lishishuId = lishishuId.replaceAll(str_id+"-", "");
					} else {
						lishishuId = lishishuId.replaceAll("-"+str_id, "");
					}
					
					//拼接
					lishishuId += "-" + str_id;
					System.out.println("现在访问过的书的ID:"+lishishuId);
					
					//响应客户端
					ck.setValue(lishishuId);
					ck.setMaxAge(60*60);
					response.addCookie(ck);
				}
			}
			
		}
		
		
		
 	}
}

9.Session的应用场景-购物车实现

Step1购物列表ShowAllBookServlet

@WebServlet("/ShowAllBookServlet6")
public class ShowAllBookServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setHeader("content-type", "text/html;charset=utf-8");
		
	
		
		//1.显示书的数据给浏览器
		PrintWriter writer = response.getWriter();
		writer.write("购买图书列表:");
		for(Entry<Integer, Book>  entry : DBUtils.getAllBooks().entrySet()){
			Book book = entry.getValue();
			String aStr = "<br><a href='http://localhost:8888" + request.getContextPath() + "/BuyBookServlet?id="+ book.getId() +"'>" + book.getName() + " - 点击购买</a>";
			writer.write(aStr);
		}
		
	
		
	}
}

Step2购买Servlet

/**
 * 购物车实现方案:
 * 	1.把商品书存在session中
 *  问题:1.浏览器重新打开,以前数据不在了【把书的id放在cookie】
 *        2.当服务重启后,以前数据也不在【1.把书的id放在cookie,2.把session保存起来】
 * @author gyf
 *
 */
@WebServlet("/BuyBookServlet")
public class BuyBookServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setHeader("content-type", "text/html;charset=utf-8");
		
		//用户买书-书放在购物车【掌握】
		
		//1.获取书的ID
		String bookId = request.getParameter("id");
		
		//2.通过书id找到这本书
		Book book = DBUtils.findBookById(Integer.parseInt(bookId));
		
		//3.把这个书放在购物车List<Book> cart
		//3.1先从session取出购物车
		List<Book> cart =  (List<Book>)request.getSession().getAttribute("cart");
		
		//3.2如果没有购物车对象,就创建一个
		if(cart == null){
			System.out.println("当前session没有购物车");
			cart = new ArrayList<Book>();
		}else{
			System.out.println("当前session有购物车");
		}
		
		cart.add(book);
		
		//4.把这个list放在session中
		request.getSession().setAttribute("cart", cart);
		
		
		//5.显示购物车的信息
		PrintWriter writer = response.getWriter();
		writer.write("购物车信息:");
		for(Book b : cart){
			writer.write("<br>" + b.getName());
		}
		
		
		
		
	}
}

10.Session的应用场景-验证码登录

Step1:写一个验证码的Servlet

/**
 * 验证码的Servlet
 */
@WebServlet("/ValidateCodeServlet")
public class ValidateCodeServlet extends HttpServlet {
	
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		//1.生成验证
		ValidateCode vc = new ValidateCode(100, 30, 4, 6);
		System.out.println("生成的验证码:"  + vc.getCode());
		
		//如果客户端传来sessionid,但是服务端面没有对应id的session对象,这个方法没有返回值
		//If create is false and the request has no valid HttpSession, this method returns null. 
		/**
		 * request.getSession(false);
		 * 文档是,请求中没有一个合法的sessionid,这个方法就返回null
		 * 情况:浏览器传了sessionid,但是服务器已经没有这个sessionid对应的session对象,但是不是返回一个session对象
		 *       浏览器没传sessionid,服务器返回null
		 * 演示结果:
		 */
		HttpSession session = request.getSession();
		System.out.println(session);
		
		/*
		//2.把验证码存在session
		session.setAttribute("code", vc.getCode());
		
		//3.取session的ID
		System.out.println("SessionID:" + session.getId());
		
		//设置session的存活时间
		//request.getSession().setMaxInactiveInterval(30);
		*/
		vc.write(response.getOutputStream());
	}

	

}

Step2:写一个登录页面

<form action="/day11_2018_0322/LoginServlet" method="post">

<table border="1">
	<tr>
		<td>用户名</td>
		<td><input type="text" name="username"/> </td>
	</tr>
	<tr>
		<td>密码</td>
		<td><input type="password" name="password"/></td>
	</tr>
	<tr>
		<td>验证码</td>
		<td>
			<input type="text" name="code"/>
			<img alt="" src="/day11_2018_0322/ValidateCodeServlet">
		</td>
	</tr>
	<tr>
		<td colspan="2">
			<input type="submit" value="登录">
		</td>
	</tr>
</table>

</form>

Step3:写一个登录处理的Servlet

/**
 * Servlet implementation class LoginServlet
 */
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest requeset, HttpServletResponse response) 
			throws ServletException, IOException {
		
		response.setHeader("content-type", "text/html;charset=utf-8");
		//1.获取请求参数
		String username = requeset.getParameter("username");
		String password = requeset.getParameter("password");
		String client_code = requeset.getParameter("code");
		
		//2.获取服务端的code
		String server_code = (String) requeset.getSession().getAttribute("code");
		
		if(server_code == null){
			response.getWriter().write("请刷新上一个页面");
			return;
		}
		
		//3.验证码对比
		if(!server_code.equalsIgnoreCase(client_code)){
			response.getWriter().write("验证码不对");
		}else{
			
			if("gyf".equals(username) && "123".equals(password)){
				response.getWriter().write("登录成功");
			}else{
				response.getWriter().write("登录失败");
			}
		}
		
		
		//4.把验证码从服务器删除
		//requeset.getSession().removeAttribute("code");
		requeset.getSession().invalidate();//让session的所有数据都删除
		System.out.println(requeset.getSession().getAttribute("code"));
		
		
	}
}

注意:平时开发,表单使用POST请求,应该在POST请求中处理

面试题

1.请求重定向和转发的区别

请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。
重定向实现方式
response.sendRedirect()
实现原理:
302/307状态码和location头即可实现重定向

** 重定向特点**:1.地址栏会变,并发送2次请求,增加服务器负担
2.以前的request中存放的变量全部失效,并进入一个新的request作用域。

**转发特点:**1.地址栏不会变,客户端发送1次请求
2.以前的request中存放的变量不会失效,就像把两个页面拼到了一起。

2.include()、forward()、sendRedirect()的区别

 假定第一次请求的为servlet1,处理转发的为servlet2
 include()方法将请求转发给servlet2,servle2对该请求做出了的响应并入到原来的servlet1响应对象中,原来的servlet1还可以继续输出响应信息。
 forward方法将请求转发给其他的servlet2,servlet2负责对请求做出响应,而原先的servlet1的执行则终止。
 sendRedict()则是在浏览器请求servlet1之后,重新告诉浏览器将请求重新定位到servlet。

3.Session和Cookie的主要区别在于

 Cookie是把用户的数据写给用户的浏览器。
 Session技术把用户的数据写到用户独占的session中。

4.getSession():内部执行原理

HttpSession request.getSession():内部执行原理

1、获取名称为JSESSIONID的cookie的值。
2、没有这样的cookie,创建一个新的HttpSession对象,分配一个唯一的SessionID,并且向客户端写了一个名字为JSESSIONID=sessionID的cookie
3、有这样的Cookie,获取cookie的值(即HttpSession对象的值),从服务器的内存中根据ID找那个HttpSession对象:
找到了:取出继续为你服务。
找不到:从2开始。

HttpSession request.getSession(boolean create)

参数:
true:和getSession()功能一样。
false:根据客户端JSESSIONID的cookie的值,找对应的HttpSession对象,找不到返回null(不会创建新的,只是查询)。

总结

学习servlet,知道了他的生命周期,实现servlet最好的方式是继承HttpServlet(),知道了映射文件中通配符的使用,学习了单例的ServletContext对象,类似于Map,可以共享数组,有setAttribute,getAttribute,removeAttribute方法对数据进行操作。知道了HTTP的原理;重要的是request和response对象的使用,其中request利用getParameter和getParameterValues的获取客户端的表单数据或者请求参数;需要掌握请求转发和请求重定向的使用和区别;后面又学了Cookie和Session两个用来保存会话数据的方式,重点掌握练习题。另外记住客户端返回到后台的参数都是字符串型的,jsp中用post请求,则服务器就要用doPost处理。多敲代码!


版权声明:本文为qq_43488797原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。