滤器就是一个用于在请求之前处理资源的组件
生命周期
随着服务器启动而初始化
随着请求的发出而过滤
随着服务器关闭而销毁
执行流程
浏览器发起请求
服务器会根据这个请求,创建request对象及response对象
过滤器会持有request对象及response对象
只有当过滤器放行之后,request对象及response对象才会传给Serlvet
过滤器链
根据配置顺序,遵从"先过滤,后放行"的原则
------------------------------
开发步骤
自定义类实现Filter接口
重写init、doFilter、destroy方法
在web.xml中配置过滤器
声明过滤器
过滤器配置过滤路径
过滤器
public class Demo01Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//Demo01Filter过滤器的初始化
System.out.println("Demo01Filter初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//Demo01Filter过滤器处理请求
System.out.println("Demo01Filter放行之前");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Demo01Filter放行之后");
}
@Override
public void destroy() {
//Demo01Filter过滤器的销毁
System.out.println("Demo01Filter销毁");
}
}
- web.xml
<!--声明Demo01Filter过滤器-->
<filter>
<filter-name>Demo01Filter</filter-name>
<filter-class>com.qfedu.filter.Demo01Filter</filter-class>
</filter>
<!--配置Demo01Filter的过滤路径-->
<filter-mapping>
<filter-name>Demo01Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
------------------------------
10.3 过滤器的相关配置
- 初始化参数
- Filter配置初始化参数
<filter>
<filter-name>Demo03Filter</filter-name>
<filter-class>com.qfedu.filter.Demo03Filter</filter-class>
<!--初始化参数-->
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root123</param-value>
</init-param>
</filter>
- 初始化参数
- Filter获取初始化参数
public class Demo03Filter implements Filter {
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
//获取初始化参数名称
String parameterName = parameterNames.nextElement();
//获取初始化参数值
String parameterValue = config.getInitParameter(parameterName);
System.out.println("name : " + parameterName + " , value : " + parameterValue);
}
}
......
}
- Filter的过滤路径
- 针对< servlet-name > ,以为Filter仅针对Demo01Servlet进行过滤
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<servlet-name>Demo01Servlet</servlet-name>
</filter-mapping>
---------------
- Filter的过滤路径
- 针对< url-pattern >
完全匹配:必须以"/"开头
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<url-pattern>/aa</url-pattern>
</filter-mapping>
过滤器只过滤访问路径完全匹配"/aa"的资源
目录匹配:必须以"/"开头,以"*"结尾
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<url-pattern>/aa/bb/*</url-pattern>
</filter-mapping>
过滤器只过滤访问路径目录匹配到“/aa/bb“的资源
后缀名匹配:必须以"*"开头,以后缀名结尾
<filter-mapping>
<filter-name>Demo03Filter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
过滤器只过滤后缀名为jsp的资源
------------------------------
10.4 过滤器的注解开发
@WebFilter注解
WebInitParam[] initParams() default {}; 配置初始化参数
String filterName() default ""; 配置过滤器名称
String[] servletNames() default {}; 配置过滤的Servlet
String[] urlPatterns() default {}; 配置过滤路径
- 基本使用
@WebFilter(filterName = "Demo04Filter" ,
urlPatterns = "/demo01",
servletNames = "Demo01Servlet" ,
initParams = {
@WebInitParam(name = "username",value = "root"),
@WebInitParam(name = "password",value = "root123")
})
public class Demo04Filter implements Filter {
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String parameterValue = config.getInitParameter(parameterName);
System.out.println(parameterName + " , " + parameterValue);
}
}
......
}
执行顺序
按照过滤器的类名的字典顺序决定谁先过滤,谁先放行
比如AFilter、BFilter,那么AFilter就会先过滤,BFilter会先放行
------------------------------
10.5 过滤器案例
10.5.1 中文乱码
public class EncodingFilter implements Filter {
private String encoding = null;
public void init(FilterConfig config) throws ServletException {
encoding = config.getInitParameter("encoding");
}
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//处理响应中文乱码
resp.setContentType("text/html;charset="+encoding);
//处理请求中文乱码
req.setCharacterEncoding(encoding);
chain.doFilter(req, resp);//放行
}
}
- web.xml
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.qfedu.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
10.5.2 自动登录
实现步骤
登录账户后,根据是否勾选了自动登录选项框,
判断是否访问和登录相关资源
如果是,直接放行
如果不是,判断是否已经在登录状态
如果是,直接放行
如果不是,需要从cookie中取出存储的用户信息,进行登录操作
如果登录成功,直接放行
如果登录失败,就跳转到登录页面
- 登录功能
@WebServlet(name = "LoginServlet" ,urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "root".equals(password)) {
String autoLogin = request.getParameter("autoLogin");
System.out.println(autoLogin);
if ("autoLogin".equals(autoLogin)) {
//进行自动登录,无非就是将用户信息(账户和密码(加密))保存起来!!!
//request、servletContext、cookie、session
Cookie cookie = new Cookie("autoLogin",username+"-"+password);
cookie.setMaxAge(7 * 24 * 60 * 60);
response.addCookie(cookie);
}
//登录成功,转发到一个页面,用来显示用户信息
User existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser",existUser);
request.getRequestDispatcher("/showIndex").forward(request,response);
} else {
//登录失败,转到登录页面
request.getRequestDispatcher("/index.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- LoginFilter自动登录
public class AutoLoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
//获取请求路径
String requestURI = request.getRequestURI();
//1,判断访问的资源是否和登录相关
if (requestURI.contains("login")) {
//和登录相关的资源,直接放行
chain.doFilter(request, resp);
} else {
//不是登录相关的资源
//2,判断是否在登录状态
User existUser = (User) request.getSession().getAttribute("existUser");
if (null == existUser) {
//不在登录状态 , 进行自动登录
//获取Cookie
Cookie cookie = CookieUtils.getCookie(request.getCookies(), "autoLogin");
//判断cookie是否为空 , 存在浏览器清理缓存
if (null == cookie) {
//浏览器清理缓存 , 相当于自动登录失败!! 跳转到登录页面,进行手动登录
request.getRequestDispatcher("/login.jsp").forward(request,resp);
} else {
//还有缓存,进行自动登录
//获取用户信息 root-root
String infoStr = cookie.getValue();
String[] infos = infoStr.split("-");
String username = infos[0];
String password = infos[1];
if ("root".equals(username) && "root".equals(password)) {
//自动登录成功 ,修改登录状态, 直接放行 ,意味着,还不在登录状态!!!
existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser",existUser);
chain.doFilter(req,resp);
} else {
//自动登录失败 (修改了密码) ,跳转到登录页面,进行手动登录
request.getRequestDispatcher("/login.jsp").forward(request,resp);
}
}
} else {
//在登录状态 , 直接放行
chain.doFilter(request,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
10.5.3 敏感词屏蔽
对request对象进行增强。增强获取参数相关方法
放行。传递增强的请求方法
@WebFilter(
filterName = "SensitiveWordsFilter" ,
urlPatterns = "/*",
initParams = {
@WebInitParam(name = "word1",value = "笨蛋"),
@WebInitParam(name = "word2" ,value = "傻瓜"),
@WebInitParam(name = "word3" ,value = "尼玛"),
@WebInitParam(name = "word4",value = "靠")
})
public class SensitiveWordsFilter implements Filter {
//敏感词
List<String> sensitiveWords = new ArrayList<>();
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String sensitiveWord = config.getInitParameter(parameterNames.nextElement());
sensitiveWords.add(sensitiveWord);
}
}
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("SensitiveWordsFilter doFilter");
HttpServletRequest request = (HttpServletRequest) req;
//增强request下的getParameter方法
HttpServletRequest requestPrxoy = (HttpServletRequest) Proxy.newProxyInstance(
request.getClass().getClassLoader(),
request.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强getParameter方法
Object returnValue = null;
String methodName = method.getName();
if ("getParameter".equals(methodName)) {
//returnValue就是getParameter方法的返回值,可能会存在敏感词
String returnValue1 = (String)method.invoke(request, args);
//开始处理敏感词
for (String sensitiveWord : sensitiveWords) {
if (returnValue1.contains(sensitiveWord)) {
//getParameter方法的返回值包含敏感词
returnValue1 = returnValue1.replace(sensitiveWord,"***");
}
}
return returnValue1;
} else {
returnValue = method.invoke(request, args);
}
return returnValue;
}
});
chain.doFilter(requestPrxoy, resp);
}
}
十一 监听器
11.1 监听器的介绍
监听器概念
事件源:事件发生的源头
监听器:监听事件发生
绑定:将监听器绑定到事件源
事件:能够触发监听器的事
Servlet监听器
事件源:request域对象、session域对象、ServletContext域对象
监听器:Servlet三种监听器
绑定:配置web.xml
事件:域对象发生改变
------------------------------
11.2 监听器的分类
一类监听器 监听域对象的创建、销毁
二类监听器 监听域对象中的属性变更(属性设置、属性替换、属性移除)
三类监听器 监听域对象的java对象的绑定
11.2.1 一类监听器
ServletRequestListener : 监听ServletRequest域对象的创建、销毁
HttpSessionListener :监听HttpSession域对象的创建、销毁
ServletContextListener : 监听ServletContext域对象的创建、销毁
开发步骤
自定义类实现一类监听器
重写监听器中的方法
配置web.xml
- 监听器
public class MyListener01 implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//监听ServletContext域的初始化,随着服务器的启动
System.out.println("ServletContext初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//监听ServletContext域的销毁,随着服务器的关闭
System.out.println("ServletContext销毁");
}
}
- web.xml(绑定)
<listener>
<listener-class>com.qfedu.listener.MyListener01</listener-class>
</listener>
事件源: ServletContext域对象
监听器:ServletContextListener
绑定: web.xml配置
事件 : ServletContext域对象发生了创建、发生了销毁
11.2.2 二类监听器
ServletRequestAttributeListener :监听ServletRequest域对象中属性变更
HttpSessionAttributeListener : 监听HttpSession域对象中属性变更
ServletContextAttributeListener : 监听ServletContext域对象中属性变更
- 监听器
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性添加
System.out.println("ServletContext added");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性值被替换
System.out.println("ServletContext replaced");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性值移除
System.out.println("ServletContext removed");
}
}
- web.xml(绑定)
<listener>
<listener-class>com.qfedu.listener.MyServletContextAttributeListener</listener-class>
</listener>
11.2.3 三类监听器
HttpSessionBindingListener
监听session域中的java对象的状态(绑定和解绑)
绑定:将java对象存储到session域对象
解绑:将java对象从session域对象移除
监听器组成
事件源:java对象
监听器:HttpSessionBindingListener
绑定:java对象实现HttpSessionBindingListener接口
事件:java对象在session中状态发生改变
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("User绑定");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("User解绑");
}
......
}
HttpSessionBindingListener监听不需要在web.xml配置
------------------------------
11.3 监听器注解开发
@WebListener 相当于在web.xml绑定了监听器
@WebListener
public class MyServletContextLIstener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext创建");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext销毁");
}
}
------------------------------
11.4 监听器综合案例
11.4.1 记录登录人数
- 开发步骤
登录功能
登录失败,转发到登录页面
登录成功,记录登录状态,重定向到首页(显示用户名、注销登录、在线人数)
注销登录
注销登录成功;正常来说,应该转发到登录页面;转发到首页
在线人数
使用HttpSessionBindingListener监听器
使用ServletContext域对象,存储在线人数count
- login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/day59/login" method="post">
账户:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
<button type="submit">登录</button>
</form>
</body>
</html>
- bean.User
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
//有人登录成功 , 在线人数(count)加1
//判断是否是第一个登录成功的人
//获取ServletContext
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
if (null == count) {
//就是第一个登录成功的人
count = 1;
} else {
//不是第一个登录成功的人
count++;
}
servletContext.setAttribute("count", count);
}
//在同一个浏览器,意味着是同一个session
//第一次登录 ,session.setAttribute("existUser","root") , valueBound +1
//第二次登录 ,session.setAttribute("existUser","root1") , valueBound +1 -> valueUnbound -1
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
//有人注销登录 ,在线人数(count)减1
System.out.println("User解绑");
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
count--;
servletContext.setAttribute("count", count);
}
private Integer id;
private String username;
private String password;
public User() {
}
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
在线人数:在同一个浏览器,操作多次登录,意味着是同一个session
- LoginServlet
@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "root".equals(password)) {
//登录成功 , 修改登录状态 ,跳转到ShowIndexServlet
User existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser", existUser);
// request.getRequestDispatcher("/showIndex").forward(request,response);
response.sendRedirect("/day59/showIndex");
} else {
//登录失败,转发到登录页面,重新登录
request.getRequestDispatcher("/login.html").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- LogoutServlet
@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
public class LogoutServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//注销登录 , 将existUser变量从session域中移除!
// request.getSession().removeAttribute("existUser");
//注销登录,将session销毁 -> 将existUser变量从session域中移除!
request.getSession().invalidate();
//注销成功
request.getRequestDispatcher("/showIndex").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
- ShowIndexServlet
@WebServlet(name = "ShowIndexServlet", urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
User existUser = (User) request.getSession().getAttribute("existUser");
StringBuffer responseBody = new StringBuffer();
if (null == existUser) {
//不在登录状态,提示
responseBody.append("您还没有登录;<a href='/day59/login.html'>请登录</a><br>");
} else {
//在登录状态,显示信息
responseBody.append("欢迎回来," + existUser.getUsername() + " <a href='/day59/logout'>注销</a><br>");
}
ServletContext servletContext = getServletContext();
//获取在线人数count
Integer count = (Integer) servletContext.getAttribute("count");
System.out.println("在线人数 " + count);
//count变量为null,在没有任何人登录状态下,访问了ShowIndexServlet
if (null == count) {
//没有任何人在登录状态 ,在线人数处理成0人
count = 0;
} else {
//有人在登录状态 ,直接输出在线人数count人
}
responseBody.append("在线人数 : " + count);
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseBody.toString());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}