您的位置:首页 > 其它

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

2016-12-12 09:38 531 查看
就像名字写那样,过滤器可以过滤请求,比如对一些评论进行过滤。又不改原有代码的基础上可以加上一个过滤器,或者是登陆验证。集中在一个过滤器中处理。写一个类实现接口 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、生命周期相关的时间

容器创建或者销毁了requestsessionservlet上下文时产生的事件。

比如说现在有多少人在访问应用,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);
}
}

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: