过滤器、监听器、上下文、servlet线程安全问题

就像名字写那样,过滤器可以过滤请求,比如对一些评论进行过滤。又不改原有代码的基础上可以加上一个过滤器,或者是登陆验证。集中在一个过滤器中处理。写一个类实现接口 Filter 之后一定要在配置文件中配置!!!监听器可以监听,上下文的概念。

过滤器:

什么是过滤器: servlet规范当中定义的一种特殊的组件,用来拦截servlet容器的调用过程。 会先调过过滤器的方法,过滤器决定是否向后继续调用就是调用servlet容器 容器收到请求之后 通常情况下会调用servlet的service方法来处理请求。如果有过滤器,则容器先调用过滤器的方法

如何写一个过滤器:

1、写一个java类,实现接口Filter 2、在doFilter方法里面,编写拦截处理逻辑 3、配置过滤器(web.xml) 让容器知道哪些请求需要拦截 比如写一个评论,然后显示出来。但是说一些敏感字。就不允许其评论 但是已经写完了的话,评论与 后端的 servlet的话。可以直接加个过滤器 容器只要一启动,就会立即创建过滤对象。只会创建一个。 容器在创建过滤器对象之后会调用该对象的init方法。该方法只会执行一次。 容器调用doFilter方法来处理请求 FilterChain(过滤器链)

过滤器

CommentFilterApackage web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class CommentFilterA implements Filter {    public void destroy() {        // TODO Auto-generated method stub     }        //容器会将request和response作为参数传递过来。        //下面两个arg0和arg1就是 但是是Servlet的。用的是其子类HttpServlet。那么就强制转换成其子类    //如果调用了FilterChain的doFilter方法,则容器会继续向后调用,如果没有调用doFilter就爱不会继续向后调用    public void doFilter(ServletRequest arg0, ServletResponse arg1,            FilterChain arg2) throws IOException, ServletException {        // TODO Auto-generated method stub        HttpServletRequest request = (HttpServletRequest) arg0;        HttpServletResponse response = (HttpServletResponse) arg1;        request.setCharacterEncoding("utf-8");        String content = request.getParameter("content");        if(content.indexOf("日")!=-1){            //包含了敏感字            PrintWriter out = response.getWriter();            out.print("!!! your commnet is error");            return ;        }else{            //没有,继续向后调用            arg2.doFilter(arg0, arg1);        }    }    public void init(FilterConfig arg0) throws ServletException {        // TODO Auto-generated method stub    }}

一定要记得配置!! web.xml

<!-- 配置过滤器 -->    <filter>    <filter-name>filterA</filter-name>    <filter-class>web.CommentFilterA</filter-class>    </filter>    <filter-mapping>    <filter-name>filterA</filter-name>/*这个就是访问什么url就会通过过滤器写的评论是将评论内容发向process的,所以~我写了个*.jsp 就根本不能访问了!!!哈哈哈 */    <url-pattern>/process</url-pattern>    </filter-mapping>

提交敏感字符

过滤器拦截,发现敏感字符。不继续执行。

练习: 评论的字符的个数有限制

写一个过滤器,检测评论的字符的个数,如果超过10个,则提示评论的字数过多。

CommentFilterB.javapackage web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class CommentFilterB implements Filter{    public void destroy() {        // TODO Auto-generated method stub    }    public void doFilter(ServletRequest arg0, ServletResponse arg1,            FilterChain arg2) throws IOException, ServletException {        // TODO Auto-generated method stub        HttpServletRequest request = (HttpServletRequest) arg0;        HttpServletResponse response = (HttpServletResponse) arg1;        request.setCharacterEncoding("utf-8");        String message = request.getParameter("content");        PrintWriter out = response.getWriter();        if(message.length()>10){            out.print("<h1>评论字数不得超过10个!</h1>");            return ;        }else{            arg2.doFilter(arg0, arg1);        }    }    public void init(FilterConfig arg0) throws ServletException {        // TODO Auto-generated method stub    }}

过滤器的优先级

当有多个过滤器都满足过滤的条件,则容器会依据的先后顺序来调用。

但是这么的就固定限制了,只能是日和10个。 那么就~

<!-- 初始化参数 -->    <init-param>    <param-name>illegalStr</param-name>    <param-value>日你</param-value>    </init-param>

在配置文件中先配置好,然后去读取初始化参数

更改CommentFilterA.java过滤器 就是给init增加一个属性,因为init执行一次就没了。 那么变量也没了。创建一个全局变量来存储容器传进来 的FilterConfig对象 然后在方法中读取配置文件中对应名字的内容

package web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class CommentFilterA implements Filter {    private FilterConfig config;    //用config来存储init中的读取初始化参数的对象。    public void destroy() {    }        //容器会将request和response作为参数传递过来。        //下面两个arg0和arg1就是 但是是Servlet的。用的是其子类HttpServlet。那么就强制转换成其子类    //如果调用了FilterChain的doFilter方法,则容器会继续向后调用,如果没有调用doFilter就爱不会继续向后调用    public void doFilter(ServletRequest arg0, ServletResponse arg1,            FilterChain arg2) throws IOException, ServletException {        // TODO Auto-generated method stub        HttpServletRequest request = (HttpServletRequest) arg0;        HttpServletResponse response = (HttpServletResponse) arg1;        request.setCharacterEncoding("utf-8");        String content = request.getParameter("content");        String mingan = config.getInitParameter("illegalStr");        if(content.indexOf(mingan)!=-1){            //包含了敏感字            PrintWriter out = response.getWriter();            out.print("!!! your commnet is error");            return ;        }else{            //没有,继续向后调用            arg2.doFilter(arg0, arg1);        }    }    public void init(FilterConfig arg0) throws ServletException {        // TODO Auto-generated method stub        //将容器传递过来的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<session<上下文)

例子: 先写一个ServletA 来在上下文中绑定数据,然后在ServletB中读取数据。

这个数据是随web应用的结束而结束,就算浏览器关闭了也还在。

ServletA:

package web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class A extends HttpServlet {    public void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType("text/html");        PrintWriter out = response.getWriter();        //先获得上下文        ServletContext ctx = getServletContext();        //将一些数据绑定到上下文        ctx.setAttribute("userlist", "ddd,qqq,lihaile");        out.close();    }}

ServletB:

package web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class B extends HttpServlet {    public void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        response.setContentType("text/html");        PrintWriter out = response.getWriter();        //获得上下文        ServletContext ctx = getServletContext();        String userlist = (String) ctx.getAttribute("userlist");        out.print("<h1>"+userlist+"</h1>");        out.close();    }}

2、访问全局的初始化参数

<!-- 全局的初始化参数 -->    <context-param>    <param-name>company</param-name>    <param-value>Recar</param-value>    </context-param>

然后读取全局的初始化参数:

String getInitParamenter(String paramName);

如何写一个监听器:

1、写一个java类实现相应的接口 要根据监听的事件类型来选择合适的接口。 比如,要监听session的创建和销毁,需要实现HttpSessionListener接口 2、在这个接口方法当中,实现监听处理逻辑 3、配置web.xml

例子:实现统计在线人数

把count这个数据绑定到山下文中。通过session事件的创建于销毁来检测当前在线人数,创建则加,销毁则减

用一个浏览器看就是在线人数1,我用了3个浏览器看就变成3个了。一个浏览器在开新窗口还是人数不会变的,因为无论开多少窗口都会用那个session的 sessionId会保存在内存中。

增加登出。就是销毁session

再写一个servlet来登出,就是删除其session。触发session销毁事件,减少一个在线人数

监听器: Countlister.java

package web;import javax.servlet.ServletContext;import javax.servlet.http.HttpSession;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;public class Countlistener implements HttpSessionListener{    /*     * session对象创建之后,容器会调用此方法     */    public void sessionCreated(HttpSessionEvent arg0) {        // TODO Auto-generated method stub        //获得上下文,先通过session事件对象来获取session        HttpSession session=arg0.getSession();        ServletContext ctx = session.getServletContext();        Integer count = (Integer) ctx.getAttribute("count");        if(count==null){            count=1;        }else{            count++;        }        ctx.setAttribute("count", count);    }    /*     * session对象销毁之后,容器会调用此方法。     */    public void sessionDestroyed(HttpSessionEvent arg0) {        HttpSession session=arg0.getSession();        ServletContext ctx = session.getServletContext();        Integer count = (Integer) ctx.getAttribute("count");        count--;        ctx.setAttribute("count", count);    }}

显示当前在线人数:

<h1>当前在线人数:<%=application.getAttribute("count") %></h1><h2><a href="logout">登出!</a></h2>

登出,销毁session类 LogoutServlet.java

package web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;public class LogoutServlet extends HttpServlet {    public void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        HttpSession session = request.getSession();        session.invalidate();    }}

servlet线程安全问题

为什么说servlet会存在线程安全问题? 容器收到请求会启动一个线程 当有多个线程访问一个方法。

package web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SomeServlet extends HttpServlet {    private int count ;    public void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        count++;        try {            Thread.sleep(2000);            //刻意造成线程安全问题        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        System.out.println(Thread.currentThread().getName()+":"+count);    }}

我访问了好几次。因为设置了sleep。所以访问的线程会sleep。然后是每个访问的线程都对count进行了操作。产生了线程安全问题

容器在默认情况下,只会创建一个servlet实例(对象) 容器收到一个请求,就会启动一个线程来处理 如果有多个请求同时访问某个servlet,就有可能产生线程安全问题 (比如,这些线程要修改servlet的属性)

解决:

可以使用 synchronized对有可能产生线程安全问题的代码块加锁 但是加锁会影响性能

package web;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SomeServlet extends HttpServlet {    private int count ;    public void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        synchronized(this){        count++;        try {            Thread.sleep(2000);            //刻意造成线程安全问题        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        System.out.println(Thread.currentThread().getName()+":"+count);        }    }}

平平淡淡才是真

过滤器、监听器、上下文、servlet线程安全问题

相关文章:

你感兴趣的文章:

标签云: