知识点
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实现的三种方式
- 实现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");
}
}
- 继承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....");
}
}
- 继承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资源,然后关闭浏览器,整个过程称之为一个会话。
保存会话数据的两种技术
- Cookie
Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
- 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处理。多敲代码!