JSP
简介
Java Server Pages 是Java的动态网页技术 .
JSP引擎
引擎原理:
JSP引擎用于将JSP文件, 转换为Servlet
1. 在服务器启动时 , JSP引擎读取.jsp文件.
2. 将文件转换为Servlet的代码 , 并给servlet添加映射地址为 jsp的文件名称.
3. 当用户浏览器访问 jsp文件名称时, 其实请求的不是jsp文件, 而是生成的servlet
4. servlet负责给浏览器进行响应.
JSP语法结构 *
JSP文件保存的路径: webContent目录下
JSP文件 可以包含HTML代码, Java代码, 以及JSP特有的标记.
Java代码 声明区 *
指的是Java的成员代码位置, 在JSP声明区中编写的Java代码, 会生成到servlet的成员位置.
语法:
<%!
这里用于编写Java代码, 且会生成到声明区
%>
Java代码 执行区 ***
指的是Servlet的service方法中. 用户每次请求 都会执行
语法:
<%
Java代码
%>
表达式 ***
用于快速的将Java代码中的变量输出到网页中.
语法:
<%=变量名%>
转换的Java:
out.print(变量名);
JSP中如何编写注释
因为JSP文件包含了三种语法结构 (java/html/jsp)
所以 ,三种语法结构的注释 都可以起到注释的效果:
html注释:
<!-- html的注释 -->
在JSP中, HTML的注释会被JSP引擎认为是HTML代码 , 会转换为out.write("<!-- -->");
Java注释:
单行: //
多行: /* */
文档: /** */
在JSP中, Java的注释 会被JSP引擎认为是Java代码 ,会原封不动的放到_jsp.java文件中.
JSP注释:
<%-- JSP注释 --%>
在JSP引擎将jsp文件 转换为.java文件时, 会忽略JSP注释的部分.
JSP 三大指令
指令的格式:
<%@ 指令名称 属性名=值 属性名2=值 ... 属性n=值 %>
page指令
用于设置页面
完整格式:
<%@ page
language="java"
extends="继承的类"
buffer="数值|none" -- 缓冲大小 ,none表示不缓冲 ,默认是8kb
session="true|false" --true:自动创建session false时表示不自动创建
autoFlush="true|false" --true:缓冲器自动清除, 默认true
isThreadSafe="true|false" --<%%>中的代码 是否是同步的 , true表示同步, 默认false
contentType="text/html;charset=utf-8" -- 内容类型以及编码格式
errorPage="网页地址" -- 当JSP代码出错误,页面由指定地址进行显示 *
isErrorPage="true|false" --true:当前页面是处理错误的页面. 只有为true时, 才可以查看异常信息. *
import="导包列表" -- 属性值是一个或多个导入的包,包于包之间使用逗号隔开即可
%>
指定项目全局错误页面
编写项目的web.xml
在根节点中, 加入子节点:
<error-page>
<error-code>404</error-code>
<location>处理404的页面地址</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>处理500的页面地址</location>
</error-page>
include 指令 熟悉
用于将一个JSP 或 HTML文件 引入到另一个JSP中.
格式:
<%@ include file="引入的路径" %>
include 动作
用于将一个 JSP 或 HTML文件 引入到另一个JSP中.
格式:
<jsp:include page="引入的路径" flush="true"/>
include 指令 与 include动作得到区别 *****
include指令: 在JSP程序的转换时期, 将被引入的JSP文件嵌入到include指令的位置, 然后统一编译执行 (最终生成了一个.java文件)
include动作: 在JSP程序的转换时期, 被引入的文件不会嵌入到include动作的位置, 而是等客户端请求时, 再临时将被引入的文件以额外的servlet的方式加载到响应中, (最终生成的是多个.java文件)
JSP的内置对象 (隐含对象)
内置对象指的是: JSP引擎在转换JSP文件时, 帮我们的代码在执行之前 创建的一些 供我们使用的对象,
内置对象具备大量的JSP中的常用功能 , 使用JSP内置对象可以大大的简化我们的开发流程.
JSP的九大内置对象 *****
request:
类型: HttpServletRequest
作用: 请求对象, 包含了请求相关的信息和参数.
response:
类型: HttpServletResponse
作用: 响应对象, 包含了一些用于响应的功能.
out:
类型: JSPWriter
作用: 是打印流, 用于向响应体中输出数据。
session:
类型: HttpSession
作用: 会话对象, 用于状态管理以及 会话跟踪。
application:
类型: ServletContext
作用: Servlet的上下文,一个应用内存中同时只有一个上下文对象. 用于多个Servlet/JSP之间通信 ,
------
config:
类型: ServletConfig
作用: Servlet的配置对象, 用于配置一些初始的键值对信息.
pageContext:
类型: PageContext
作用: 页面的上下文 , 每一个JSP都拥有一个上下文对象, 用于多段代码之间进行通信.
exception:
类型: Throwable
作用: 当页面的page指令中 isErrorPage属性值为true时, 才会存在此对象, 用于收集错误信息!
通常此对象值为null , 只有其他页面指定errorPage=当前页面时, 且其他页面发生BUG后,跳转到此页面时,对象才不为null
page:
类型: Object
作用: 指当前JSP页面自身 , 在JSP引擎生成的代码中 , page对象的赋值代码为:
Object page = this;
JSP的四大域对象 *****
九大隐含对象中, 包含了四个较为特殊的隐含对象, 这四个对象我们称其为域对象,
它们都具备存储数据 / 删除数据 / 获取数据 的方法:
存储数据:
setAttribute(String key ,Object value);
获取数据:
Object value = getAttribute(String key);
删除数据:
removeAttribute(String key);
这四个域对象 , ‘域’ 指的是作用域 ! 分别是:
pageContext : 页面的上下文 作用域: 一个JSP页面
request : 请求对象 作用域: 一次请求 (请求可以被转发 , 一次请求可能包含多个JSP页面)
session : 会话对象 作用域: 一次会话 (一次会话可能包含多次请求)
application : servlet上下文对象 作用域: 一次服务(服务器的启动到关闭) (一次服务可能包含多次会话)
JSP useBean动作 熟悉
作用: 向四大域对象中, 存储bean对象.
格式:
<jsp:useBean
id="存储时的key"
scope="page/request/session/application"
class="要存储的对象类型">
</jsp:useBean>
JSP useBean+ setProperty动作
作用: 向四大域对象中, 存储bean对象. 且设置属性值
格式:
<jsp:useBean
id="存储时的key"
scope="page/request/session/application"
class="要存储的对象类型">
</jsp:useBean>
<jsp:setProperty name="存储时的key" property="属性名" value="属性值"/>
//setProperty动作可以出现多次
案例:
<jsp:useBean id="p" scope="page" class="cn.xdl.bean.Person"></jsp:useBean>
<jsp:setProperty name="p" property="name" value="刘健健"/>
<jsp:setProperty name="p" property="age" value="18"/>
JSP useBean+ setProperty动作 快速获取表单提交的内容 了解
格式:
<jsp:useBean
id="存储时的key"
scope="page/request/session/application"
class="要存储的对象类型">
</jsp:useBean>
<jsp:setProperty name="存储时的key" property="属性名"/>
注意:
如果setProperty动作中 , property的值即是对象的属性名, 又是我们用户请求的参数的名称的话, 会自动将参数获取到并赋值给对象
JSP getProperty动作 了解
作用:
从四大域对象中 取出某个对象的属性, 并显示到网页中
语法格式:
<jsp:getProperty name="对象的key" property="属性名">
案例:
<jsp:useBean id="per" scope="session" class="cn.xdl.bean.Person"></jsp:useBean>
<jsp:setProperty property="name" name="per" value="小明"/>
<jsp:setProperty property="age" name="per" value="28"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<jsp:getProperty property="name" name="per"/>
JSTL
通过重写URL , 得到session
//1. 重写普通的网址 , 通常是超链接/表单中使用的网址
//url = response.encodeUrl(url);
//2. 重写重定向的网址, 这种网址用于重定向
url = response.encodeRedirectUrl(url);
EL表达式 ***
作用:
用于快速的从域对象中取出数据, 并将结果输出到网页.
也可以用于一些运算, 运算的结果也会输出到网页
格式:
${表达式}
例如: 用于运算:
运算的结果:${ 1+2+3+4+5 }
用于取出域对象中的数据
访问存储数据的格式:
${存储的key}
访问存储的对象属性值:
静态取值:${存储的key.属性名}
静态取值:${存储的key["属性名"]}
动态取值:${存储的key[属性名的key]}
访问集合/数组中的对象
静态取值:${存储的key[下标].属性名}
静态取值:${存储的key[下标]["属性名"]}
动态取值:${存储的key[下标][属性名的key]}
有时我们需要使用变量的值. 可以将其先存储到域对象中, 再在el表达式中使用存储的key来操作
例如:
<%
User[] data = {new User("root1","123"),new User("root2","223"),new User("root3","323"),new User("root4","423"),};
pageContext.setAttribute("users", data);
pageContext.setAttribute("a","username");
%>
<%for(int i=0;i<data.length;i++){
pageContext.setAttribute("index", i);
%>
静态取值: ${ users[index].username }
<%} %>
EL表达式 取出数据的流程. *
取出的顺序: 范围从小到大
步骤:
1. 先从pageContext中寻找数据是否存在
2. 当pageContext中不存在此数据时 , 去request中寻找数据是否存在
3. 当request中不存在此数据时, 去session中寻找数据是否存在
4. 当session中不存在此数据时, 去application中寻找数据是否存在.
5. 当application中不存在此数据时, 向网页输出 长度为0的字符串 (是"" , 不是null)
在上述的流程中, 一旦某一个步骤寻找到了数据 , 就会将数据输出到网页中, 且后续流程不再执行.
taglib指令
用于在JSP中引入标签库 .
语法格式:
<%@ taglib prefix="" uri=""%>
属性: prefix: 是引入的标签库的名称, 用于区分多个标签库. 在使用此标签库中的标签时, 需在标签前添加标签库名称:
例如: 我们引入一个标签库, prefix=a ,则其中的标签在使用时: <a:标签名></a:标签>
uri: 用于匹配标签库. 在引入的tld文件中存在一个uri属性值, 我们uri属性与tld文件中的属性相同时, 则引入这个文件描述的库.
JSTL标签库
是一套JSP的标准标签库. 对JSP的标签进行了扩展. IF标签 作用: 用于判断元素是否显示
格式:
<库的名称:if test="">></库的名称:if>
test属性值:
可以是boolean值, 或运算结果为boolean的el表达式 .
案例:
<%-- <heiheihei:if test="true">
从前有座山
</heiheihei:if>
<heiheihei:if test="false">
山上有座庙
</heiheihei:if> --%>
<%
pageContext.setAttribute("flag", false) ;
%>
<heiheihei:if test="${ flag }">
从前有座山
</heiheihei:if>
<%
pageContext.setAttribute("flag",true);
%>
<heiheihei:if test="${ flag }">
山上有座庙
</heiheihei:if>
choose+when+otherwise标签
类似Java中的: switch+case+default
这三个标签, 只有when是由test属性的 ,属性值是boolean值, 允许使用el表达式传入
作用:
用于多分支 显示 .
案例:
<%
pageContext.setAttribute("flag",100);
%>
<heiheihei:choose>
<heiheihei:when test="${ flag== 1}">
拔毛烧开水 , 铁锅炖大鹅
</heiheihei:when>
<heiheihei:when test="${ flag== 2}">
英雄不问出处, 流氓不论岁数
</heiheihei:when>
<heiheihei:when test="${ flag== 3}">
衣带渐宽终不悔
</heiheihei:when>
<heiheihei:when test="${ flag== 4}">
好吃不过饺子 , 好闻不过包子
</heiheihei:when>
<heiheihei:otherwise>
二十四桥明月夜 , 玉人何处教吹箫 .
</heiheihei:otherwise>
</heiheihei:choose>
forEach标签
用于遍历集合 或 数组元素
格式:
<标签库名称:forEach items="" var="">
</标签库名称:forEach>
属性:
items : 要遍历的数组 或 集合 必须通过el表达式传递
var : 在循环遍历时, 从数组或集合中取出的每一个元素会被存储到pageContext中, key就是var的值
案例:
<%
ArrayList<String> d = new ArrayList<>();
d.add("拔毛烧开水 , 铁锅炖大鹅 ");
d.add("英雄不问出处, 流氓不论岁数 ");
d.add("衣带渐宽终不悔 ");
d.add("好吃不过饺子 , 好闻不过包子 ");
d.add("二十四桥明月夜 , 玉人何处教吹箫 . ");
d.add("厚积薄发");
d.add("日月之行若出其中");
d.add("锄禾日当午 , 复方草珊瑚");
//将集合存储到域对象pageContext中, 便于EL表达式取出使用
pageContext.setAttribute("data",d);
%>
<heiheihei:forEach items="${ data }" var="x">
<h4>${x }</h4>
</heiheihei:forEach>
如何自定义标签库 熟悉
自定义标签库的类:
两种实现方式:
1. 继承SimpleTagSupport
2. 继承TagSupport
SimpleTagSupport案例
public class MyTag1 extends SimpleTagSupport {
private static ArrayList<String> d = new ArrayList<>();
static {
d.add("拔毛烧开水 , 铁锅炖大鹅 ");
d.add("英雄不问出处, 流氓不论岁数 ");
d.add("衣带渐宽终不悔 ");
d.add("好吃不过饺子 , 好闻不过包子 ");
d.add("二十四桥明月夜 , 玉人何处教吹箫 . ");
d.add("厚积薄发");
d.add("日月之行若出其中");
d.add("锄禾日当午 , 复方草珊瑚");
}
/**
* 当用户使用此标签
*/
@Override
public void doTag() throws JspException, IOException {
//1. 先得到向网页输出数据的 输出流
JspWriter jw = getJspContext().getOut();
//2. 得到要输出的文本
String text = getText();
//3. 将文本输出
jw.write(text);
jw.flush();
}
public String getText() {
//生成随机数, 从集合中随机取出一句名言
Random r = new Random();
int index = r.nextInt(d.size());
return d.get(index);
}
}
TagSupport 案例:
public class MyTag2 extends TagSupport {
/* @Override
public int doAfterBody() throws JspException {
// TODO Auto-generated method stub
return super.doAfterBody();
}
@Override
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
return super.doEndTag();
}*/
/**
* 总数量
*/
private int length;
/**
* 一页显示的数量
*/
private int pageSize;
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
@Override
public int doStartTag() throws JspException {
//总页数
int num = length/pageSize + (length%pageSize==0?0:1);
String html = "<div class=\"page\">"
+ "<button>首页</button> "
+ "<button>上一页</button> ";
if(num < 5) {
for(int i=0;i<num;i++) {
html = html+"<button>"+(i+1)+"</button>";
}
}else {
html = html+"<button>1</button>";
html = html+"<button>2</button>";
html = html+"<button>3</button>";
html = html+" ... ";
html = html+"<button>"+num+"</button>";
}
html = html+ " <button>下一页</button> "
+ "<button>末页</button> "
+ "</div>";
JspWriter jw = pageContext.getOut();
try {
jw.write(html);
jw.flush();
} catch (IOException e) {
e.printStackTrace();
}
return super.doStartTag();
}
}
xld文件:
xld文件存储在web-inf中!
案例:
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 标签库的描述 -->
<description>
这个标签库编写的目的是为了 , 不知道为啥.
</description>
<!-- 设置标签库名称 -->
<display-name>xdl</display-name>
<!-- 描述标签库的版本 -->
<tlib-version>10.1</tlib-version>
<!-- 描述标签库的短名称 -->
<short-name>xdl</short-name>
<!-- 用于匹配的uri -->
<uri>http://www.dsb.com/heiheihei</uri>
<!-- 向标签库中 添加一个标签 -->
<tag>
<!-- 对于标签的解释, 在使用时会提示给用户 -->
<description>这个标签用于向网页中随机生成一条名言</description>
<!-- 标签名称 -->
<name>heihei</name>
<!-- 标签的Java类的全名 -->
<tag-class>cn.xdl.tag.MyTag1</tag-class>
<!-- 标签是否允许包含内容
可以设置的值 : JSP , 表示允许存在JSP代码
可以设置的值: empty , 表示为空
-->
<body-content>empty</body-content>
</tag>
<!-- 向标签库中 添加一个标签 -->
<tag>
<!-- 对于标签的解释, 在使用时会提示给用户 -->
<description>这个标签用于向网页中加入分页标签</description>
<!-- 标签名称 -->
<name>page</name>
<!-- 标签的Java类的全名 -->
<tag-class>cn.xdl.tag.MyTag2</tag-class>
<!-- 标签是否允许包含内容
可以设置的值 : JSP , 表示允许存在JSP代码
可以设置的值: empty , 表示为空
-->
<body-content>empty</body-content>
<!-- 描述属性 -->
<attribute>
<!-- 属性的描述 -->
<description>这个属性用于设置总数量</description>
<!-- 属性名 , 与类中的名称必须一致 -->
<name>length</name>
<!-- 属性是否必须传递 -->
<required>true</required>
<!-- 是否支持el表达式传递 -->
<rtexprvalue>true</rtexprvalue>
<!-- 属性的类型 -->
<type>int</type>
</attribute>
<!-- 描述属性 -->
<attribute>
<!-- 属性的描述 -->
<description>这个属性用于一页展示的数量</description>
<!-- 属性名 , 与类中的名称必须一致 -->
<name>pageSize</name>
<!-- 属性是否必须传递 -->
<required>true</required>
<!-- 是否支持el表达式传递 -->
<rtexprvalue>true</rtexprvalue>
<!-- 属性的类型 -->
<type>int</type>
</attribute>
</tag>
</taglib>
Fileter & Listener
web三大组件
Servlet是web的三大组件之一 .
1. Servlet
2. Fileter 过滤器
3. Listener 监听器
Filter 过滤器 *
过滤的是请求 .
面向切面编程思想 .
使用步骤:
1. 编写一个类 , 实现Filter接口
2. 通过web.xml 或 注解的方式, 配置过滤器的过滤地址.
doFilter中的请求与响应对象 为什么不是http的 ? 了解
过滤器在早期设计时, 不只是针对HTTP请求. 针对所有协议的请求都可以进行过滤.
因为我们现在都是使用HTTP协议, 所以很感觉怪异
想要操作Http相关的请求对象 与 响应对象 怎么办?
只需要将请求对象强制转换为 HttpServletRequest 将响应对象 强制转换为 HttpServletResponse 即可
案例
Java代码
public class UserFilter implements Filter {
/**
* 当过滤器即将销毁时
*/
@Override
public void destroy() {
// TODO Auto-generated method stub
}
/**
* 当匹配的请求出现时, 执行. 且执行在 servlet/jsp/html等等所有的服务器资源之前.
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
//1. 先得到session
HttpSession session = req.getSession();
//2. 判断session中是否存在帐号
if(session.getAttribute("username")!=null) {
//3. 存在则放行
fc.doFilter(request, response);
}else {
//4. 不存在则拦截 , 并响应给用户拦截的原因
res.sendRedirect("userError.jsp");
}
}
/**
* 当过滤器初始化时
*/
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
WEB.XML代码
<filter>
<filter-name>uf</filter-name>
<filter-class>cn.xdl.demo1.UserFilter</filter-class>
<filter-mapping> <filter-name>uf</filter-name> <url-pattern>/home.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>uf</filter-name> <url-pattern>/userCenter.jsp</url-pattern> </filter-mapping>
过滤器链
当多个过滤器的 过滤地址重复时, 就形成了过滤器链, 用户的一次请求, 需要经过多次过滤放行.
过滤器链的执行顺序是:
web.xml中配置的顺序:
按照xml中配置的先后顺序 来执行的 , web.xml中配置代码靠前的 ,优先执行.
注解配置的顺序:
按照类名的自然排序 , 排序执行;
注意: 注解配置的过滤器, 一定执行在web.xml过滤器之后
例如:
类名Filter1 执行在类名Filter2之前.
类名Aaa 执行在 类名 Aab之前
Listener 熟悉
事件驱动, 监听的是tomcat产生的事件:
两类事件:
1. 生命周期相关事件
2. 域对象中数据的变化事件
ServletContextListener
用于监听ServletContext的创建 和 销毁
因为ServletContext的创建, 就表示项目的启动. ServletContext的销毁 就表示项目的关闭.
所以此监听器 ,是用于监听项目的启动 和 关闭的 .
我们常在项目启动时, 进行资源初始化的操作 . 准备一些后续项目中会用到的资源.
在项目关闭时, 进行资源的释放操作 . 解除资源的占用句柄 .
案例:
@WebListener
public class Listener1 implements ServletContextListener {
/**
* 用于监听应用关闭
*/
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
}
/**
* 用于监听应用启动
*/
public void contextInitialized(ServletContextEvent arg0) {
// TODO Auto-generated method stub
}
}
ServletContextAttributeListener
@WebListener
public class ServletContextAttributeListener1 implements ServletContextAttributeListener {
/**
*/
public void attributeAdded(ServletContextAttributeEvent e) {
//从事件对象中, 得到增加的数据 (键和值)
String key = e.getName();
Object value = e.getValue();
System.out.println("添加的数据key:"+key+" , 数据值:"+value);
}
/**
* 当删除数据时
*/
public void attributeRemoved(ServletContextAttributeEvent e) {
//从事件对象中, 得到删除的数据 (键和值)
String key = e.getName();
Object value = e.getValue();
System.out.println("删除的数据key:"+key+" , 数据值:"+value);
}
/**
* 当数据被替换时
*/
public void attributeReplaced(ServletContextAttributeEvent e) {
//从事件对象中, 得到被替换的旧值
String key = e.getName();
Object oldValue = e.getValue();
//从事件对象中, 可以得到ServletContex对象
Object newValue = e.getServletContext().getAttribute(key);
System.out.println("替换的数据key:"+key+" , 旧的值:"+oldValue+" , 新的值:"+newValue);
}
}
HttpSessionListener
用于监听session的创建与销毁 .
因为我们服务器中每一个session 表示一次用户的会话.
一个客户端就会创建一个单独的session. 可以将session的数量, 理解为在线用户的数量.
所以我们经常在session创建与销毁的监听中, 计算当前在线的人数.
案例:
@WebListener
public class HttpSessionListener1 implements HttpSessionListener {
/**
* session的创建
*/
public void sessionCreated(HttpSessionEvent e) {
CountUtil.add();
}
/**
* session的销毁
*/
public void sessionDestroyed(HttpSessionEvent e) {
CountUtil.remove();
}
}
public class CountUtil {
private static int count = 0;
private static ArrayList<Integer> countNum = new ArrayList<Integer>();
private static Random r = new Random();
public static void add() {
//5-10的随机数字
int num = r.nextInt(25)+25;
//计数
count+=num;
//将本次计数的值 存储到集合中
countNum.add(num);
}
public static void remove() {
//获取并删除最后 一次 计数的 数量值
int num = countNum.remove(countNum.size()-1);
count-=num;
}
public static int get() {
return count;
}
}
HttpSessionAttributeListener
用于监听session中的数据的增加, 删除 ,修改
/**
*监听用户会话状态的变化
*/
@WebListener
public class HttpSessionAttributeListener1 implements HttpSessionAttributeListener {
/**
* 当session中数据增加
*/
public void attributeAdded(HttpSessionBindingEvent e) {
String key = e.getName();
Object value = e.getValue();
System.out.println("session中增加了数据: key="+key+" , value="+value);
}
/**
* 当session中数据删除
*/
public void attributeRemoved(HttpSessionBindingEvent e) {
String key = e.getName();
Object value = e.getValue();
System.out.println("session中删除了数据: key="+key+" , value="+value);
}
/**
* 当session中数据被替换
*/
public void attributeReplaced(HttpSessionBindingEvent e) {
String key = e.getName();
Object oldValue = e.getValue();
Object newValue = e.getSession().getAttribute(key);
System.out.println("session中替换了数据: key="+key+" , 旧的值="+oldValue+", 新的值="+newValue);
}
}