监听器
-
功能
- Servlet中的监听器功能是负责监听WEB的各种操作,当相关的事件触发之后将产生事件,并对此事件进行处理,在WEB中可以对application、session、request三种操作进行监听。 分类
- 分别对application,session,request事件的监听 实现
- 实现监听功能,主要是由一个普通类,实现监听器的接口,在web.xml中配置监听器或者添加注解
以下是主要使用的六个监听器接口
接口 | 作用 |
---|---|
ServletRequestListener | 监听request的生命周期 |
ServletRequestAttributeListener | 监听request域里面attribute参数的添加、删除或是修改 |
HttpSessionListener | 监听session的 生命周期(创建以及销毁) |
HttpSessionAttributeListener | session域中的Attribute参数的添加、删除或是修改 |
ServletContextListener | 监听application生命周期 |
ServletContextAttributeListener | 全局域application域里面attribute参数的添加修改或是删除 |
监听器监听在线人数
-
思路
- 首先,需要利用session的有关参数的监听器。在登录时使用session域存储登录的用户信息user。当监听到参数的改变之后,在监听器里面实现记录当前在线人数操作。其次,记录的人数需存储于application域里面。使用application的生命周期监听器ServletContextListener来存储在Attribute中。 代码
- CountOnLineListener.java文件
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import priv.zs.upload.beans.User;
/**
* Application Lifecycle Listener implementation class CountOnLine
*
*/
@WebListener
public class CountOnLine implements ServletContextListener, HttpSessionAttributeListener {
private ServletContext app ;
/**
* Default constructor.
*/
public CountOnLine() {
}
/**
* @see ServletContextListener#contextDestroyed(ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent sce) {
}
/**
* @see HttpSessionAttributeListener#attributeAdded(HttpSessionBindingEvent)
*/
//session参数添加 在线人数
public void attributeAdded(HttpSessionBindingEvent se) {
// System.out.println("session添加一个参数");
// System.out.println("监听到的添加属性名:" + se.getName());
//设置参数,若是添加的参数名不是"user",证明不是用户添加,而是session域中存储量其他的参数
if(se.getName().equals("user")) {
/*
* 添加注解消除掉警告 此警告是由于app.getAttribute()方法所获得的的 参数为Object类型,
* 若不是User对象则会出现异常,无法将参数值转换为List<User>类型
*/
@SuppressWarnings("unchecked")
List<User> users = (List<User>) app.getAttribute("users");
User user = (User) se.getValue();
users.add(user);
int count = (int) app.getAttribute("count");
count++;
app.setAttribute("count", count);
/*
* count为基本数据类型,若是不存储进参数里面,则会无法修改在线人数
* 而List<User>为引用数据类型,不需要存储回去亦可改变参数
*/
}
// System.out.println("count数目:" + count);
}
/**
* @see HttpSessionAttributeListener#attributeRemoved(HttpSessionBindingEvent)
*/
//session参数减少 离线人数
public void attributeRemoved(HttpSessionBindingEvent se) {
// System.out.println("有人注销.....");
@SuppressWarnings("unchecked")
List<User> users = (List<User>) app.getAttribute("users");
User user = (User) se.getValue();
users.remove(user);
int count = (int) app.getAttribute("count");
count--;
app.setAttribute("count", count);
}
/**
* @see HttpSessionAttributeListener#attributeReplaced(HttpSessionBindingEvent)
*/
public void attributeReplaced(HttpSessionBindingEvent se) {
}
/**
* @see ServletContextListener#contextInitialized(ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent sce) {
//初始化应用全局对象app
app = sce.getServletContext();
/**
* 一开始启动服务器时,无人登录,所以在线人数初始化为0
* 新建一个键值对count -- value 存储在线人数
* 新建集合存储用户名
*/
//设置在线人数参数
app.setAttribute("count", 0);
//设置在线用户集合
app.setAttribute("users", new ArrayList<User>());
}
-
示例servlet
- 跳转onLine.jsp的ServletdoGet方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext app = this.getServletContext();
int count = (int) app.getAttribute("count");
@SuppressWarnings("unchecked")
List<User> users = (List<User>) app.getAttribute("users");
request.setAttribute("count", count);
request.setAttribute("users", users);
request.getRequestDispatcher("onLine.jsp").forward(request, response);
}
-
示例jsp
- onLine.jsp
<body>
在线人数:${requestScope.count}<br /><br />
在线用户信息:
<c:forEach var="user" items="${requestScope.users}">
<p>用户名:${user.account}</p>
</c:forEach>
</body>
文件上传
在文件上传时,前端 jsp 文件使用form表单实现,提交方法设置为post,因为get方法会将参数显示在URL地址栏,大小为4-6KB,多了则会导致数据丢失。
文件的大小显然不可能固定在4-6KB以下,因此使用post方式提交则可以尽可能保证数据不会丢失
且,此方法使用的是servlet3.0的方法,需要使用注解@MultipartConfig,否则将会报异常
单文件上传
单文件上传,在servlet里面使用request,getPart(String name);方法,name为<input />标签的name值。
代码以上传图片为例子
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
//获取图片(单张图片)
Part part = request.getPart("headImg");
//获取文件提交的路径
String filePath = part.getSubmittedFileName();
// System.out.println(filePath);
//获取文件后缀名
String suffix = filePath.substring(filePath.lastIndexOf("."));
// System.out.println("后缀名:" + suffix);
//生成文件名
//new Date().getTime()得到自1970年以来至今的毫秒数,一般不会重复,可借此生成唯一文件名
String fileName = new Date().getTime() + suffix;
//生成文件保存路径(文件对应的upload目录下)
String path = request.getServletContext().getRealPath("/upload"); //"E:\\" + fileName;
// System.out.println("upload路径:" + path);
//写入文件
//设置文件路径
String savePath = path + "\\" + fileName;
// System.out.println("文件保存路径:" + savePath);
//用write(String str)方法写入文件,str为文件的存储地址
part.write(savePath);
//将地址存储进session域
session.setAttribute("headImg", fileName);
// request.getRequestDispatcher("show.jsp").forward(request, response);
response.sendRedirect("show.jsp");
}
多文件上传
多文件上传与单文件有一定的相似之处,唯一不同只是获取文件方法不同。多文件上传使用request.getParts();
单文件上传getPart(String name),需要有参数,多文件则不需要。
同样,以上传图片为例
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=UTF-8");
//获取文件
List<Part> parts = (List<Part>) request.getParts();
//定义一个集合来存储文件保存的路径
List<String> photos = new ArrayList<String>();
HttpSession session = request.getSession();
for(Part part:parts) {
try {
//添加睡眠时间,避免getTime()方法获取时间,以5ms隔开相同的情况
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取文件上传路径
String filePath = part.getSubmittedFileName();
// System.out.println(filePath);
//获取文件后缀名
String suffix = filePath.substring(filePath.lastIndexOf("."));
// System.out.println("后缀名:" + suffix);
//生成文件名
String fileName = new Date().getTime() + suffix;
//生成文件保存路径(文件对应的upload目录下)
String path = request.getServletContext().getRealPath("/upload"); //"E:\\" + fileName;
// System.out.println("upload路径:" + path);
//写入文件
String savePath = path + "\\" + fileName;
// System.out.println("文件保存路径:" + savePath);
part.write(savePath);
//将地址存储进session域
photos.add(fileName);
session.setAttribute("photos", photos);
}
// request.getRequestDispatcher("show.jsp").forward(request, response);
response.sendRedirect("show.jsp");
}
优化代码
代码可以改进。实质上,多文件上传于单文件上传几乎一样,只不过是多文件爱你上传获取到的是一个集合,而单文件却是一个对象。并且其中的方法直接复制粘贴套过来嵌套循环使用,实现多文件的上传,最多是在多文件里面添加了一个线程睡眠的方法。因此,写一个工具类,使用静态方法由类直接调用,传入参数HttpServletRequest接口与Part对象即可。
UploadUtil.java工具类
import java.io.IOException;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
public class UploadUtil {
public static void upload(HttpServletRequest request, Part part, List<String> address) throws IOException, InterruptedException {
//添加睡眠时间,避免getTime相同的情况
Thread.sleep(5);
String filePath = part.getSubmittedFileName();
// System.out.println(filePath);
//获取文件后缀名
String suffix = filePath.substring(filePath.lastIndexOf("."));
// System.out.println("后缀名:" + suffix);
//生成文件名
String fileName = new Date().getTime() + suffix;
//生成文件保存路径(文件对应的upload目录下)
String path = request.getServletContext().getRealPath("/upload"); //"E:\\" + fileName;
// System.out.println("upload路径:" + path);
//写入文件
String savePath = path + "\\" + fileName;
// System.out.println("文件保存路径:" + savePath);
part.write(savePath);
//将地址存储进session域
address.add(fileName);
}
}
多文件上传servlet类UploadMore.java中的doPost()方法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=UTF-8");
//获取文件
List<Part> parts = (List<Part>) request.getParts();
List<String> photos = new ArrayList<String>();
HttpSession session = request.getSession();
try {
for(Part part:parts) {
//添加睡眠时间,避免getTime相同的情况
UploadUtil.upload(request, part, photos);
session.setAttribute("photos", photos);
}
response.sendRedirect("show.jsp");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
单文件UploadSingle.java的doPost()方法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
List<String> address = new ArrayList<String>();
//获取图片(单张图片)
Part part = request.getPart("headImg");
try {
UploadUtil.upload(request, part, address);
session.setAttribute("headImg", address.get(0));
response.sendRedirect("show.jsp");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
页面布局
前端jsp页面
主页show.jsp登陆之后的
<a href="count.do">查看在线人数</a>
<h1>用户信息如下:</h1>
<p>账号:${sessionScope.user.account}</p>
<p>密码:${sessionScope.user.password}</p>
<p>
头像:
<c:choose>
<c:when test="${empty sessionScope.headImg}">
<a href="single.jsp">上传头像</a>
</c:when>
<c:otherwise>
<img src="${pageContext.request.contextPath}/upload/${sessionScope.headImg}" width="150"/><a href="single.jsp">上传头像</a>
</c:otherwise>
</c:choose>
</p>
<p>
相册:
<c:forEach var="photo" items="${sessionScope.photos }">
<img src="${pageContext.request.contextPath}/upload/${photo}" width="150" />
</c:forEach>
<a href="more.jsp">上传相册</a>
</p>
<a href="logout.do">注销登录</a>
项目示例:
https://download.csdn.net/download/zb1812606603/12490798