【Servlet】 # servlet的入门概述

1. Servlet

1.1 什么是Servlet?

是⼀个遵循Servlet开发的java类。Servlet是由服务器调⽤的,运⾏在服务器端。

1.2 HTTP协议

超⽂本传输协议(HTTP,HyperText Transfer Protocol)是互联⽹上应⽤最为⼴泛的⼀种⽹络协议。所有的WWW⽂件都必须遵守这个标准。它是TCP/IP协议的⼀个应⽤层协议

HTTP协议就是客户端和服务器交互的⼀种通迅的格式。

HTTP1.0和HTTP1.1的区别

  • HTTP1.0协议,客户端与web服务器建⽴连接后,只能获得⼀个web资源【短连接,获取资源后就断开】
  • HTTP1.1协议,允许客户端与web服务器建⽴连接后,在⼀个连接上获取多个web资源【保持连接】

1.3 HTTP请求

浏览器向服务器请求某个web资源时,称之为浏览器服务器发送了⼀个http请求。

http请求应该包含三个部分:

  1. 请求⾏【描述客户端的请求⽅式、请求的资源名称,以及使⽤的HTTP协议版本号】
  2. 多个消息头【描述客户端请求哪台主机,以及客户端的⼀些环境信息等】
  3. ⼀个空⾏

请求行

  • GET:⽤来查询数据,提交速度快,在URL地址后附带的参数是有限制的,其数据容量通常不能超过1K。

  • POST:⽤来提交数据,可以在请求的实体内容中向服务器发送数据,传送的数据量⽆限制。

请求头

Accept: text/html,image/* Accept-Encoding: gzip,compress Accept-Charset: ISO-8859-1

1.4 HTTP响应

⼀个HTTP响应代表着服务器浏览器回送数据

HTTP响应应该包含四个部分:

  1. ⼀个状态⾏【描述服务器对请求的处理结果。】
  2. 多个消息头【描述服务器的基本信息以及数据,通过描述信息,可以通知客户端如何处理服务器回送的数据】
  3. ⼀个空⾏
  4. 实体内容【服务器向客户端回送的数据】

状态⾏

HTTP版本号状态码原因叙述
HTTP/1.1200OK
404客户端的请求有错误
500服务器有错误

响应头

Server:apache tomcat Content-Encoding: gzip Content-Type: text/html; charset=GB2312

2. servlet生命周期

  1. 加载Servlet。当Tomcat第⼀次访问Servlet的时候,Tomcat会负责创建Servlet的实例
  2. 初始化。当Servlet被实例化后,Tomcat会调⽤init()⽅法初始化这个对象
  3. 处理服务。当浏览器访问Servlet的时候,Servlet 会调⽤service()⽅法处理请求
  4. 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会⾃动调⽤**destroy()**⽅法,让该
    实例释放掉所占的资源。⼀个Servlet如果⻓时间不被使⽤的话,也会被Tomcat⾃动销毁
  5. 卸载。当Servlet调⽤完destroy()⽅法后,等待垃圾回收。如果有需要再次使⽤这个Servlet,会重
    新调⽤init()⽅法进⾏初始化操作。
  • 只要访问Servlet,service()就会被调⽤。

  • init()只有第⼀次访问Servlet的时候才会被调⽤。

  • destroy()只有在Tomcat关闭的时候才会被调⽤。

3. servlet编写

  • 继承HttpServlet,重写service方法
public class MyServlet extends HttpServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        String name = req.getParameter("name");
        // TODO
    }
}
  • 配置web.xml文件
    TIM截图20200430182222

4. 细节问题

4.1 servlet细节

4.1.1 ⼀个已经注册的Servlet可以被多次映射

<servlet>
    <servlet-name>Demo1</servlet-name>
    <servlet-class>lwclick.web.Demo1</servlet-class>
</servlet>

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

<servlet-mapping>
    <servlet-name>Demo1</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

4.1.2 Servlet映射的URL可以使⽤通配符

通配符有两种格式:

  1. *. 扩展名
  2. 正斜杠(/)开头并以“/*”结尾。
<servlet-mapping>
    <servlet-name>Demo1</servlet-name>
    <url-pattern>/*</url-pattern>
    
    <url-pattern>*.jsp</url-pattern>
</servlet-mapping>

4.1.3 Servlet是单例的

  • Servlet对象⼀旦创建了,就会驻留在内存中,为后续的请求做服务,直到服务器关闭。

  • 每次访问请求,都创建新的请求对象(HttpServletRequest)和响应对象(HttpServletResponse)

  • 多个⽤户访问Servlet的时候,服务器会为每个⽤户创建⼀个线程。当多个⽤户并发访问Servlet共享资源的时候就会出现线程安全问题。

    • 如果⼀个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}
    • 如果⼀个变量不需要共享,则直接在service()中定义.这样不会存在线程安全问题

4.1.4 load-on-startup

如果在<servlet>元素中配置了⼀个<load-on-startup> 元素,那么WEB应⽤程序在启动时,就会装载并创建Servlet的实例对象、以及调⽤Servlet实例对象的init()⽅法。

在这里插入图片描述

作用:

  1. 为web应⽤写⼀个InitServlet,这个servlet配置为启动时装载,为整个web应⽤创建必要的数据库表和数据
  2. 完成⼀些定时的任务【定时写⽇志,定时备份数据】

4.1.5 在web访问任何资源都是在访问servlet

⽆论在web中访问什么资源【包括JSP】,都是在访问Servlet。

没有⼿⼯配置缺省Servlet的时候,你访问静态图⽚,静态⽹⻚,缺省Servlet会在你web站点中寻找该图⽚或⽹⻚,如果有就返回给浏览器,没有就报404错误

4.2 ServletConfig对象

ServletConfig对象有什么⽤?

通过此对象可以读取web.xml中配置的初始化参数。能够让程序更加灵活【更换需求,更改配置⽂件web.xml即可,程序代码不⽤改】

<servlet>
    <servlet-name>Demo1</servlet-name>
    <servlet-class>zhongfucheng.web.Demo1</servlet-class>
    
    <init-param>
        <param-name>name</param-name>
        <param-value>zhongfucheng</param-value>
    </init-param>
</servlet>

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

TIM截图20200430203421

4.3 ServletContext对象

4.3.1 什么是ServletContext对象?

当Tomcat启动的时候,就会创建⼀个ServletContext对象。它代表着当前web站点

4.3.2 ServletContext有什么⽤?

  1. ServletContext既然代表着当前web站点,那么所有Servlet都共享着⼀个ServletContext对象,所以Servlet之间可以通过ServletContext实现通讯
  2. ServletConfig获取的是配置的是单个Servlet的参数信息ServletContext可以获取的是配置整个web站点的参数信息
  3. 利⽤ServletContext读取web站点的资源⽂件
  4. 实现Servlet的转发【不常用】

4.3.3 Servlet之间实现通讯

ServletContext对象可以被称之为域对象,可以简单理解成⼀个容器【类似于Map集合】

通过serAttribute实现通讯

//获取到ServletContext对象
ServletContext servletContext = this.getServletContext();

String value = "lwclick";

//MyName作为关键字,value作为值存进 域对象【类型于Map集合】
servletContext.setAttribute("MyName", value);
//获取ServletContext对象
ServletContext servletContext = this.getServletContext();

//通过关键字获取存储在域对象的值
String value = (String) servletContext.getAttribute("MyName");

System.out.println(value);

4.3.4 获取web站点配置的信息

  • web.xml⽂件⽀持对整个站点进⾏配置参数信息【所有Servlet都可以取到该参数信息】
<context-param>
    <param-name>name</param-name>
    <param-value>lwclick</param-value>
</context-param>
//获取到ServletContext对象
ServletContext servletContext = this.getServletContext();

//通过名称获取值
String value = servletContext.getInitParameter("name");
System.out.println(value);

4.3.5 读取资源文件

⽂件放在web⽬录下,直接通过⽂件名称就能获取(web --> WEB-INF --> 资源文件)

//获取到ServletContext对象
ServletContext servletContext = this.getServletContext();

//调⽤ServletContext⽅法获取到读取⽂件的流
InputStream inputStream = servletContext.getResourceAsStream("2.png");

5. Response对象

5.1. response、request对象

  • Tomcat收到客户端的http请求,针对每⼀次请求,分别创建⼀个代表请求的request对象代表响应的response对象

  • 获取浏览器提交过来的数据,找request对象

  • 向浏览器输出数据,找response对象

HttpServletResponse对象封装了http响应的信息。

5.2. HttpServletResponse的应⽤

5.2.1 调⽤getOutputStream()⽅法向浏览器输出数据

二进制数据

response.setHeader("Content-Type", "text/html;charset=UTF-8");
response.getOutputStream().write("你好呀我是中国".getBytes("UTF-8"));

5.2.2 调⽤getWriter()⽅法向浏览器输出数据

字符数据,不能输出⼆进制数据

response.setContentType("text/html;charset=UTF-8");

//获取到printWriter对象
PrintWriter printWriter = response.getWriter();
printWriter.write("看完博客点赞!");

5.2.3 实现⽂件下载

在web站点下有⼀个图⽚等文件

//获取到资源的路径
String path = this.getServletContext().getRealPath("/download/1.png");

//读取资源
FileInputStream fis = new FileInputStream(path);

//获取到⽂件名,路径在电脑上保存是\\形式的。
String fileName = path.substring(path.lastIndexOf("\\") + 1);

//设置消息头,告诉浏览器,我要下载1.png这个图⽚
response.setHeader("Content-Disposition", "attachment; filename="+URLEncoder.encode(fileName, "UTF-8")););

//把读取到的资源写给浏览器
int len = 0;
byte[] bytes = new byte[1024];
ServletOutputStream sos = response.getOutputStream();
while ((len = fis.read(bytes)) > 0) {
	sos.write(bytes, 0, len);
}

//关闭资源
servletOutputStream.close();
fileInputStream.close();

5.2.4 实现自动刷新

response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("3秒后跳转⻚⾯.....");

//三秒后跳转到index.jsp⻚⾯去,web应⽤的映射路径我设置成/,url没有写上应⽤名
response.setHeader("Refresh", "3;url='/index.jsp'");

5.2.5 设置缓存

【浏览器默认有缓存,对于需要实时更新的数据,取消缓存】
//浏览器有三消息头设置缓存,为了兼容性!将三个消息头都设置了
response.setDateHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");

5.2.6 实现数据压缩

GZIP类来对数据压缩,把压缩后的数据取出来,写给浏览器

5.2.7 生成随机图⽚

//在内存中⽣成⼀张图⽚,宽为80,⾼为20,类型是RGB
BufferedImage bImg = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);

//获取到这张图⽚
Graphics g = bImg.getGraphics();

//往图⽚设置颜⾊和字体
g.setColor(Color.white);
g.fillRect(0, 0, 80, 20);
g.setFont(new Font(null, Font.BOLD, 20));

//往图⽚上写数据,先写个12345,横坐标是0,纵坐标是20【⾼度】
graphics.drawString("12345", 0, 20);

//要往浏览器写⼀张图⽚,那要告诉浏览器回送的类型是⼀张图⽚
response.setHeader("ContentType", "jpeg");

//java提供了图⽚流给我们使⽤,这是⼀个⼯具类
//把图⽚传进去,类型是jpg,写给浏览器
ImageIO.write(bImg, "jpg", response.getOutputStream());
private String makeNum() {
    Random random = new Random();
    
    //这样就会⽣成0-7位的随机数,如果不够7位,我们加到7位就⾏了
    int anInt = random.nextInt(9999999);
    
    //将数字转成是字符串
    String num = String.valueOf(anInt);
    
    //判断位数有多少个,不够就加
    StringBuffer stringBuffer = new StringBuffer();
    for (int i = 0; i < 7 - num.length(); i++) {
    	stringBuffer.append("0");
    }
    
    return stringBuffer.append(num).toString();
}

5.2.8 重定向跳转

点击⼀个超链接,通知浏览器跳转到另外的⼀个⻚⾯就叫重定向跳转。

//重定向到index.jsp⻚⾯
response.sendRedirect("/lwclick/index.jsp");

重定向是通过302状态码和跳转地址实现的


6. Request对象

6.1. 什么是HttpServletRequest

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

6.2. 常用方法

  • getRequestURL 返回客户端发出请求时的完整URL
  • getQueryString 返回请求⾏中的参数部分
  • getRemoteAddr 返回发出请求的客户机的IP地址
  • getLocalAddr 返回WEB服务器的IP地址
  • getHeader 获得客户机请求头
  • getParameter 获得客户机请求参数(客户端提交的数据)

6.3. 应用

6.3.1 防盗链

不能使用复制粘贴网址,拿走网站上的资源

//获取到⽹⻚是从哪⾥来的
String referer = request.getHeader("Referer");

//如果不是从我的⾸⻚来或者从地址栏直接访问的,
if ( referer == null || !referer.contains("localhost:8080/lwclick/index.jsp") ) {
    //回到⾸⻚去
    response.sendRedirect("/lwclick/index.jsp");
    return;
}

//能执⾏下⾯的语句,说明是从我的⾸⻚点击进来的,那没问题,照常显示
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("路⻜做了xx");

6.3.2 表单提交数据【通过post⽅式提交数据】

点击提交按钮的时候,数据封装进了FormData中

http请求中把实体主体带过去了【传输的数据称之为实体主体】

既然request对象封装了http请求,所以request对象可以解析到发送过来的数据

于是只要把编码设置成UTF-8就可以解决乱码问题了。

<form action="/lwclick/formServlet" method="post">
    <table>
        <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="radio" name="gender" value=""><input type="radio" name="gender" value=""></td></tr>
        
        <tr><td>爱好</td>
            <td>
                <input type="checkbox" name="hobbies" value="游泳">游泳
                <input type="checkbox" name="hobbies" value="跑步">跑步
                <input type="checkbox" name="hobbies" value="⻜翔">⻜翔
            </td></tr>
        
        <input type="hidden" name="aaa" value="my name is lwclick">
        
        <tr><td>你的来⾃于哪⾥</td>
            <td>
                <select name="address">
                    <option value="⼴州">⼴州</option>
                    <option value="深圳">深圳</option>
                    <option value="北京">北京</option>
                </select>
            </td></tr>
        
        <tr><td>详细说明:</td>
            <td>
            	<textarea cols="30" rows="2" name="textarea"></textarea>
            </td></tr>
        
        <tr>
            <td><input type="submit" value="提交"></td>
            <td><input type="reset" value="重置"></td>
        </tr>
    </table>
</form>

在formServlet获取值

//设置request字符编码的格式
request.setCharacterEncoding("UTF-8");

//通过html的name属性,获取到值
String username = request.getParameter("username");
String password = request.getParameter("password");
String gender = request.getParameter("gender");

//复选框和下拉框有多个值,获取到多个值
String[] hobbies = request.getParameterValues("hobbies");
String[] address = request.getParameterValues("address");

//获取到⽂本域的值
String description = request.getParameter("textarea");

//得到隐藏域的值
String hiddenValue = request.getParameter("aaa");

....各种System.out.println().......

6.3.3 超链接⽅式提交数据(get方式) – 不推荐

get⽅式不同,它的数据是从消息⾏带过去的,没有封装到request对象⾥⾯

<a href="/lwclick/formServlet?username=xxx">使⽤超链接将数据带给浏览器</a>

    
//接收以username为参数名带过来的值
String username = request.getParameter("username");
System.out.println(username);

sendRedirect()方式

sendRedirect("servlet的地址?参数名="+参数值 &"参数名="+参数值);

中文乱码

//此时得到的数据已经是被ISO 8859-1编码后的字符串了,这个是乱码
String name = request.getParameter("username");

//乱码通过反向查ISO 8859-1得到原始的数据
byte[] bytes = name.getBytes("ISO8859-1");

//通过原始的数据,设置正确的码表,构建字符串
String value = new String(bytes, "UTF-8");

6.3.4 实现转发(不要在转发之前写数据给浏览器)

使⽤request的**getRequestDispatcher.forward(request,response)**实现转发,做到的功能也是⻚⾯跳转

//调⽤requestDispatcher对象的forward()实现转发,传⼊request和response⽅法
request.getRequestDispatcher("/index.jsp").forward(request, response);

实现servlet之间的通讯(尽量使用request)

//以username为关键字存lwclick值
request.setAttribute("username", "lwclick");

request.getRequestDispatcher("/reqServlet").forward(request, response);


//获取到存进request对象的值
String userName = (String) request.getAttribute("username");

6.4 转发和重定向的区别

  • 实际发⽣位置不同,地址栏不同

    • 转发是由服务器进⾏跳转的,在转发的时候,浏览器的地址栏没有发⽣变化的,实现转发只是⼀次的http请求,⼀次转发中request和response对象都是同⼀个。转发是对浏览器透明的。
    • 重定向是由浏览器进⾏跳转的,浏览器的地址会发⽣变化的。实现重定向会发出两个http请求,request域对象是⽆效的
  • 用法不同

    ​ 给服务器⽤的直接从资源名开始写,给浏览器⽤的要把应⽤名写上

    • request.getRequestDispatcher(" / 资源名 URI").forward(request,response)
      • 转发时"/"代表的是本应⽤程序的根⽬录
    • response.send(" / web应⽤/资源名 URI")
      • 重定向时"/"代表的是webapps⽬录
  • 能够去往的URL的范围不⼀样

    • 转发是服务器跳转只能去往当前web应⽤的资源
    • 重定向是浏览器跳转,可以去往任何的资源
  • 传递数据的类型不同

    • 转发的request对象可以传递各种类型的数据,包括对象
    • 重定向只能传递字符串
  • 跳转的时间不同

    • 转发:执⾏到跳转语句时就会⽴刻跳转
    • 重定向:整个⻚⾯执⾏完之后才执⾏跳转
  • 应⽤场景

    • 转发: 访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器⾥ URL 不变
    • 重定向: 提交表单,处理成功后 redirect 到另⼀个 jsp,防⽌表单重复提交,浏览器⾥ URL 变了

7. Cookie

7.1. 会话技术

⽤户开⼀个浏览器,访问⼀个⽹站,只要不关闭该浏览器,不管该⽤户点击多少个超链接,访问多少资源,直到⽤户关闭浏览器,整个这个过程我们称为⼀次会话.

7.2. Cookie

7.2.1 什么是cookie

⽹⻚之间的交互是通过HTTP协议传输数据的,⽽Http协议是无状态,不安全的协议(⼀旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建⽴新的连接。)

W3C提出了:给每⼀个⽤户都发⼀个通⾏证,⽆论谁访问的时候都需要携带通⾏证,这样服务器就可以从通⾏证上确认⽤户的信息。通⾏证就是Cookie

流程:

  • 浏览器访问服务器,如果服务器需要记录该⽤户的状态,就使⽤response向浏览器发送⼀个Cookie,浏览器会把Cookie保存起来
  • 当浏览器再次访问服务器的时候,浏览器会把请求的⽹址连同Cookie⼀同交给服务器。

7.2.2 常⽤的Cookie⽅法

  • public Cookie(String name,String value)
  • setValue与getValue⽅法
  • setMaxAge与getMaxAge⽅法
  • setPath与getPath⽅法
  • setDomain与getDomain⽅法
  • getName⽅法
//设置response的编码
response.setContentType("text/html;charset=UTF-8");

//创建Cookie对象,指定名称和值
Cookie cookie = new Cookie("username", "lwclick");

//设置Cookie的时间
cookie.setMaxAge(1000);

//向浏览器给⼀个Cookie
response.addCookie(cookie);
response.getWriter().write("我已经向浏览器发送了⼀个Cookie");

7.3 Cookie细节

7.3.1 不可跨域名性

浏览器通过域名判断⼀个⽹站是否能操作另⼀个⽹站的Cookie。

即使⼀级域名相同,⼆级域名不同,也不能获取到Cookie

7.3.2 Cookie保存中⽂

response.setContentType("text/html;charset=UTF-8");
PrintWriter printWriter = response.getWriter();

String name = "中国";
Cookie cookie = new Cookie("country", URLEncoder.encode(name, "UTF-8"));
cookie.setMaxAge(2000);

//⼀定不要忘记添加到浏览器中
response.addCookie(cookie);

printWriter.write("我颁发了⼀个Cookie,值保存的是中⽂数据");


【取出CookieCookie[] cookies = request.getCookies();

for (int i = 0; cookies != null && i < cookies.length; i++) {
    String name = cookies[i].getName();
    
    //经过URLEncoding就要URLDecoding
    String value = URLDecoder.decode(cookies[i].getValue(), "UTF-8");
    printWriter.write(name + "------" + value);
}

7.3.3 Cookie的有效期

有效期通过setMaxAge()来设置

  • MaxAge为正数,浏览器将Cookie写到硬盘中,只要还在MaxAge秒之前,登陆⽹站时该Cookie就有效【不论关闭了浏览器还是电脑】

  • MaxAge为负数,Cookie是临时性的,仅在本浏览器内有效

  • MaxAge为0,则表示删除该Cookie(需要添加到浏览器中)。

7.4 Cookie的应⽤

7.4.1 显示⽤户上次访问的时间

用户每次登陆的时候,取到Cookie保存的值,再更新下Cookie的值。

访问Serlvet有两种情况

  • 第⼀次访问
  • 已经访问过了
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

response.setContentType("text/html;charset=UTF-8");
PrintWriter printWriter = response.getWriter();

//获取⽹⻚上所有的Cookie
Cookie[] cookies = request.getCookies();

//判断Cookie的值是否为空
String cookieValue = null;

for (int i = 0; cookies != null && i < cookies.length; i++) {
    
    //获取到以time为名的Cookie
    if (cookies[i].getName().equals("time")) {
        printWriter.write("您上次登陆的时间是:");
        cookieValue = cookies[i].getValue();
        printWriter.write(cookieValue);
        
        cookies[i].setValue(simpleDateFormat.format(new Date()));
        response.addCookie(cookies[i]);
        //既然已经找到了就可以break循环了
        break;
    }
}

//如果Cookie的值是空的,那么就是第⼀次访问
if (cookieValue == null) {
    //创建⼀个Cookie对象,⽇期为当前时间
    Cookie cookie = new Cookie("time", simpleDateFormat.format(new Date()));
    
    //设置Cookie的⽣命期
    cookie.setMaxAge(20000);
    
    //response对象回送Cookie给浏览器
    response.addCookie(cookie);
    printWriter.write("您是第⼀次登陆啊!");
}

7.4.2 显示上次浏览过商品

以书籍为例⼦!⾸先设计Book对象


8. Session

另⼀种记录浏览器状态的机制,Cookie保存在浏览器中,Session保存在服务器中。

8.1. Session API

  • String getId(); 【获取Session的id】
  • void setMaxInactiveInterval(int var1); 【设置Session超时时间】
  • int getMaxInactiveInterval(); 【获取Session超时时间】
  • Object getAttribute(String var1); 【获取Session属性】
  • Enumeration getAttributeNames(); 【获取Session所有的属性名】
  • void setAttribute(String var1, Object var2); 【设置Session属性】
  • void removeAttribute(String var1); 【移除Session属性】
  • void invalidate(); 【销毁该Session】

8.2. Session作为域对象

//得到Session对象
HttpSession httpSession = request.getSession();

//设置Session属性
httpSession.setAttribute("name", "看完博客就要点赞!!");
//获取到从上面Servlet的Session存进去的值
HttpSession httpSession = request.getSession();
String value = (String) httpSession.getAttribute("name");

System.out.println(value);

8.3. Session的⽣命周期和有效期

  • ⽤户第⼀次访问服务器Servlet,jsp等动态资源就会被⾃动创建,Session对象保存在内存⾥

  • Session⽣成后,只要⽤户继续访问,服务器就会更新Session的最后访问时间,⽆论是否对Session进⾏读写,服务器都会认为Session活跃了⼀次。

  • 为了防⽌内存溢出,服务器会把⻓时间没有活跃的Session从内存中删除,这个时间也就是Session的超时时间。(默认30分钟)

    //设置Session最⻓超时时间为60秒,这⾥的单位是秒
    httpSession.setMaxInactiveInterval(60);
    

8.4. Session的实现原理

Session 之所以可以识别不同的⽤户,依靠的就是Cookie

该Cookie是服务器⾃动颁发给浏览器的,不⽤⼿⼯创建的。该Cookie的maxAge值默认是-1,也就是说仅当前浏览器使⽤,不将该Cookie存在硬盘中

当我们访问Servlet1的时候,服务器就会创建⼀个Session对象,执⾏我们的程序代码,并⾃动颁发个Cookie给⽤户浏览器

当我们⽤同⼀个浏览器访问Servlet2的时候,浏览器会把Cookie的值通过http协议带过去给服务器,服务器就知道⽤哪⼀Session。

⽽当我们使⽤新会话的浏览器访问Servlet2的时候,该新浏览器并没有Cookie,服务器⽆法辨认使⽤哪⼀个Session,所以就获取不到值

禁⽤了Cookie,通过URL地址重写

  • encodeURL(String url)
  • encodeRedirectURL(String url)

8.5. Session案例

8.5.1 使⽤Session完成⽤户简单登陆

创建User类

private String username = null;
private String password = null;
public User() {
}
public User(String username, String password) {
    this.username = username;
    this.password = password;
}
....各种set、get⽅法

集合模拟⼀个数据库

private static List<User> list = new ArrayList<>();
//装载些数据进数据库
static {
    list.add(new User("aaa","111"));
    list.add(new User("bbb","222"));
    list.add(new User("ccc","333"));
}
//通过⽤户名和密码查找⽤户
public static User find(String username, String password) {
    for (User user : list) {
        if (user.getUsername().equals(username) &&
            user.getPassword().equals(password)) {
            	return user;
        }
    }
    return null;
}

表单提交

<form action="/ouzicheng/LoginServlet" method="post">
    ⽤户名:<input type="text" name="username"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="submit" value="提交">
</form>

获取到表单提交的数据,查找数据库是否有相对应的⽤户名和密码。

String username = request.getParameter("username");
String password = request.getParameter("password");

User user = UserDB.find(username, password);
//如果找不到,就是⽤户名或密码出错了。
if (user == null) {
    response.getWriter().write("you can't login");
    return;
}

//标记着该⽤户已经登陆了!
HttpSession httpSession = request.getSession();
httpSession.setAttribute("user", user);
//跳转到其他⻚⾯,告诉⽤户成功登陆了。

response.sendRedirect(response.encodeURL("index.jsp"));

8.5.2 利⽤Session防⽌表单重复提交

  • 在session域中存储⼀个token
  • 然后前台⻚⾯的隐藏域获取得到这个token
  • 在第⼀次访问的时候,我们就判断seesion有没有值,如果有就⽐对。对⽐正确后我们就处理请求,接着就把session存储的数据给删除了
  • 等到再次访问的时候,我们session就没有值了,就不受理前台的请求了!

8.5.3 一次性校验码

⽣成验证码后,把验证码的数据存进Session域对象中,判断⽤户输⼊验证码是否和Session域对象的数据⼀致。

//在内存中⽣成图⽚
BufferedImage bImg = new BufferedImage(80, 20,BufferedImage.TYPE_INT_RGB);

//获取到这张图⽚
Graphics2D gp2D = (Graphics2D) bImg.getGraphics();

//设置背景⾊为⽩⾊
gp2D.setColor(Color.white);
gp2D.fillRect(0, 0, 80, 20);

//设置图⽚的字体和颜⾊
gp2D.setFont(new Font(null, Font.BOLD, 20));
gp2D.setColor(Color.BLUE);

//⽣成随机数
String randomNum = makeNum();

//往这张图⽚上写数据,横坐标是0,纵坐标是20
gp2D.drawString(randomNum, 0, 20);

//将随机数存进Session域中
request.getSession().setAttribute("randomNum", randomNum);

//控制浏览器不缓存该图⽚
response.setHeader("Expires", "-1");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");

//通知浏览器以图⽚的⽅式打开
response.setHeader("Content-type", "image/jpeg");

//把图⽚写给浏览器
ImageIO.write(bImg, "jpg", response.getOutputStream());
private String makeNum() {
    Random random = new Random();
    
    //⽣成0-6位的随机数(6个9)
    int num = random.nextInt(999999);
    
    //验证码的数位全都要6位数,于是将该随机数转换成字符串,不够位数就添加
    String randomNum = String.valueOf(num);
    
    //使⽤StringBuffer来拼凑字符串
    StringBuffer stringBuffer = new StringBuffer();
    for (int i = 0; i < 6 - randomNum.length(); i++) {
    	stringBuffer.append(i);
    }
    
    return stringBuffer.append(randomNum).toString();
}

显示页面

<form action="/ouzicheng/Login2Servlet">
    ⽤户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    验证码:<input type="text" name="randomNum">
    <img src="/ouzicheng/ImageServlet" ><br><br>
    
    <input type="submit" value="提交">
</form>

处理提交表单数据的Servlet,判断⽤户带过来验证码的数据是否和Session的数据相同。

//获取⽤户输⼊验证码的数据
String client_randomNum = request.getParameter("randomNum");

//获取Session中的数据
String session_randomNum = (String) request.getSession().getAttribute("randomNum");

//判断他俩数据是否相等,⽤户是否有输⼊验证码,Session中是否为空
if (client_randomNum == null || session_randomNum == null || !client_randomNum.equals(session_randomNum)) {
    System.out.println("验证码错误了!!!");
    return ;
}
//下⾯就是验证⽤户名和密码...................

8.6. Session和Cookie的区别

  • 从存储⽅式上⽐较
    • Cookie只能存储字符串,如果要存储⾮ASCII字符串还要对其编码。
    • Session可以存储任何类型的数据,可以把Session看成是⼀个容器
  • 从隐私安全上⽐较
    • Cookie存储在浏览器中,对客户端是可⻅的。信息容易泄露出去。如果使⽤Cookie,最好将Cookie加密
    • Session存储在服务器上,对客户端是透明的。不存在敏感信息泄露问题。
  • 从有效期上⽐较
    • Cookie保存在硬盘中,只需要设置maxAge属性为⽐较⼤的正整数,即使关闭浏览器,Cookie还是存在的
    • Session的保存在服务器中,设置maxInactiveInterval属性值来确定Session的有效期。
      • 并且Session依赖于名为JSESSIONID的Cookie,该Cookie默认的maxAge属性为-1。
      • 如果关闭了浏览器,该Session虽然没有从服务器中消亡,但也就失效了。
  • 从对服务器的负担⽐较
    • Session是保存在服务器的,每个⽤户都会产⽣⼀个Session,如果是并发访问的⽤户⾮常多,是不能使⽤Session的,Session会消耗⼤量的内存。
    • Cookie是保存在客户端的。不占⽤服务器的资源。像baidu、Sina这样的⼤型⽹站,⼀般都是使⽤Cookie来进⾏会话跟踪。
  • 从浏览器的⽀持上⽐较
    • 如果浏览器禁⽤了Cookie,那么Cookie是⽆⽤的了!
    • 如果浏览器禁⽤了Cookie,Session可以通过URL地址重写来进⾏会话跟踪。
  • 从跨域名上⽐较
    • Cookie可以设置domain属性来实现跨域名
    • Session只在当前的域名内有效,不可夸域名

8.7. session和cookie共同使用

把Cookie保存在硬盘中,即使我关闭了浏览器,浏览器再次访问⻚⾯的时候,可以带上Cookie,从⽽服务器识别出Session。

Cookie cookie = new Cookie("JSESSIONID",session.getId());
cookie.setMaxAge(30*60);
cookie.setPath("/lwclick/");
response.addCookie(cookie);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LRcoding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值