前言:一句话概括启动的顺序
context param上下文参数——>listener监听器——>filter过滤器——>servlet——>intercept拦截器
生命周期
context-param上下文参数
当容器tomcat启动的时候会读取web.xml中的<context-param>标签和<listener>标签并初始化ServletContext(上下文)对象(ServletContext代表当前web项目,当前web项目中的所有内容都共享此对象)
然后容器tomcat将<context-param>中的内容解析为键值对赋予ServletContext(这也就是为什么我们可以通过context-param的键获得context-param的值)
之后容器创建<listener></listener>中的类实例,即创建监听器,在监听中会有contextInitialized(ServletContextEvent args)初始化方法,通过在这个方法中的参数获得ServletContext = ServletContextEvent.getServletContext();
最后得到context-param的值 = ServletContext.getInitParameter("context-param的键");
listener监听器得到参数后就可以进行换句话说,这个时候,对<context-param>中的键值对做的操作,将在的WEB项目完全启动之前被执行,常通过在web.xml中的配置来加载spring的配置文件
<listener> <!-- 继承于ServletContextListener,在tomcat启动和关闭时会调用 --> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <!-- 要读取到servletContext容器中的spring配置文件的路径 --> <param-value>classpath:ApplicationContext.xml</param-value>
</context-param>
listener监听器
根据执行的时机不同划分,常用的监听器一共有三类,都是继承于父接口EventListener的子接口
ServletContextListener接口
Tomcat启动和关闭时调用的监听器,也就是说在tomcat启动时创建,tomcat关闭时销毁
- public void contextInitialized(ServletContextEvent evt):ServletContext对象被创建后调用;
- public void contextDestroyed(ServletContextEvent evt):ServletContext对象被销毁前调用;
HttpSessionListener接口
开始会话和结束会话时调用的监听器,也就是说开启session时创建,session失效时销毁
- public void sessionCreated(HttpSessionEvent evt):HttpSession对象被创建后调用;
- public void sessionDestroyed(HttpSessionEvent evt):HttpSession对象被销毁前调用;
关于session失效有两种情况,一种是session.invalidate()方法注销session,另一种是超过了session的存活时间,tomcat中默认是30分钟,可以到tomcat/conf/web.xml文件中更改设置
ServletRequestListener接口
开始请求和结束请求时调用的监听器,也就是说接收request请求时创建,完成request请求后销毁
- public void requestInitiallized(ServletRequestEvent evt):ServletRequest对象被创建后调用;
- public void requestDestroyed(ServletRequestEvent evt):ServletRequest对象被销毁前调用。
filter过滤器
init(FilterConfig):在服务器启动时会创建Filter实例,并且每个类型的Filter只创建一个实例,从此不再创建!在创建完Filter实例后,会马上调用init()方法完成初始化工作,这个方法只会被执行一次;
doFilter(ServletRequest req,ServletResponse res,FilterChain chain):这个方法会在用户每次访问指定过滤范围时都会执行doFileter,如果需要“放行”,那么需要调用FilterChain的doFilter(ServletRequest,ServletResponse)方法,如果不调用FilterChain的doFilter()方法,那么目标资源将无法执行;
destroy():服务器会在创建Filter对象之后,把Filter放到缓存中一直使用,通常不会销毁它。一般会在服务器关闭时销毁Filter对象,在销毁Filter对象之前,服务器会调用Filter对象的destory()方法。
servlet
默认在第一次接受浏览器请求时创建servlet实例对象,在tomcat关闭时销毁
javax.servlet.Servlet接口中,有三个方法说明了Servlet的生命周期:
void init(ServletConfig):创建后马上调用init()完成初始化;
void service(ServletRequest,ServletResponse):每次处理请求时调用service()方法;
void destroy():当Tomcat要销毁Servlet实例时,先调用destroy()方法;
可以在web.xml文件中将servlet的创建时机改为和服务器同步
interceptor拦截器
和过滤器类似,但是只拦截用户对action的请求,tomcat启动时创建,拦截到用户对action的请求时执行前置拦截器栈,放行后执行action逻辑并返回页面视图,最后再执行后置拦截器栈进行收尾工作
void init():创建后马上调用init()完成初始化;
String intercept(ActionInvocation invocation):通过invocation.invoke()方法继续执行拦截器栈中的其他拦截器
void destroy():当Tomcat要销毁Interceptor实例时,先调用destroy()方法;
前置拦截——>action——>页面执行——>后置拦截
实际应用
过滤器filter
过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集, 或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。
最常用的过滤器就是登陆过滤器LoginFilter,过滤器一般都是继承Filter 接口
监听器listener
当你要触发一个事件,但这件事既不是过滤,又不是拦截,那很可能就是监听。 联想到Windows编程里的,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。监听器的概念类似于这些。
上下文参数context-param
用来存储要应用在web项目全局中的参数
servlet
用来处理tomcat等容器解析后的请求
拦截器interceptor:
进行权限验证,或者是来判断用户是否登陆,日志记录,或者限制时间点访问。我自己用过拦截器,是用户每次登录时,都能记录一个登录时间。 (这点用拦截器明显合适,用过滤器明显不合适,因为没有过滤任何东西)
作用范围
代码和配置
context-param 要读取到servletContext中的配置文件+web.xml配置
listener,filter,servlet java类+web.xml配置
interceptor java类+struts.xml配置
context-param上下文参数
context-param在web.xml文件中配置要读取的xml文件路径即可,一般都是读取spring的ApplicationContext.xml文件
<listener> <!-- 继承于ServletContextListener,在tomcat启动和关闭时会调用 --> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <!-- 要读取到servletContext容器中的spring配置文件的路径 --> <param-value>classpath:ApplicationContext.xml</param-value> </context-param>
listener监听器
java类:根据监听范围需求的不同实现ServletContextListener或HttpSessionListener或ServletRequestListener三者之一
package com.rl.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * 自定义监听器实现了ServletContextListener接口,也会在服务器启动时初始化 */ public class MyServletContextListener implements ServletContextListener { /** * 服务器停止时销毁 */ public void contextDestroyed(ServletContextEvent sce) { System.out.println("MyServletContextListener被销毁"); } /** * 服务器启动时创建 */ public void contextInitialized(ServletContextEvent sce) { System.out.println("MyServletContextListener被创建"); ServletContext sc = sce.getServletContext(); } }
配置listener:在web.xml文件中配置listener类的路径即可
<listener> <!--指定作为监听器的类--> <listener-class>com.rl.listener.MyRequestListener</listener-class> </listener>
filter过滤器
java类:作为过滤器的类要实现Filter接口
package com.rl.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class HelloFilter implements Filter { /** * 当服务器关闭当前方法被调用,实例被销毁 */ public void destroy() { System.out.println("过滤器被销毁..."); } /** * 过滤器的执行方法 */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("过滤器开始...."); //让过滤器往下走,放行,如果后面还有过滤器,那么就执行下一个过滤器,如果是最后一个过滤器就去执行Controller chain.doFilter(request, response); System.out.println("过滤器结束...."); } /** * 当服务器启动的时候被执行,说明过滤器的实例是在服务器启动时被创建的 */ public void init(FilterConfig filterConfig) throws ServletException { System.out.println("过滤器被创建..."); } }
配置filter:filter要在web.xml文件中配置filter类的路径和filter过滤的范围
<filter> <filter-name>helloFilter</filter-name> <!--指定作为过滤器的类--> <filter-class>com.rl.filter.HelloFilter</filter-class> </filter> <filter-mapping> <filter-name>helloFilter</filter-name> <!--过滤器过滤的范围--> <url-pattern>/*</url-pattern> </filter-mapping>
servlet
java类:实现Servlet接口或继承HttpServlet类(HttpServlet的父类GenericServlet实现了Servlet接口)
package com.rl.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * doGet是给get方式的http的请求做相应的 * doPost是给post方式的http的请求做相应的 */ public class HttpServletDemo extends HttpServlet { @Override public void init() throws ServletException { System.out.println("HttpServletDemo实例被创建了"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doGet方法被调用了"); resp.getOutputStream().write("doGet方法被调用了".getBytes()); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("doPost方法被调用了"); doGet(req, resp); } }
配置servlet:要在web.xml文件中配置servlet类的路径和servlet的浏览器访问路径
<servlet> <!-- 设置servlet的名字 --> <servlet-name>helloServlet</servlet-name> <!-- 具体的servlet的类 --> <servlet-class>com.rl.servlet.ServletDemo1</servlet-class> <!-- 将servlet的启动时机设置为和服务器同步 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <!-- 指定要映射 的servlet的名字 --> <servlet-name>helloServlet</servlet-name> <!-- servlet的具体映射路径 --> <url-pattern>/hello</url-pattern> </servlet-mapping>
interceptor过滤器
java类:作为Interceptor拦截器的类要继承于AbstractInterceptor类或继承于MethodFilterInterceptor,要想排除对指定的action的拦截,就一定要继承MethodFilterInterceptor类(它的父类也是AbstractInterceptor)
package com.rl.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class MyInterceptor extends AbstractInterceptor { @Override public void init() { System.out.println("创建拦截器"); } @Override public void destroy() { System.out.println("销毁拦截器"); } @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("前置拦截执行...."); //让拦截器向下走并且返回结果代码 String result = invocation.invoke(); System.out.println("后置拦截执行..."); return result; } }
要拦截的action类
package com.rl.action; import com.opensymphony.xwork2.ActionSupport; /** * 担当控制层的Action动作类 */ public class PersonAction2 extends ActionSupport{ public String savePerson(){ System.out.println("新增Person"); return super.SUCCESS; } public String deletePerson(){ System.out.println("删除Person"); return super.SUCCESS; } }
interceptor配置:牢记在interceptor标签中,action要放在其他标签的后面,不然会报错
<package name="testInterceptor" extends="struts-default" namespace="/person"> <interceptors> <!-- 配置我们自己的拦截器 --> <interceptor name="myInterceptor" class="com.rl.interceptor.MyInterceptor"></interceptor> <!-- 配置我们自己的拦截器栈 --> <interceptor-stack name="myStack"> <!-- 默认的拦截器栈 --> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="myInterceptor"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 包内默认拦截器栈设置为我们自己的myStack --> <default-interceptor-ref name="myStack"></default-interceptor-ref> <action name="*" class="com.rl.action.PersonAction2" method="{1}Person"> <result name="success">/success.jsp</result> </action> </package>
参考链接:
过滤器,拦截器,监听器具体应用上的区别 - 阿里云 https://yq.aliyun.com/ziliao/365762
session会话过期时间设置 https://blog.csdn.net/zqd_java/article/details/53687954
web.xml的配置中<context-param>配置作用 https://www.cnblogs.com/jiaguozhilian/p/5819032.html
struts.xml文件中package标签中的子标签顺序 https://blog.csdn.net/lc448986375/article/details/8027150
servlet百度百科 https://baike.baidu.com/item/servlet/477555?fr=aladdin