您的位置:首页 > 编程语言 > Java开发

【JavaWeb-12】Listener监听3个方面、Filter过滤器、全局编码过滤器案例

2016-09-25 20:52 751 查看
1、Listener主要监听3个方面,第一类是监听域对象,一共有3个域对象。

——第一个是ServletContext对象,3个步骤,首先是写一个继承自ServletContextListener的类,然后实现它的创建和销毁方法,最后在web.xml中注册这个类即可。



在xml中注册的语句如下:

<listener>
<listener-class>com.hello.listener.ServletContextListener</listener-class>
</listener>


第一个ServletContext其实就是application对象,所以我们程序启动就能在控制台看到创建的消息,关闭服务器的话就能看到销毁的消息。

——第二个是HttpSession域对象。继承自HttpSessionListener接口实现方法进行注册即可。我们在访问index.jsp的时候其实默认开启了session,所以我们直接访问的话,会有创建session的消息出来,我们在jsp中写如下代码,则会在创建消息出现之后出现销毁消息。

<%
session.invalidate();
%>


——第三个是ServletRequest域对象,继承自ServletRequestListener接口实现方法注册即可。我们直接访问index.jsp一样可以得到实验结果,因为访问静态文件和访问servlet类似都要经过servlet类处理,详细一点就是经过它里面的service处理,service处理结束的时候就相当于这个request被销毁了。所以我们可以得到下列消息。

ServletRequest被销毁了!
HttpSession被创建了!
HttpSession被销毁了!
ServletRequest被创建了!


2、第二类监听的是域对象属性的变化。也是上面那3个域对象,只是这一次继承的接口不一样了,其他用法都是类似的,我们只拿继承自HttpRequestAttributeListener为例。我们在index.jsp中写如下代码,然后访问这个页面。

request.setAttribute("email", "andy@163.com");
request.setAttribute("email", "eric@163.com");
request.removeAttribute("email");


结果如下,另外两个域对象的属性监听方法操作是一样的。

ServletRequest新增了属性!
ServletRequest修改了属性!
ServletRequest移除了属性!


3、第三类是监听session绑定JavaBean。这里与以上两种有所区别。它需要实体类来继承自接口并实现方法,而不是随便创建一个类。其次,它不需要在xml中注册。

public class User implements HttpSessionBindingListener {
private String name;
private String pwd;

……
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("User被绑定了!");

}
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("User被解绑了!");
}

}


然后在index.jsp中写如下代码,即可得到测试消息:

<%
session.removeAttribute("u");
session.invalidate();
%>


4、Filter是重点。Filter其实和Servlet有点类似。我们之前说过Servlet的使用流程是,创建一个Servlet,然后在xml里面配置名字、类和路径(使用MyEclipse的话在图形化界面就配置好了),这样访问什么路径就去找什么Servlet。Filter也是类似的,本质上它是一个继承Filter接口的类,然后我们也需要在xml里面去配置名字、类和路径,这样访问什么路径的话就先到这个Filter类里面来进行处理,如果Filter类放行的话处理完它就会继续奔向Servlet。

——我们看到创建的继承自Filter接口的类如下,主要是用了其中的doFilter方法。



——这是我们在xml中的配置。

<filter>
<filter-name>filterTest</filter-name>
<filter-class>com.hello.filter.FilterTest</filter-class>
</filter>
<filter-mapping>
<filter-name>filterTest</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


——小知识。我们在xml中的配置最好按照顺序来写,比如filter的配置写在servlet的上面,这个顺序的查看是点击
web-app
然后按
F2
,就出现下面这个样子,可以看到顺序。



——如果把doFilter写成如下,我们可以测试执行顺序。是到资源里面去了之后又返回到filter里面了。

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter开始了,然后放行!");
//放行,到servlet中去
chain.doFilter(request, response);
System.out.println("访问servlet或资源后,又返回到doFilter了!");
}


结果是:

doFilter开始了,然后放行!
抵达Servlet了!
访问servlet或资源后,又返回到doFilter了!


——FilterChain只需要满足一个条件:对同一个资源进行过滤就行。至于哪一个filter先执行,是看xml里面哪一个filter-mapping写在上面。然后最后一个过滤器调用资源。

5、Filter生命周期。当服务器启动的时候就开始实例化,然后紧接着调动init方法初始化(只调用1次,这一点时间和Servlet不同,后者是第一次访问servlet的时候进行实例化和初始化,而前者是服务器启动就开始了),当访问资源的时候路径匹配到Filter的时候就调用doFilter方法,服务器关闭的时候调用destroy方法。

6、FilterConfig。在xml中配置,然后利用filterConfig获取这个参数值。

<filter>
<filter-name>filterTest</filter-name>
<filter-class>com.hello.filter.FilterTest</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filterTest</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


然后在filter里面获取,设置给request,再传递到servlet中,这样在servlet中就不需要配置编码了,因为这个request已经配置过了。

public class FilterTest implements Filter {

private FilterConfig filterConfig;

public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig=filterConfig;
}

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding(filterConfig.getInitParameter("encoding"));
chain.doFilter(request, response);
}
}


7、Filter配置,主要讲的是url-pattern的配置。我们之前说servlet的url-pattern时说过匹配原则,完全匹配的优先,然后又路径的其次,最后是通配符的。这里同样适用。

——但是这里多了一些配置,比如我们可以直接把url-pattern换成servletname,那就变成了对制定的servlet进行拦截过滤,而不是通过间接的路径进行匹配了,这叫精准打击么?

<filter-mapping>
<filter-name>filterTest</filter-name>
<servlet-name>DoFilterServletTest</servlet-name>
</filter-mapping>


——还有一些情况,比如,我在一个servlet里面转发到了另一个servlet,这个时候,第二个servlet可能虽然满足了url-pattern里面的规则,但是并不会被过滤,为什么呢?因为我们的
<filter-mapping>
里有一个
dispatcher
,它的默认值是REQUEST。意味着如果我们是以request的方式访问这个资源的话才会被filter拦截,而如果是从其他地方转发过来访问这个资源的话,尽管匹配上了,但是filter不拦截,需要我们把
dispatcher
的值设置成FORWARD。但是,如果设置成FORWARD的话,REQUEST访问又不起作用了,所以我们可以设置多个。



<filter-mapping>
<filter-name>filterTest</filter-name>
<servlet-name>DoFilterServletTest</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>


8、利用Filter实现自动登录的案例。如果用户勾选了自动登录保存7天的话,我们把用户名密码保存在cookie中。然后我们可以在Filter里面进行拦截,拦截的时候先判断如果不是直接访问登录页面或者登录的servlet的话就查看是否有cookie,如果有cookie取得用户名密码,比较数据库后,放行。这里面其实是用了cookie、session的知识。

9、md5(message digest 5)加密。

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String raw_pwd="123456789";
try {
MessageDigest md=MessageDigest.getInstance("md5");
BASE64Encoder b64c=new BASE64Encoder();
String new_pwd=b64c.encode(md.digest(raw_pwd.getBytes("UTF-8")));
System.out.println(new_pwd);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}


10、利用Filter全局编码的案例。我们可能对每一个servlet都要设置request的编码,这种编码可以在Filter里面进行,然后把这个修改过的request放行。但是这种方式只能对post有效,如果是get方式提交的表单里面有中文的话,就会出现乱码。所以我们可以利用装饰器模式来对request进行修改。

——步骤是先定义一个类,和被包装类继承一样的类或者接口。然后在装饰类中持有一个被装饰类的引用,也就是把被包装类当做参数传递给我们新类的构造函数。最后把需要修改的函数重写,不需要的直接调用返回。

——但是这里面有个问题就是,request实现接口的话有很多方法,我们只需要重写一个,其他的我们都需要一个个的调用直接返回很累。所以我们可以让我们的这个类继承自一个适配器,因为适配器不强制实现所有的方法,所以我们可以只选那么我们需要重写的就行(主要是getParameter之类的方法)。

——之前的做法是获取到一个get表单里面的值,如果是中文的话,用以下处理即可。

String username=request.getParameter("username");
username=new String(username.getBytes("iso-8859-1"),"UTF-8");
System.out.println(username);


——我们重写了request的getParameterMap方法,把里面的每个值都转码了,然后getParameter和getParameterValues都是调用的getParameterMap方法。

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest) request;
MyRequest re=new MyRequest(req);
chain.doFilter(re, response);
}


class MyRequest extends HttpServletRequestWrapper{
HttpServletRequest request;
public MyRequest(HttpServletRequest request){
super(request);
this.request=request;
}
@Override
public String getParameter(String name) {
Map<String,String[]> map=getParameterMap();
return map.get(name)[0];
}
@Override
public Map getParameterMap() {
Map<String,String[]> map=request.getParameterMap();
boolean flag=true;
if(flag){
for(Map.Entry<String, String[]> m:map.entrySet()){
String[] values=m.getValue();
for(int i=0;i<values.length;i++){
try {
values[i]=new String(values[i].getBytes("iso-8859-1"),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
flag=false;
}
return map;
}
@Override
public String[] getParameterValues(String name) {
Map<String,String[]> map=getParameterMap();
return map.get(name);
}
}


备注:如果仅仅是针对post请求的话,还是比较简单的,就直接在doFilter里面写一两句set的代码即可。但是要处理get请求的话,需要重写getParameter方法。

源代码:JavaEE Filter全局编码过滤器
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  filter 编码