写在前面:
其实现在更多的人在使用springboot框架来搭建项目,好处是简单方便拓展,但是想要学习以上说明的三个功能,我个人感觉还是从SSM框架进行切入,能够方便理解。另外,这篇博客更多的是记录如何使用,涉及原理的部分需要更多得学习。
SSM框架和监听器、过滤器、拦截器 —— 监听器
能帮助开发者监听web中的特定事件,比如ServletContext,HttpSession,ServletRequest的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控。
1.1 初识监听器,了解web.xml加载过程
我们应该都知道,对于ssm搭建的javaWeb项目,一般都是使用Tomcat或Jboss等服务器容器来启动的,java项目启动配置弄好之后,启动容器,就可以将项目启动。一般 tomcat 或者 Jboss 都是先访问加载web项目的web.xml配置文件。
容器一般是先加载该配置文件中的节点<context-param> 和 <listener>:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
紧接着容器会创建ServletContext,这是属于该web项目的上下文环境,所谓上下文环境其实也可以理解为一个容器,里面装着web的应用信息。注意一个web项目也只有一个ServletContext,其生命周期是
- 创建:容器启动(将web应用加载到容器中)
- 销毁:容器关闭,服务器shut down
那这个ServletContext对象的作用是什么呢?
- 获得web应用全局初始化参数,之后项目中便可以调用这些参数。
- 可以通过这个context获得web项目中任何资源的绝对路径:
string path= context.getrealpath(“相对于该web应用的相对路径”);注意:src下的文件被服务器内部加载会被放到WEB-INF/classes文件夹中。
例:string path_c =context.getrealpath(“WEB-INF/classes/c.txt”)
也可以使用类加载器,专门加载classes文件夹下的文件。xxxservlet.class.getClassloader().getresource(“c.txt”).getpath();其中c.txt 是相对于classes的相对路径。
- 它是一个域对象,域对象:存储数据的区域就是域对象。
ServletContext域对象的作用范围是整个web应用(所有web应用中的自资源都可以向ServletContext对象中存储数据,而且数据可以共享)。
首先容器以<context-param>中的<param-name>为键,<param-value>为值,将数据转换为键值对,存入ServletContext中;
接着容器会创建<lisrtener>中的类实例,根据配置的class的类路径<listener-class>,来创建监听对象ContextLoaderListener,该对象中的 contextInitialized 是初始化方法,启动web时,调用该方法,得到ServletContextEventevent.getServletContext();
public void contextInitialized(ServletContextEvent event){ this.initWebApplicationContext(event.getServletContext()); }
接着容器会读取<filter>节点,根据指定类路径来实例化过滤器;
以上是在web项目还没有完全启动起来的时候,已经完成了工作。如果系统中有Servlet,则servlet是在第一次调用的时候被实例化的,且一般不会被容器销毁,他可以服务多个用户的请求。所以Servlet会被上面的元素初始化的要迟;
总的来说,web.xml的加载顺序是:
<context-param> —> <listener> —> <filter> —> <servlet>,如果web.xml中出现了相同的元素,则按照元素出现的先后顺序加载。
1.2 要怎么编辑自定义监听器呢?
从上述可以知道,想要SSM项目启动需要在web.xml中配置上监听器ContextLoaderListener,而该监听器的作用是 可以初始化<context-param>中的参数到ServletContext中。
那么我们如果想要自定义监听器的话就可以参考ContextLoaderListener。我们先看一下该类中的源码是怎么实现的:
package org.springframework.web.context; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } public void contextDestroyed(ServletContextEvent event) { this.closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
可以看到该监听器继承了接口 ServletContextListener ,接口中有两个需要实现的方法 contextInitialized 和 contextDestroyed:
package javax.servlet; import java.util.EventListener; public interface ServletContextListener extends EventListener { void contextInitialized(ServletContextEvent var1); void contextDestroyed(ServletContextEvent var1); }
很明显是初始化方法和销毁方法。据查询,该接口的实现类的对应的方法,将会在Tomcat 或者 Jboss 容器启动过程中进行回调。
1.2.1 ServletContextListener 接口
因此,如果我们想要在项目启动的时候 自定义加载初始化信息(如:配置数据库信息、设置计时器去定时执行任务等操作)监听器的话,
可以参考在自定义的 Listener 类中去实现 ServletContextListener 接口,如:
package com.shine.platform.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.util.HashMap; import java.util.Map; /** * @Author: Shine EtherealWind * @Description: * @Date: create in 17:07 2021/7/29 */ public class TestMyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { System.out.println("* 应用启动的时候 TestMyListener init... *"); Map<String, String> sysRoles = new HashMap<>(); sysRoles.put("ADMIN","管理员"); sysRoles.put("NEWMAN", "小白用户"); sysRoles.put("BIZUSER", "业务用户"); servletContextEvent.getServletContext().setAttribute("SYS_ROLES", sysRoles); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } }
然后别忘记在web.xml中配置监听
<listener> <listener-class>com.shine.platform.listener.TestMyListener</listener-class> </listener>
就是在启动应用的时候加载TestMyListener 的初始化方法,可以将信息放到ServletContext中。
那在应用层怎么调用呢?
方法一:
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); ServletContext servletContext = webApplicationContext.getServletContext(); Map<String, String> sysRoles = (Map<String, String>)servletContext.getAttribute("SYS_ROLES"); System.out.println("初始化监听器中设置的角色:" + sysRoles.get("ADMIN"));
方法二:
@RequestMapping(value = "/login") @ResponseBody public String login(User user, HttpServletRequest request){ List<Map<String, Object>> list = new ArrayList<Map<String,Object>>(); Map<String, String> sysRoles2 = (Map<String, String>)request.getServletContext().getAttribute("SYS_ROLES"); System.out.println("初始化监听器中设置的角色:" + sysRoles2.get("BIZUSER"));
return null; }
而执行的结果中是能够得到期望结果的
1.2.2 HttpSessionListener接口
对于javaweb项目,当浏览器第一次访问服务器的时候,服务器就会创建一个session。如果我们想要监听当前网站的访问人数,实现对httpsession的监控又要怎么去做呢?
java提供有HttpSessionListener来监听Httpsession。因此我们来实现该接口就可以做到监听session了
package com.shine.platform.listener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * @Author: Shine EtherealWind * @Description: * @Date: create in 9:54 2021/8/5 */ public class TestHttpSessionListener implements HttpSessionListener { private static int visitNum = 0; @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { System.out.println("会话创建的时候调用该方法 TestHttpSessionListener . sessionCreated"); visitNum ++ ; System.out.println("当前系统访问用户数:"+visitNum); } @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { System.out.println("...destroy..."); visitNum -- ; System.out.println("当前系统访问用户数:"+visitNum); } }
注意:不要忘记web.xml里面的配置
<listener> <listener-class>com.shine.platform.listener.TestHttpSessionListener</listener-class> </listener>
在有浏览器第一次访问服务器的时候监听触发sessionCreated方法,而sessionDestroyed方法是在调用session.invalidate()方法时或者超过session有效期被触发
其中session的有效期可以再web.xml中被配置,需要注意单位是分钟:
1.2.3 ServletRequestListener接口
该接口监听的是 ServletRequest 对象,
当客户端向服务器发送一次请求,服务器就会调用初始化方法,
当服务器对这个请求作出响应之后,就会调用销毁方法。
用途:可以用来记录系统被访问的次数。