1、监听器
1.1 什么是监听器
监听器是 Servlet 规范中定义的一种特殊类
- 用来监听 ServletContext、HttpSession 和 ServletRequest 等域对象的创建和销毁事件
- 用来监听域对象的属性发生修改的事件
- 可以在事件发生前、发生后做一些必要的处理
既可以使用 web.xml 进行注册,也可以使用 @WebListener 注解来进行注册
1.2 监听器的用途
- 统计在线人数和用户
- 系统启动的时候加载初始化信息
- 统计网站的访问量
- 和 Spring 相结合
1.3 监听器的分类
Servlet 规范中定义了 9 个监听器接口,可以用来监听 ServletContext、HttpSession 和 ServletRequest 对象的生命周期和属性变化事件
监听器按照监听的事件可以分为 3 大类:
- 监听对象创建和销毁的监听器
- 监听对象中属性变更的监听器
- 监听 HttpSession 中的对象状态改变的监听器
1.3.1 监听对象创建和销毁的监听器
1.3.2 监听对象中属性变更的监听器
1.3.3 监听 HttpSession 中的对象状态改变的监听器
注意:如果监听器是 HttpSessionBindingLinstener,只有实现了这个监听器接口的对象存储到 Session 域的时候,才会触发绑定事件,但是一个类即使没有实现 HttpSessionAttributeListener,那么当这个类的对象存储到 Session 域的时候,也是会触发事件
1.4 监听器的启动顺序
1.5 四种状态
ServletContext,HttpSession,ServletRequest 三个域对象的属性变化(getAttribute,setAttribute,removeAttribute)对象感知监听器,对象需要实现接口,不需要配置 xml
一个对象必须要实现了 Serializable 接口,才能被存储到磁盘上
- 绑定状态:就一个对象被放到 session 域中
- 解绑状态:就是这个对象从 session 域中移除了
- 钝化状态:是将 session 内存中的对象持久化(序列化)到磁盘
- 活化状态:就是将磁盘上的对象再次恢复到 session 内存中
2、过滤器
2.1 什么是过滤器
对事物进行过滤的,对请求进行过滤。使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。如登录控制,权限管理,过滤敏感词汇等
2.2 使用方式
2.2.1 使用注解 @WebFilter
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {
String description() default "";
String displayName() default ""; // filter显示名称
WebInitParam[] initParams() default {};
String filterName() default ""; // 该filter的名字
String smallIcon() default "";
String largeIcon() default "";
String[] servletNames() default {}; // 指定对哪些servlet进行过滤
String[] value() default {}; // 指定拦截路径
String[] urlPatterns() default {}; // 指定拦截路径
DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
boolean asyncSupported() default false; // 是否支持异步模式
}
urlPatterns 和 value 是一样的。urlPatterns 和 value 只能配置一个,不能两个都配置,两个都配置就会报错
2.2.2 使用 web.xml 方式
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.pendulumye.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.3 过滤器生命周期
Filter 有 3 个阶段,分别是初始化,拦截和过滤,销毁。
- 初始化阶段:当服务器启动时,我们的服务器(Tomcat)就会读取配置文件,扫描注解,然后来创建我们的 Filter。
- 拦截和过滤阶段:只要请求资源的路径和拦截的路径相同,那么过滤器就会对请求进行过滤,这个阶段在服务器运行过程中会一直循环。
- 销毁阶段:当服务器(Tomcat)关闭时,服务器创建的 Filter 也会随之销毁
Filter 的三个阶段就对应着 Filter 的 3 个方法:
- init 方法会在 Filter 创建时调用
- doFilter 方法会在请求和拦截匹配时调用
- destroy 方法会在 Filter 销毁时调用
2.4 过滤器执行顺序
在我们的请求到达 Servle 之间是可以经过多个 Filter 的,一般来说,建议 Filter 之间不要有关联,各自处理各自的逻辑即可。这样,我们也无需关心执行顺序问题。如果一定要确保执行顺序,就要对配置进行修改了,执行顺序如下:
- 在 web.xml 中,filter 执行顺序跟声明的顺序有关,先声明的先执行
- 使用注解配置的话,filter 的执行顺序跟名称的字母顺序有关,例如:AFilter 会比 BFilter 先执行
- 如果既有在 web.xml 中声明的 Filter,也有通过注解配置的 Filter,那么会优先执行 web.xml 中配置的 Filter
多个Filter过滤器同时执行的时候,他们都要使用同一个Request对象
2.5 使用案例
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>评论</title>
</head>
<body>
<h1>输入评论内容</h1>
<form action="${pageContext.request.contextPath}/comment" method="post">
<textarea name="message" cols="30" rows="10"></textarea>
<input type="submit" value="提交">
</form>
<p >${requestScope.get("name")}<span style="color: red">${requestScope.get("comment")}</span></p>
</body>
</html>
@WebFilter(servletNames = {"comment"},initParams = {@WebInitParam(name = "sensitiveWord", value = "zz")})
public class CommentFilter implements Filter {
private List<String> sensitiveWords = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//得到敏感词汇
String word = filterConfig.getInitParameter("sensitiveWord");
//加入集合
sensitiveWords.add(word);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//设置编码
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
//得到评论
String message = servletRequest.getParameter("message");
for (String sensitiveWord : sensitiveWords) {
//对所有敏感词汇进行过滤
if (message.contains(sensitiveWord)){
//替换敏感词汇
message = message.replace(sensitiveWord, "**");
}
}
//存入request域
servletRequest.setAttribute("comment",message);
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
@WebServlet(name = "comment",value = "/comment")
public class CommentServlet extends HttpServlet {
//记录评论敏感词汇的ip
private HashSet<String> hashSet = new HashSet<>();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String message = request.getParameter("message");
String comment = (String) request.getAttribute("comment");
if (message.equals(comment)){
System.out.println("没有敏感词汇.....");
//设置名字
request.setAttribute("name","good boy:");
}else {
//有敏感词汇,记录IP
String localAddr = request.getLocalAddr();
System.out.println(localAddr);
hashSet.add(localAddr);
//设置名字
request.setAttribute("name","bad boy:");
}
//转发到comment.jsp页面
request.getRequestDispatcher("/comment.jsp").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
3、拦截器
3.1 什么是拦截器
拦截器(Interceptor)是一种动态拦截方法调用的机制,在 SpringMVC 中动态拦截控制器方法的执行
3.2 使用方式
3.2.1 使用 AOP 切面功能
3.2.2 使用 Spring 的拦截器相关接口
实现拦截器一般需要以下几个步骤:
- 实现 WebMvcConfigurer 接口,重写 addCorsMappings() 方法和 addInterceptors() 方法【配置拦截器】
- 实现 HandlerInterceptor 接口或者继承 HandlerInterceptorAdapter,重写 preHandle() 方法【自定义拦截器】
简单介绍一下WebMvcConfigurer中比较重要的几个方法:
addInterceptors
:添加拦截器addCorsMappings
:跨域addViewControllers
:页面跳转(不用像现在我们要写一个 Controller 进行映射就可实现跳转)addResourceHandlers
:静态资源(自定义静态资源映射目录)configureDefaultServletHandling
:默认静态资源处理器configureViewResolvers
:视图解析器(配置请求视图映射,配置了以后我们返回一个页面路径的字符串时,解析器会帮我们拼装前缀和后缀等信息)configureMessageConverters
:信息转换器(比如我们入参的信息直接转换成 json 或者转换成对应的 bean 类就具体在这里配置)
3.3 拦截器执行流程
3.4 使用案例
@Configuration // 标识这是一个配置类
public class InterceptorConfiguration implements WebMvcConfigurer {
/**
* 配置:允许 http 请求进行跨域访问
* @param corsRegistry
*/
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
corsRegistry.addMapping("/**") // 哪些接口 URL 需要增加跨域设置
.allowedOrigins("*") // 制定的是前端哪些域名被允许跨域访问
.allowCredentials(true) // 需要带 cookie 等凭证时,设置为 true,会把相关信息带上
.allowedMethods("GET", "HEAD", "POST", "DELETE", "OPTIONS") // 指定允许的请求方法
.maxAge(3600); // cookie 失效的时间,单位秒;若设置为 -1,则关闭浏览器失效
}
/**
* 配置:要拦截的路径以及不拦截的路径
* @param interceptorRegistry
*/
@Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
// 注册 Interceptor 拦截器(Interceptor 是我们自己写的拦截器类)
InterceptorRegistration registration = interceptorRegistry.addInterceptor(new LoginInterceptor());
// 添加需要拦截的路径
registration.addPathPatterns("/**");
// 添加不拦截的路径
registration.excludePathPatterns(
"**/*.html",
"**/*.jsp",
"**/*.css"
);
}
@Override
public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
}
@Override
public void addFormatters(FormatterRegistry formatterRegistry) {
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
}
@Override
public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
}
@Override
public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
@Override
public Validator getValidator() {
return null;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
Admin admin = (Admin) request.getSession();.getAttribute(CrowdConstant.ATTR_NAME_LOGIN_ADMIN);
if (admin != null){
return true;
}
// 如果拦截以后重定向的路径,一般设置为登录页面
response.sendRedirect(request.getContextPath() + "login.jsp");
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
1 条评论