就像名字写那样,过滤器可以过滤请求,比如对一些评论进行过滤。又不改原有代码的基础上可以加上一个过滤器,或者是登陆验证。集中在一个过滤器中处理。写一个类实现接口 Filter 之后一定要在配置文件中配置!!!监听器可以监听,上下文的概念。
什么是过滤器:
servlet 规范当中定义的一种特殊的组件,用来拦截 servlet 容器的调用过程。
会先调过过滤器的方法,过滤器决定是否向后继续调用就是调用 servlet 容器
容器收到请求之后 通常情况下会调用 servlet 的 service 方法来处理请求。如果有过滤器,则容器先调用过滤器的方法
1、写一个 java 类,实现接口 Filter
2、在 doFilter 方法里面,编写拦截处理逻辑
3、配置过滤器 (web.xml) 让容器知道哪些请求需要拦截
比如写一个评论,然后显示出来。但是说一些敏感字。就不允许其评论
但是已经写完了的话,评论与 后端的 servlet 的话。可以直接加个过滤器
容器只要一启动,就会立即创建过滤对象。只会创建一个。
容器在创建过滤器对象之后会调用该对象的 init 方法。该方法只会执行一次。
容器调用 doFilter 方法来处理请求
FilterChain(过滤器链)
过滤器
- CommentFilterA 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 {
- 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.java 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 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 上下文
容器启动之后,会为每一个 web 应用创建唯一的一个符合 ServletContext 接口要求的对象
特点:
一个 web 应用对应一个唯一的上下文
只要容器没有关闭,并且应用没有被删除,则上下文会一直存在。
如何获得上下文
4 种方式
GenericServlet,ServletConfig,FilterConfig,HttpSession 提供了一个 getServletContext 方法来获得上下文。
作用:
1、绑定数据
- setAttribute,
- getAttribute,
- removeAttribute
注:将数据绑定到上下文上面,可以随时访问。因为他一直在。
转发
request.setAttribute
绑定到 session 上
session.setAttrobute
也可以 ServletContext.setAttribute
绑定的不同上面不同,ServletContext 时间最长,但是占用内存。
所有优先使用保存时间短的即 request
在满足使用条件的情况下,优先使用生命周期短的
- (request<session<上下文)
这个数据是随 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 会存在线程安全问题?
容器收到请求会启动一个线程
当有多个线程访问一个方法。
- 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);
- }
- }
- }
来源: http://www.bubuko.com/infodetail-1868729.html