学习笔记:javaEE基础4 监听器过滤器

来源于从网页搭建入门Java Web

这里是过滤器(filter)和监听器(listener)专场



过滤器(filter)

作用:内容过滤处理,编码处理,权限处理

两种配置方式:web.xml @WebFilter

第一种

1
2
3
4
5
6
7
8
9
10
11
12
13
<filter>
<filter-name>AFiler</filter-name>
<filter-class>pers.schwarzeni.learning.filter.AChainFilter</filter-class>
<init-param>
<param-name>version</param-name>
<param-value>v1.2.3</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>AFiler</filter-name>
<url-pattern>/filter/target</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

注意两点:过滤器串的顺序和配置文件中<filter-mapping>声明的顺序相同,其次,设置的<dispatcher>告诉该过滤器作用于那种类型的url访问,有以下几种,基础好的人应该都不陌生

  • FORWARD
  • ERROR
  • REQUEST
  • INCLUDE

@WebFilter 方式声明过滤器无法控制顺序,它还有一些参数,这里就不再列举

1
2
3
4
5
6
@WebFilter(filterName = "CChainFilter",  urlPatterns = "/filter/target")
public class CChainFilter implements Filter {
@Override
public void destroy() {
}
......

使用过滤器需要实现 javax.servlet.Filter 接口,重写以下方法,下面是修改文件编码的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WebFilter(filterName = "ContentEncodingFilter", urlPatterns = "/*")
public class ContentEncodingFilter implements Filter {

private String encoding;

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
req.setCharacterEncoding(this.encoding);
chain.doFilter(req, resp);
resp.
}

@Override
public void init(FilterConfig config) throws ServletException {
this.encoding = config.getInitParameter("encoding");
}
}

如果把代码写在 chain.doFilter(req, resp); 下面就是对servlet已经处理好并准备返回的数据进行处理,其实和nodejs的express插件原理差不多


监听器(listener)

作用:统计在线人数,页面访问统计,应用启动时完成初始化工作,与Spring结合

分类

  • request类
    • ServletRequestListener(创建销毁)
    • ServletRequestAttributeListener (添加替换删除)
  • session类
    • HttpSessionListener(创建销毁)
    • HttpSessionAttributeListener(添加替换删除)
    • HttpSessionBindingListener(绑定解绑)
  • servlet类
    • ServletContextListener(创建销毁)
    • ServletContextAttributeListener(添加替换删除)

代表不同的作用域,分别要实现一些方法,这里就不再列举,可以通过两种方式声明,和过滤器类似,但是,HttpSessionBindingListener不用声明,使用方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MySerlvetSessionBindingListener implements HttpSessionBindingListener{

@Override
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.print("绑定:");
System.out.println(httpSessionBindingEvent.getSession().getId() + " " + httpSessionBindingEvent.getName());
}

@Override
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("解绑: " +httpSessionBindingEvent.getSession() + " " + httpSessionBindingEvent.getName());
}
}

只有在将其放入session移除session时才会触发这两个函数

1
2
<%session.setAttribute("user", new MySerlvetSessionBindingListener());%>
<% session.removeAttribute("user"); %>

综合案例:登录控制

规则:同一个用户名同一时间只能有一个保持登录状态,新的登陆者会将就的登陆者挤下去

使用一个map存储 用户名 和 sessionId 之间的映射,一个map存储 sessionId 和 session 之间的映射

sessionId作为对用户的唯一标示,可以判断是否是同一个用户,避免误踢

在登录的时候提交表单名为username的用户名,将用户名存入session中,session.setAttribute("user", request.getParameter("username")) 使用监听器监听该事件,并判断是否重复登录

在用户要访问主页面的时候使用过滤器判断session中是否有用户的登录信息,没有的话使其返回登录页


以下为缓存两个map的类,使用单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class UserCache {
private static UserCache userCache = new UserCache();

// 用户与sessionId的map
private Map<String, String>userSessionIdMap = new HashMap<>();

//sessionId和session的map
private Map<String, HttpSession> sessionIdSessionMap = new HashMap<>();

private UserCache() {
}

public static UserCache getInstance() {
return UserCache.userCache;
}

/**
* 根据用户名获取其sessionId
*/
public String getSessionIdByUser(String user) {
return this.userSessionIdMap.get(user);
}

/**
* 设置用户和sessionId
*/
public void setSessionIdByUser(String user, String sessionId) {
this.userSessionIdMap.put(user, sessionId);
}

/**
* 根据sessionId获取session
*/
public HttpSession getSessionBySessionId(String sessionId) {
return this.sessionIdSessionMap.get(sessionId);
}

/**
* 设置sessionId和session
*/
public void setSessionBySessionId(String sessionId, HttpSession session) {
this.sessionIdSessionMap.put(sessionId, session);
}
}

以下为监听器,监听对session的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@WebListener()
public class LoginSessionAttributeListener implements HttpSessionAttributeListener {

public LoginSessionAttributeListener() {
}

@Override
public void attributeAdded(HttpSessionBindingEvent sbe) {
String attrName = sbe.getName();
// 由于其监听session一切属性的绑定,所以这里需要做一个判断
if ("user".equals(attrName)) {
String user = (String)sbe.getValue();
HttpSession session = sbe.getSession();
String sessionId = session.getId();

// 尝试获取缓存中原有的sessionId
String sessionId2 = UserCache.getInstance().getSessionIdByUser(user);
if (sessionId2 == null) {

} else {
// 如果已有登录信息,则使旧的失效
HttpSession oldSession = UserCache.getInstance().getSessionBySessionId(sessionId2);
oldSession.invalidate();
}

// 对缓存进行重新设置
UserCache.getInstance().setSessionBySessionId(sessionId, session);
UserCache.getInstance().setSessionIdByUser(user, sessionId);

}
}

@Override
public void attributeRemoved(HttpSessionBindingEvent sbe) {
}

@Override
public void attributeReplaced(HttpSessionBindingEvent sbe) {
}
}

过滤器判断session中是否用用户的登录信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@WebFilter(filterName = "UserFilter", urlPatterns = "/opl/app/*")
public class UserFilter implements Filter {
@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)req;
String user = (String)request.getSession().getAttribute("user");

// 判断session中是否有用户的登录信息
if (user == null) {
request.setAttribute("msg", "请先登录");
request.getRequestDispatcher("/opl/login.jsp").forward(request, resp);
return;
}
chain.doFilter(req, resp);
}

@Override
public void init(FilterConfig config) throws ServletException {

}

}

最后是登录的jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
if ("POST".equalsIgnoreCase(request.getMethod())) {
String username = request.getParameter("username");
// 将用户的登录信息添加到session中,同时触发session监听器
request.getSession().setAttribute("user", username);
response.sendRedirect("/opl/app/index.jsp");
}
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<p style="text-align: center;color: #ff0000;">${msg}</p>
<form action="/opl/login.jsp" method="post">
<input type="text" name="username"/>
<button type="submit">提交</button>
</form>
</body>
</html>