过滤器监听器上下文servlet线程安全问题.docx
《过滤器监听器上下文servlet线程安全问题.docx》由会员分享,可在线阅读,更多相关《过滤器监听器上下文servlet线程安全问题.docx(18页珍藏版)》请在冰豆网上搜索。
过滤器监听器上下文servlet线程安全问题
过滤器、监听器、上下文、servlet线程安全问题
过滤器:
什么是过滤器:
servlet规范当中定义的一种特殊的组件,用来拦截servlet容器的调用过程。
会先调过过滤器的方法,过滤器决定是否向后继续调用就是调用servlet容器
容器收到请求之后通常情况下会调用servlet的service方法来处理请求。
如果有过滤器,则容器先调用过滤器的方法
如何写一个过滤器:
1、写一个Java类,实现接口Filter
2、在doFilter方法里面,编写拦截处理逻辑
3、配置过滤器(web.xml)让容器知道哪些请求需要拦截
比如写一个评论,然后显示出来。
但是说一些敏感字。
就不允许其评论
但是已经写完了的话,评论与后端的servlet的话。
可以直接加个过滤器
容器只要一启动,就会立即创建过滤对象。
只会创建一个。
容器在创建过滤器对象之后会调用该对象的init方法。
该方法只会执行一次。
容器调用doFilter方法来处理请求
FilterChain(过滤器链)
过滤器
CommentFilterA
packageweb;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.FilterConfig;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassCommentFilterAimplementsFilter{
publicvoiddestroy(){
//TODOAuto-generatedmethodstub
}
//容器会将request和response作为参数传递过来。
//下面两个arg0和arg1就是但是是Servlet的。
用的是其子类HttpServlet。
那么就强制转换成其子类
//如果调用了FilterChain的doFilter方法,则容器会继续向后调用,如果没有调用doFilter就爱不会继续向后调用
publicvoiddoFilter(ServletRequestarg0,ServletResponsearg1,
FilterChainarg2)throwsIOException,ServletException{
//TODOAuto-generatedmethodstub
HttpServletRequestrequest=(HttpServletRequest)arg0;
HttpServletResponseresponse=(HttpServletResponse)arg1;
request.setCharacterEncoding("utf-8");
Stringcontent=request.getParameter("content");
if(content.indexOf("日")!
=-1){
//包含了敏感字
PrintWriterout=response.getWriter();
out.print("!
!
!
yourcommnetiserror");
return;
}else{
//没有,继续向后调用
arg2.doFilter(arg0,arg1);
}
}
publicvoidinit(FilterConfigarg0)throwsServletException{
//TODOAuto-generatedmethodstub
}
}
一定要记得配置!
!
web.xml
--配置过滤器-->
filterA
web.CommentFilterA
filterA
/*这个就是访问什么url就会通过过滤器写的评论是将评论内容发向process的,所以~我写了个*.jsp就根本不能访问了!
!
!
哈哈哈*/
/process
练习:
评论的字符的个数有限制
写一个过滤器,检测评论的字符的个数,如果超过10个,则提示评论的字数过多。
CommentFilterB.java
packageweb;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.FilterConfig;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassCommentFilterBimplementsFilter{
publicvoiddestroy(){
//TODOAuto-generatedmethodstub
}
publicvoiddoFilter(ServletRequestarg0,ServletResponsearg1,
FilterChainarg2)throwsIOException,ServletException{
//TODOAuto-generatedmethodstub
HttpServletRequestrequest=(HttpServletRequest)arg0;
HttpServletResponseresponse=(HttpServletResponse)arg1;
request.setCharacterEncoding("utf-8");
Stringmessage=request.getParameter("content");
PrintWriterout=response.getWriter();
if(message.length()>10){
out.print("
评论字数不得超过10个!
");
return;
}else{
arg2.doFilter(arg0,arg1);
}
}
publicvoidinit(FilterConfigarg0)throwsServletException{
//TODOAuto-generatedmethodstub
}
}
过滤器的优先级
当有多个过滤器都满足过滤的条件,则容器会依据的先后顺序来调用。
但是这么的就固定限制了,只能是日和10个。
那么就~
--初始化参数-->
illegalStr
日你
在配置文件中先配置好,然后去读取初始化参数
更改CommentFilterA.java过滤器
就是给init增加一个属性,因为init执行一次就没了。
那么变量也没了。
创建一个全局变量来存储容器传进来
的FilterConfig对象
然后在方法中读取配置文件中对应名字的内容
packageweb;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.FilterConfig;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassCommentFilterAimplementsFilter{
privateFilterConfigconfig;
//用config来存储init中的读取初始化参数的对象。
publicvoiddestroy(){
}
//容器会将request和response作为参数传递过来。
//下面两个arg0和arg1就是但是是Servlet的。
用的是其子类HttpServlet。
那么就强制转换成其子类
//如果调用了FilterChain的doFilter方法,则容器会继续向后调用,如果没有调用doFilter就爱不会继续向后调用
publicvoiddoFilter(ServletRequestarg0,ServletResponsearg1,
FilterChainarg2)throwsIOException,ServletException{
//TODOAuto-generatedmethodstub
HttpServletRequestrequest=(HttpServletRequest)arg0;
HttpServletResponseresponse=(HttpServletResponse)arg1;
request.setCharacterEncoding("utf-8");
Stringcontent=request.getParameter("content");
Stringmingan=config.getInitParameter("illegalStr");
if(content.indexOf(mingan)!
=-1){
//包含了敏感字
PrintWriterout=response.getWriter();
out.print("!
!
!
yourcommnetiserror");
return;
}else{
//没有,继续向后调用
arg2.doFilter(arg0,arg1);
}
}
publicvoidinit(FilterConfigarg0)throwsServletException{
//TODOAuto-generatedmethodstub
//将容器传递过来的FilterConfig对象保存下来
config=arg0;
}
}
过滤器优点
1、不用修改原有程序,在原有程序上增加一些新的功能。
2、将多个组件相同的处理逻辑集中写在过滤器里面,方便代码的维护。
(比如登陆,可以把验证写在过滤器里)
监听器
什么是监听器:
servlet规范当中定义的一种特殊的组件,用来监听容器产生的事件的。
容器会产生什么事件:
主要有两大类:
1、生命周期相关的时间
容器创建或者销毁了request,session,servlet,上下文时产生的事件。
比如说现在有多少人在访问应用,session是用来保存状态的。
只用统计session的个数。
他的创建和销毁,做个监听器进行监听
2、绑定数据相关的事件
调用了request,session,servlet上下文的setAttribute,removeAttribute时产生的事件。
servlet上下文
什么是servlet上下文
容器启动之后,会为每一个web应用创建唯一的一个符合ServletContext接口要求的对象
特点:
一个web应用对应一个唯一的上下文
只要容器没有关闭,并且应用没有被删除,则上下文会一直存在。
如何获得上下文
4种方式
GenericServlet,ServletConfig,FilterConfig,HttpSession提供了一个getServletContext方法来获得上下文。
作用:
1、绑定数据
setAttribute,getAttribute,removeAttribute
注:
将数据绑定到上下文上面,可以随时访问。
因为他一直在。
转发
request.setAttribute
绑定到session上
session.setAttrobute
也可以ServletContext.setAttribute
绑定的不同上面不同,ServletContext时间最长,但是占用内存。
所有优先使用保存时间短的即request
在满足使用条件的情况下,优先使用生命周期短的
(request例子:
先写一个ServletA来在上下文中绑定数据,然后在ServletB中读取数据。
这个数据是随web应用的结束而结束,就算浏览器关闭了也还在。
ServletA:
packageweb;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.ServletContext;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassAextendsHttpServlet{
publicvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
response.setContentType("text/html");
PrintWriterout=response.getWriter();
//先获得上下文
ServletContextctx=getServletContext();
//将一些数据绑定到上下文
ctx.setAttribute("userlist","ddd,qqq,lihaile");
out.close();
}
}
ServletB:
packageweb;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.ServletContext;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassBextendsHttpServlet{
publicvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
response.setContentType("text/html");
PrintWriterout=response.getWriter();
//获得上下文
ServletContextctx=getServletContext();
Stringuserlist=(String)ctx.getAttribute("userlist");
out.print("
"+userlist+"
");
out.close();
}
}
2、访问全局的初始化参数
--全局的初始化参数-->
company
Recar
然后读取全局的初始化参数:
StringgetInitParamenter(StringparamName);
如何写一个监听器:
1、写一个java类实现相应的接口
要根据监听的事件类型来选择合适的接口。
比如,要监听session的创建和销毁,需要实现HttpSessionListener接口
2、在这个接口方法当中,实现监听处理逻辑
3、配置web.xml
例子:
实现统计在线人数
把count这个数据绑定到山下文中。
通过session事件的创建于销毁来检测当前在线人数,创建则加,销毁则减
用一个浏览器看就是在线人数1,我用了3个浏览器看就变成3个了。
一个浏览器在开新窗口还是人数不会变的,因为无论开多少窗口都会用那个session的
sessionId会保存在内存中。
增加登出。
就是销毁session
再写一个servlet来登出,就是删除其session。
触发session销毁事件,减少一个在线人数
监听器:
Countlister.java
packageweb;
importjavax.servlet.ServletContext;
importjavax.servlet.http.HttpSession;
importjavax.servlet.http.HttpSessionEvent;
importjavax.servlet.http.HttpSessionListener;
publicclassCountlistenerimplementsHttpSessionListener{
/*
*session对象创建之后,容器会调用此方法
*/
publicvoidsessionCreated(HttpSessionEventarg0){
//TODOAuto-generatedmethodstub
//获得上下文,先通过session事件对象来获取session
HttpSessionsession=tSession();
ServletContextctx=session.getServletContext();
Integercount=(Integer)ctx.getAttribute("count");
if(count==null){
count=1;
}else{
count++;
}
ctx.setAttribute("count",count);
}
/*
*session对象销毁之后,容器会调用此方法。
*/
publicvoidsessionDestroyed(HttpSessionEventarg0){
HttpSessionsession=arg0.getSession();
ServletContextctx=session.getServletContext();
Integercount=(Integer)ctx.getAttribute("count");
count--;
ctx.setAttribute("count",count);
}
}
显示当前在线人数:
当前在线人数:
<%=application.getAttribute("count")%>
登出!
登出,销毁session类
LogoutServlet.java
packageweb;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.ServletContext;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.servlet.http.HttpSession;
publicclassLogoutServletextendsHttpServlet{
publicvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
HttpSessionsession=request.getSession();
session.invalidate();
}
}
servlet线程安全问题
为什么说servlet会存在线程安全问题?
容器收到请求会启动一个线程
当有多个线程访问一个方法。
packageweb;
importjava.io.IOException;