Filter ,Listener和BaseServlet
1. Filter过滤器
1.1 Filter过滤器的配置方式
1.1.1 注解方式配置
关注
String[] value() default {};
String[] urlPatterns() default {};
设置当前过滤器Filter限制过滤的条件路径
@WebFilter(urlPatterns = {"/Day45/user", "/Day45/admin"})
@WebFilter("/Day45/user") ==> 注解中的value属性
1.1.2 web.xml方式配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<!-- 当前过滤器名字 -->
<filter-name>BFilter</filter-name>
<!-- 对应当前过滤器的.class文件 -->
<filter-class>com.qfedu.a_filter.BFilter</filter-class>
</filter>
<filter-mapping>
<!-- 对应过滤器的名字 -->
<filter-name>BFilter</filter-name>
<!-- 限制过滤路径 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<!-- 对应过滤器的名字 -->
<filter-name>BFilter</filter-name>
<!-- 限制过滤路径 -->
<url-pattern>/Day45/user</url-pattern>
</filter-mapping>
</web-app>
1.1.3 配置方式总结
1. 关注的重点是需要过滤的URL-Pattern,那些资源那需要进行过滤操作
2. 允许多个Filter同时过滤限制一个路径,例如 机场安检,过安检需要同时完成三次安检
1.2 过滤器链
image.png
过滤器链执行顺序,两种方式考虑
1. 使用注解方式配置过滤器,按照过滤器类名的字典顺序对应过滤器链的执行顺序
2. 使用web.xml文件配置filter过滤器,根据<filter-mapping>在web.xml文件中的顺序来觉得过滤器链的执行流程。
1.3 Filter过滤器初始化参数
指定加载一定的资源
敏感词汇过滤,跟当前的过滤器进行绑定,这里可以使用初始化参数
1. 通过注解方式完成配置
2. 通过web.xml
// 注解方式完成
@WebFilter(value = "/*",
initParams = {@WebInitParam(name = "filename", value = "saolei.properties")})
<!-- XML配置方式完成 -->
<filter>
<filter-name>EFilter</filter-name>
<filter-class>com.qfedu.a_filter.EFilter</filter-class>
<!-- 初始化参数 -->
<init-param>
<param-name>filename</param-name>
<param-value>saolei.xml</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1.4 案例
1.4.1 过滤器辅助完成对应Request和Response编码集处理
package com.qfedu.util;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 访问服务器请求和对应的响应编码集处理
* Apache Tomcat/7.0.103
* Apache Tomcat/8.5.41
* Apache Tomcat/9.0.33
*
* Tomcat 7 以及 一下版本存在ISO-8859-1
*
* @author MI 2020/4/2 11:03
*/
@WebFilter("/*")
public class AEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 获取当前Tomcat服务器版本信息
String serverInfo = request.getServletContext().getServerInfo();
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if (serverInfo.startsWith("Apache Tomcat/7") || serverInfo.startsWith("Apache Tomcat/6")) {
// 存在ISO-8859-1
// 留下一个小问题
System.out.println("Tomcat 6 or 7");
} else {
// Tomcat 8以及以上处理POST请求中文乱码问题和对应Response响应乱码问题
System.out.println("Tomcat 8 or 9");
httpServletRequest.setCharacterEncoding("utf-8");
httpServletResponse.setContentType("text/html;charset=utf-8");
}
chain.doFilter(httpServletRequest, httpServletResponse);
}
@Override
public void destroy() {
}
}
1.4.2 登录过滤
package com.qfedu.util;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 判断是否登录,如果登录状态才可以访问对应的资源,未登录不能访问
* 当前过滤器的路径是整个项目
* 需要考虑
* imgServlet,indexServlet,logout 不可以访问
* login,login.html 可以访问
* 判断登录条件
* HttpSession
* @author MI 2020/4/2 11:20
*/
@WebFilter("/*")
public class BLoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 1. 判断申请访问的资源名
String requestURI = req.getRequestURI();
System.out.println(requestURI);
// 2. 用户当前访问的登录操作,可以放行
if (requestURI.contains("login")) {
System.out.println("登录操作放行\n");
chain.doFilter(req, resp);
} else {
// 3. 检查用户登录状态
HttpSession session = req.getSession(false);
// Session不存在,获取信息不对称
if (null == session || !"骚磊".equals(session.getAttribute("name"))) {
System.out.println("资源不匹配\n");
resp.sendRedirect("login.html");
} else {
// 放行
System.out.println("登录状态放行\n");
chain.doFilter(req, resp);
}
}
}
@Override
public void destroy() {
}
}
2. Listener 监听器
2.1 什么是监听器
监听器如果考虑生活中的常见案例,安检通道,数据抓取。对于一种事件的监听机制,同时会触发一些操作。
WEB Application中监听器Listener来监听WEB服务对象创建, 信息的创建,修改,删除,增加... 发现以上问题,可以【触发】监听器,从而进行一系列的其他操作。
例如:
1. WEB Application项目启动,ServletContext会被创建,可以监听
ServletContext对象创建过程,触发一些对于当前WEB Application项目的初
始化过程。
2. 监听当前一些Session信息,可以获取当前网站的访问量,当时的用户量...
3. 属性修改,可以保存日志记录
2.2 监听器类型
2.2.1 监听ServletContext变化
ServletContextListener
public void contextInitialized(ServletContextEvent sce);
监听ServletContext初始化操作
用于ServletContext对象在创建过程中,其实就是整个WEB Application加载
的过程中初始化一定的参数,例如 资源信息,数据操作,驱动加载...
public void contextDestroyed(ServletContextEvent sce);
监听ServletContext销毁操作
用于ServletContext对象销毁过程中,也就是整个WEB Application关闭,可
以使用该方法销毁资源内容
2.2.2 静态ServletContext属性变化
ServletContextAttributeListener
public void attributeAdded(ServletContextAttributeEvent scae);
监听ServletContext对象中属性添加操作。
public void attributeRemoved(ServletContextAttributeEvent scae);
监听ServletContext对象中属性删除操作。
public void attributeReplaced(ServletContextAttributeEvent scae);
监听ServletContext对象中属性更改操作。
2.2.3 监听HttpSession对象变化
HttpSessionListener
public void sessionCreated(HttpSessionEvent se);
监听Session被创建
public void sessionDestroyed(HttpSessionEvent se);
监听Session被销毁
2.2.4 监听HttpSession属性值变化
HttpSessionAttributeListener
public void attributeAdded(HttpSessionBindingEvent se);
监听HttpSession对象中属性添加操作。
public void attributeRemoved(HttpSessionBindingEvent se);
监听HttpSession对象中属性删除操作。
public void attributeReplaced(HttpSessionBindingEvent se);
监听HttpSession对象中属性更改操作。
2.2.5 监听HttpSession存储和读取
HttpSessionActivationListener
public void sessionWillPassivate(HttpSessionEvent se);
监听HttpSession对象存储操作
public void sessionDidActivate(HttpSessionEvent se);
监听HttpSession对象读取操作
2.2.6 监听ServletRequest对象变化
ServletRequestListener
public void requestDestroyed(ServletRequestEvent sre);
ServletRequest销毁
public void requestInitialized(ServletRequestEvent sre);
ServletRequest对象初始化过程
2.2.7 监听ServletRequest对象属性变化
ServletRequestAttributeListener
public void attributeAdded(ServletRequestAttributeEvent srae);
Request对象属性添加操作监听
public void attributeRemoved(ServletRequestAttributeEvent srae);
Request对象属性删除操作监听
public void attributeReplaced(ServletRequestAttributeEvent srae);
Request对象属性更新操作监听
2.3 监听器的两种配置方式
2.3.1 注解方式
/**
* 当前类实现ServletContextListener接口,完成方法,但是没有注册
*
* @author MI 2020/4/2 14:55
*/
@WebListener
public class ApplicationLifeListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Servlet Context Object Initialized...");
try {
Class.forName("com.qfedu.c_listener.TestDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Servlet Context Object Destroyed...");
}
}
2.3.2 web.xml配置
<!-- 在web.xml标注当前监听器使用的是哪一个类 -->
<listener>
<listener-class>com.qfedu.c_listener.HttpSessionLiftListener</listener-class>
</listener>
2.4 小案例
2.4.1 ServletRequest属性变化监听演示
package com.qfedu.d_requestAttr;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.annotation.WebListener;
/**
* @author MI 2020/4/2 15:08
*/
@WebListener
public class ServletRequestAttrListener implements ServletRequestAttributeListener {
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("Add Name : " + srae.getName());
System.out.println("Add value : " + srae.getValue());
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("Remove Name:" + srae.getName());
System.out.println("Remove value:" + srae.getValue());
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("Replace Name:" + srae.getName());
System.out.println("Replace value:" + srae.getValue());
}
}
3. BaseServlet封装
3.1 目前面临的问题
目前WEB Application中每一个业务逻辑都需要一个对应的Servlet进行操作,而且Servlet中处理的方法都是一直的,doGet和doPost,这里会导致一些问题
1. Servlet过多,服务器有压力
a. URL-Pattern 匹配压力
b. 内存压力
c. 处理压力
2. 程序员也有压力的
a. 一个业务操作一个Servlet
b. 匹配原则一大堆
c. 代码冗余
一个核心Servlet完成操作的分发
3.2 当前创建Servlet的方式
自定义Servlet程序继承HttpServlet,重写doGet和doPost方法,重复性太高!!!
StudentSystem学生管理系统
请求URL
http://localhost:8080/ss/addStudent
http://localhost:8080/ss/deleteStudent
http://localhost:8080/ss/updateStudent
http://localhost:8080/ss/removeStudent
http://localhost:8080/ss/listStudent
代码中需要5个Servlet来满足操作。
每一个Servlet对应一个Student操作业务逻辑,是否可以规划到一个Servlet中,在URL请求上做出约束,约束完成方法
改变URL策略
http://localhost:8080/ss/studentServlet?method=addStudent
http://localhost:8080/ss/studentServlet?method=deleteStudent
http://localhost:8080/ss/studentServlet?method=updateStudent http://localhost:8080/ss/studentServlet?method=removeStudent
http://localhost:8080/ss/studentServlet?method=listStudent
3.3 优化URL,操作方法
在每一个Servlet,利用方法实现功能代码,利用doGet和doPost作为转发操作是可以完成代码,但是复杂度较高,重复性较大
package com.qfedu.studentsystem.servlet;
import com.qfedu.studentsystem.dao.StudentDao;
import com.qfedu.studentsystem.dao.impl.StudentDaoImpl;
import com.qfedu.studentsystem.entity.Student;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
/**
* http://localhost:8080/ss/studentServlet?method=addStudent
* http://localhost:8080/ss/studentServlet?method=deleteStudent
* http://localhost:8080/ss/studentServlet?method=updateStudent
* http://localhost:8080/ss/studentServlet?method=modifyStudent
* http://localhost:8080/ss/studentServlet?method=listStudent
*
* @author MI 2020/4/2 16:01
*/
@WebServlet("/studentServlet")
public class StudentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("method");
try {
Method method1 = this.getClass().getMethod(method, HttpServletRequest.class, HttpServletResponse.class);
method1.invoke(this, req, resp);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public void addStudent(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Map<String, String[]> parameterMap = req.getParameterMap();
Student student = new Student();
try {
BeanUtils.populate(student, parameterMap);
StudentDao studentDao = new StudentDaoImpl();
studentDao.addStudent(student);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
// 回到所有学生信息页面中 重定向到学生列表中
resp.sendRedirect("studentServlet?method=listStudent");
}
public void deleteStudent(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String id = req.getParameter("id");
System.out.println(id);
new StudentDaoImpl().deleteStudent(Integer.parseInt(id));
// 重定向到ListStudentServlet中
resp.sendRedirect("studentServlet?method=listStudent");
}
public void updateStudent(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String id = req.getParameter("id");
Student student = new StudentDaoImpl().findStudentById(Integer.parseInt(id));
resp.getWriter().append(html);
}
public void modifyStudent(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Map<String, String[]> parameterMap = req.getParameterMap();
Student student = new Student();
try {
BeanUtils.populate(student, parameterMap);
new StudentDaoImpl().updateStudent(student);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
resp.sendRedirect("studentServlet?method=listStudent");
}
public void listStudent(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 找出所有学生的List集合
List<Student> allStudent = new StudentDaoImpl().findAllStudent();
}
}
3.4 剥离Servlet核心操作
package com.qfedu.studentsystem.util;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 完成一个BaseServlet操作
*
* @author MI 2020/4/2 16:23
*/
public abstract class BaseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从用户请求url中获取对应的method参数,也就是需要执行的方法
String methodName = req.getParameter("method");
try {
/*
this是谁???
abstract修饰的类是没有自己的类对象的,this不能表示BaseServlet对象
this对应的是执行当前service方法的类对象,其实就是BaseServlet子类对象
*/
System.out.println("BaseServlet this : " + this);
Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, req, resp);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
网友评论