Listener监听器和Filter过滤器

Listener监听器

WEB中的监听器

WEB 中的 Listener 和 Filter 是属于 Servlet 规范中的高级的技术.
WEB中的监听器共有三类八种(监听三个域对象)
* 事件源:Servlet 中的三个域对象.ServletContext,HttpSession,ServletRequest.
* 监听器:自定义类实现8个接口.
* 事件源和监听器的绑定:配置.

监听器的分类

三类八种:
* 一类:监听三个域对象的创建和销毁的监听器
* ServletContextListener
* HttpSessionListener
* ServletRequestListener
* 二类:监听三个域对象的属性变更的监听器(属性添加,移除,替换)
* ServletContextAttributeListener
* HttpSessionAttributeListener
* ServletRequestAttributeListener
* 三类:监听HttpSession中的 JavaBean 的状态改变(绑定,解除绑定,钝化,活化)
* HttpSessionBindingListener
* HttpSessionActivationListener

WEB中的监听器的使用

编写一个类实现监听器的接口;
通过配置文件配置监听器;

一类:监听三个域对象的创建和销毁的监听器

ServletContextListener:监听ServletContext对象的创建和销毁

方法

void contextDestroyed(ServletContextEvent sce)
          Notification that the servlet context is about to be shut down.
 void contextInitialized(ServletContextEvent sce)
          Notification that the web application initialization process is starting.

ServletContext 对象的创建和销毁

创建:服务器启动的时候,服务器为每个WEB应用创建一个属于该web项目的对象ServletContext.
销毁:服务器关闭或者项目从服务器中移除的时候.

应用

public class MyServletContextListener implements ServletContextListener{
  @Override
  public void contextInitialized(ServletContextEvent sce) {
    System.out.println("ServletContext对象被创建了...");
  }
  @Override
  public void contextDestroyed(ServletContextEvent sce) {
    System.out.println("ServletContext对象被销毁了...");
  }
}
配置:
<!-- 配置监听器 -->
<listener>
  <listener-class>com.boomoom.weblistener.MyServletContextListener</listener-class>
</listener>

企业中的应用

1、加载框架的配置文件: Spring框架 ContextLoaderListener
2、定时任务调度:java.util.Timer,java.util.TimerTask

Timer的方法
 void schedule(TimerTask task, Date firstTime, long period)
          安排指定的任务在指定的时间开始进行重复的固定延迟执行。
 void schedule(TimerTask task, long delay)
          安排在指定延迟后执行指定的任务。
 void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
          安排指定的任务在指定的时间开始进行重复的固定速率执行。
 void scheduleAtFixedRate(TimerTask task, long delay, long period)
          安排指定的任务在指定的延迟后开始进行重复的固定速率执行。

ServletContextListener执行任务调度

public class MyServletContextListener implements ServletContextListener{

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext对象被创建了...");
        Timer timer = new Timer();
        /*Calendar calendar = Calendar.getInstance();
        calendar.set(2016,3,11,10,18,00);
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("发送邮件:");
            }
        }, calendar.getTime(), 5000);*/
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("发送邮件:");
            }
        }, 10000, 2000);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext对象被销毁了...");
    }
}

HttpSessionListener:监听HttpSession的创建和销毁的监听器

方法

 void sessionCreated(HttpSessionEvent se)
          Notification that a session was created.
 void sessionDestroyed(HttpSessionEvent se)
          Notification that a session is about to be invalidated.

HttpSession何时创建和销毁

* 创建:服务器端第一次调用getSession();
* 销毁:
* 非正常关闭服务器(正常关闭session会序列化):
* session过期了默认30分钟.
* 手动调用session.invalidate();

HttpSession的问题

* 访问Servlet会不会创建Session : 不会
* 访问JSP会不会创建Session :会. (Session作为JSP的内置对象,调用了getSession)
* 访问html会不会创建Session :不会

应用

public class MyHttpSessionListener implements HttpSessionListener{

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("HttpSession被创建了...");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("HttpSession被销毁了...");
    }

}
配置:
  <listener>
    <listener-class>com.cnblogs.weblistener.MyHttpSessionListener</listener-class>
  </listener>

企业中的应用:统计在线人数

public class SessionLinstener implements HttpSessionLinstener {
    public void sessionCreated(HttpSessionEvent se) {
        ServletContext sc = se.getSession().getServletContext();
        Integer num = (Integer) sc.getAttribute("peopleOnline");
        if (null == num) {
            sc.setAttribute("peopleOnline",1);
        } else {
            num++;
            sc.setAttribute("peopleOnline",num);
        }
    }

    public void sessionCreated(HttpSessionEvent se) {
        try {
            ServletContext sc = se.getSession().getServletContext();
            Integer num = (Integer) sc.getAttribute("peopleOnline");
            num--;
            sc.setAttribute("peopleOnline",num);
        } catch (Exception e) {
        // TODO: handle exception
        }
    }
}

ServletRequestListener:监听ServletRequest对象的创建和销毁的监听器

方法

 void requestDestroyed(ServletRequestEvent sre)
          The request is about to go out of scope of the web application.
 void requestInitialized(ServletRequestEvent sre)
          The request is about to come into scope of the web application.

request对象何时创建和销毁

* 创建:客户端向服务器发送一次请求,服务器就会创建request对象.
* 销毁:服务器对这次请求作出响应后就会销毁request对象.

request的问题

访问一个Servlet会不会创建request对象:会
访问一个JSP会不会创建request对象:会
访问一个HTML会不会创建request对象:会

应用

public class MyServletRequestListener implements ServletRequestListener{
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("ServletRequest被销毁了...");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("ServletRequest被创建了...");
    }
}
配置:
  <listener>
    <listener-class>com.cnblogs.weblistener.MyServletRequestListener</listener-class>
  </listener>

企业中监听器的应用

在线人数,在线用户数,访问量,及数据持久化

 JavaWeb-Servlet技术的监听器-解析与实例-网站在线用户信息与网页点击量

二类:监听三个域对象的属性变更的监听器:(属性添加,移除,替换)

ServletContextAttributeListener:监听ServletContext对象的属性变更

Method Summary
 void attributeAdded(ServletContextAttributeEvent scab)
          Notification that a new attribute was added to the servlet context.
 void attributeRemoved(ServletContextAttributeEvent scab)
          Notification that an existing attribute has been removed from the servlet context.
 void attributeReplaced(ServletContextAttributeEvent scab)
          Notification that an attribute on the servlet context has been replaced.

HttpSessionAttributeListener:监听HttpSession中的属性变更

Method Summary
 void attributeAdded(HttpSessionBindingEvent se)
          Notification that an attribute has been added to a session.
 void attributeRemoved(HttpSessionBindingEvent se)
          Notification that an attribute has been removed from a session.
 void attributeReplaced(HttpSessionBindingEvent se)
          Notification that an attribute has been replaced in a session.

ServletRequestAttributeListener:监听ServletRequest对象的属性变更

Method Summary
 void attributeAdded(ServletRequestAttributeEvent srae)
          Notification that a new attribute was added to the servlet request.
 void attributeRemoved(ServletRequestAttributeEvent srae)
          Notification that an existing attribute has been removed from the servlet request.
 void attributeReplaced(ServletRequestAttributeEvent srae)
          Notification that an attribute was replaced on the servlet request.

三类:监听HttpSession中的JavaBean的对象的状态改变的监听器

第三类监听器很特殊,不需要进行配置的.作用在JavaBean上的监听器.JavaBean可以自己感知到在Session中的状态.

HttpSessionBindingListener:监听HttpSession中的JavaBean的绑定和解除绑定

Method Summary
 void valueBound(HttpSessionBindingEvent event)
          Notifies the object that it is being bound to a session and identifies the session.
 void valueUnbound(HttpSessionBindingEvent event)
          Notifies the object that it is being unbound from a session and identifies the session.

HttpSessionActivationListener:监听HttpSession中的JavaBean的钝化和活化

Method Summary
 void sessionDidActivate(HttpSessionEvent se) 活化(反序列化)
          Notification that the session has just been activated.
 void sessionWillPassivate(HttpSessionEvent se) 钝化(序列化到硬盘)
          Notification that the session is about to be passivated.

优化Session:
通过配置<Context>标签配置定时session序列化.
在tomcat/conf/context.xml中配置<Context>:
    在tomcat中所有的虚拟主机和虚拟路径都会按照这个配置执行.
在tomcat/conf/Catalina/localhost/context.xml配置<Context>:
    在tomcat中的localhost虚拟主机中的所有虚拟路径按照这个配置执行.
在当前的工程下的META-INF/context.xml配置<Context>:
    当前这个工程按照配置执行.

当前的工程下的META-INF/context.xml的代码:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- 
    maxIdleSwap:1  1分钟session会自动序列化到硬盘.
    directory:cnblogs  序列化后存放的位置.( tomcat/conf/Catalina/localhost/工程名/cnblogs)
 -->
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
    <Store className="org.apache.catalina.session.FileStore" directory="cnblogs"/>
</Manager>
</Context>

Filter过滤器

过滤器实现自动登录

Cookie技术

利用Cookie记住用户的用户名和密码,设置号有效时间,用户访问页面的时候带入.

Filter:过滤器的概述

过滤器Filter:可以过滤从客户端向服务器发送的请求.

主要方法

void destroy()
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
void init(FilterConfig filterConfig)

使用过滤器

* 编写一个类实现Filter接口;
* 配置过滤器;

过滤器的生命周期

过滤器的创建和销毁:
* 创建:服务器启动的时候。
* 销毁:服务器关闭的时候。

(1) 加载和实例化 
Web 容器启动时,即会根据 web.xml 中声明的 filter 顺序依次实例化这些 filter。 

(2) 初始化 
Web 容器调用 init(FilterConfig) 来初始化过滤器。容器在调用该方法时,向过滤器传递 FilterConfig 对象,FilterConfig 的用法和 ServletConfig 类似。利用 FilterConfig 对象可以得到 ServletContext 对象,以及在 web.xml 中配置的过滤器的初始化参数。在这个方法中,可以抛出 ServletException 异常,通知容器该过滤器不能正常工作。此时的 Web 容器启动失败,整个应用程序不能够被访问。实例化和初始化的操作只会在容器启动时执行,而且只会执行一次。 

(3) doFilter 
doFilter 方法类似于 Servlet 接口的 service 方法。当客户端请求目标资源的时候,容器会筛选出符合 filter-mapping 中的 url-pattern 的 filter,并按照声明 filter-mapping 的顺序依次调用这些 filter 的 doFilter 方法。在这个链式调用过程中,可以调用 chain.doFilter(ServletRequest, ServletResponse) 将请求传给下一个过滤器(或目标资源),也可以直接向客户端返回响应信息,或者利用 RequestDispatcher 的 forward 和 include 方法,以及 HttpServletResponse 的 sendRedirect 方法将请求转向到其它资源。需要注意的是,这个方法的请求和响应参数的类型是 ServletRequest 和 ServletResponse,也就是说,过滤器的使用并不依赖于具体的协议。 

(4) 销毁 
Web 容器调用 destroy 方法指示过滤器的生命周期结束。在这个方法中,可以释放过滤器使用的资源。 
Filter生命周期

FilterConfig:过滤器的配置对象

Method Summary
 String getFilterName()
          Returns the filter-name of this filter as defined in the deployment descriptor.
 String getInitParameter(String name)
          Returns a String containing the value of the named initialization parameter, or null if the parameter does not exist.
 Enumeration getInitParameterNames()
          Returns the names of the filter's initialization parameters as an Enumeration of String objects, or an empty Enumeration if the filter has no initialization parameters.
 ServletContext getServletContext()
          Returns a reference to the ServletContext in which the caller is executing.

public void init(FilterConfig filterConfig) throws ServletException {
    // 获得当前的Filter的名称:
    String filterName = filterConfig.getFilterName();
    System.out.println(filterName);
    // 获得初始化参数:
    String username = filterConfig.getInitParameter("username");
    String password = filterConfig.getInitParameter("password");
    System.out.println(username+"   "+password);
    // 获得所有的初始化参数的名称:
    Enumeration<String> en = filterConfig.getInitParameterNames();
    while(en.hasMoreElements()){
        String name = en.nextElement();
        String value = filterConfig.getInitParameter(name);
        System.out.println(name+"    "+value);
    }
}

FilterChain:过滤器链

过滤器链中的过滤器的执行的顺序与<filter-mapping>的配置顺序有关.
* doFilter(request,response); -- 放行,放行到下一个过滤器中,如果没有下一个过滤器,到达目标资源.

Filter相关的配置

<url-pattern>的配置:
* 完全路径匹配 :以 / 开始 /demo4/demo1.jsp
* 目录匹配 :以 / 开始 以 * 结束. /* /demo1/*
* 扩展名匹配 :不能以 / 开始 以 * 开始. *.do *.action

<servlet-name>的配置:根据Servlet的名称拦截Servlet.     //替代<url-patten>的标签

<dispatcher>的配置:
* REQUEST :默认值.
* FORWARD :转发.
* INCLUDE :包含.
* ERROR :错误页面跳转.(全局错误页面)

在servlet中new Service() 和在service中 new Dao() 这种方式的替换。

自动登录代码实现

自动登录:访问index.jsp,点击上面的登录,跳到login.jsp,填写表单,提交到UserServlet。UserServlet中的处理,完成登录信息在cookie和session中的保存,再转发到index.jsp。index.jsp中,直接获取session域中的用户,显示用户欢迎等的登录状态信息。  用户关掉浏览器再访问index.jsp时,带这cookie去的,这个时候拦截器,取出cookie中的信息,验证后,存入session中,放行到index.jsp,index.jsp中的session域对象就有了登录信息了,就完成了自动登陆。

package com.boomoom.autologin.web.servlet;

import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import com.boomoom.autologin.domain.User;
import com.boomoom.autologin.service.UserService;

public class UserServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         * 1.接收参数:
         * 2.封装数据:
         * 3.调用业务层处理数据:
         * 4.页面跳转:
         */
        // 接收参数:
        try{
            Map<String,String[]> map = req.getParameterMap();
            // 封装数据:
            User user = new User();
            BeanUtils.populate(user, map);
            // 调用业务层:
            UserService userService = new UserService();
            User existUser = userService.login(user);
            // 跳转页面:
            if(existUser == null){
                // 登录失败:
                req.setAttribute("msg", "用户名或密码错误!");
                req.getRequestDispatcher("/demo5/login.jsp").forward(req, resp);
            }else{
                // 登录成功:
                // 记住用户名和密码:    //在jsp页面中定义: <input type="checkbox" name="autoLogin" value="true"> 自动登录
                String autoLogin = req.getParameter("autoLogin");
                if("true".equals(autoLogin)){
                    // 自动登录的复选框已经勾选.
                    Cookie cookie = new Cookie("autoLogin",existUser.getUsername()+"#"+existUser.getPassword());
                    cookie.setPath("/day16");
                    cookie.setMaxAge(60 * 60 * 24 * 7);
                    resp.addCookie(cookie);
                }
                // 使用Session记录用户信息.
                req.getSession().setAttribute("existUser", existUser);
                resp.sendRedirect(req.getContextPath()+"/demo5/index.jsp");
            }
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
UserServlet
package com.boomoom.autologin.web.filter;

import java.io.IOException;
import java.sql.SQLException;
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.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.boomoom.autologin.domain.User;
import com.boomoom.autologin.service.UserService;
import com.boomoom.utils.CookieUtils;

/**
 * 自动登录的过滤器的实现
 * @author admin
 *
 */
public class AutoLoginFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        /**
         * 判断session中是否有用户的信息:
         * * session中如果有:放行.
         * * session中没有:
         *    * 从Cookie中获取:
         *        * Cookie中没有:放行.
         *        * Cookie中有:
         *            * 获取Cookie中存的用户名和密码到数据库查询.
         *                * 没有查询到:放行.
         *                * 查询到:将用户信息存入到session . 放行.
         */
        // 判断session中是否有用户的信息:
        HttpServletRequest req = (HttpServletRequest) request;
        HttpSession session = req.getSession();
        User existUser = (User) session.getAttribute("existUser");
        if(existUser != null){
            // session中有用户信息.
            chain.doFilter(req, response);
        }else{
            // session中没有用户信息.
            // 获得Cookie的数据:
            Cookie[] cookies = req.getCookies();
            Cookie cookie = CookieUtils.findCookie(cookies, "autoLogin");
            // 判断Cookie中有没有信息:
            if(cookie == null){
                // 没有携带Cookie的信息过来:
                chain.doFilter(req, response);
            }else{
                // 带着Cookie信息过来.
                String value = cookie.getValue();// aaa#111
                // 获得用户名和密码:
                String username = value.split("#")[0];
                String password = value.split("#")[1];
                // 去数据库进行查询:
                User user = new User();
                user.setUsername(username);
                user.setPassword(password);
                UserService userService = new UserService();
                try {
                    existUser = userService.login(user);
                    if(existUser == null){
                        // 用户名或密码错误:Cookie被篡改的.
                        chain.doFilter(req, response);
                    }else{
                        // 将用户存到session中,放行
                        session.setAttribute("existUser", existUser);
                        chain.doFilter(req, response);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void destroy() {
    }
}
AutoLoginFilter
<servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>com.cnblogs.autologin.web.servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/UserServlet</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>AutoLoginFilter</filter-name>
    <filter-class>com.cnblogs.autologin.web.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>AutoLoginFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
web.xml

在对过滤的要求较高的时候,cookie == null 和 user == null 的判断完成,不能简单的放行,而是要对请求的资源做判断再放行。静态资源放行,其他的请求转去登录页。

String uri = req.getReqestURI();
if (uri.endsWith("login.jsp")||uri.endsWith("loginServlet")||uri.endsWith("jpg")||uri.endsWith("png")||uri.endsWith("css")||uri.endsWith("js")||uri.endsWith("jhtml")) {
    chain.doFilter(req, response);
} else {
    req.getRequestDispatcher("/demo/login.jsp").forward(request, response);
}
cookie和user为null的时候,严谨的拦截方式

通用的字符集编码的过滤器

通常会提交带有中文的数据,GET/POST请求都有可能提交中文数据.通常情况下在Servlet中处理中文乱码.现在将乱码的处理交给过滤器完成.只需要在Servlet中关心参数的接收就可以了.只需要在Servlet中调用request.getParameter();接收参数就可以,而不去关心到底get/post如何处理乱码.

增强request中的getParameter方法

继承 :控制这个类构造.
装饰者模式 :增强的类和被增强类实现相同的接口,增强的类中获得到被增强的类的引用.缺点:接口中方法太多.
动态代理 :被增强的类实现接口就可以.

1、采用装饰者模式,这里的HttpServletRequestWrapper是一个模板类,继承这个类可以避免对HttpServletRequest接口的全部方法的实现。

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper{

    private HttpServletRequest request;

    public MyHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    @Override
    public String getParameter(String name) {
        // 根据请求方式不同,去处理:
        // 获得请求方式:
        String method = request.getMethod();
        if("get".equalsIgnoreCase(method)){
            String value = null;
            try {
                value = new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return value;
        }else if("post".equalsIgnoreCase(method)){
            try {
                request.setCharacterEncoding("UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return super.getParameter(name);
    }

    
}
MyHttpServletRequestWrapper
package com.boomoom.encoding.filter;

import java.io.IOException;
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;

public class GenericEncodingFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        MyHttpServletRequestWrapper myReq = new MyHttpServletRequestWrapper(req);
        chain.doFilter(myReq, response);
    }

    @Override
    public void destroy() {
    }

}
GenericEncodingFilter
<filter>
    <filter-name>GenericEncodingFilter</filter-name>
    <filter-class>com.cnblogs.encoding.filter.GenericEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>GenericEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping> 
web.xml

 在HttpServletRequestWrapper的getParameter方法的返回值处,还能做一个敏感词过滤的处理。

2、采用动态代理

补充:动态带的demo

package com.boomoom.b_proxy;
/**
 * 服务员的接口.
 * @author admin
 *
 */
public interface Waiter {

    public void server();
    
    public String sayHello(String name);
}
Waiter
package com.boomoom.b_proxy;
/**
 * 服务器的实现类
 * @author admin
 *
 */
public class Waitress implements Waiter {

    @Override
    public void server() {
        System.out.println("服务中...");
    }

    @Override
    public String sayHello(String name) {
        return "Hello "+name;
    }

}
Waitress
package com.boomoom.b_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.junit.Test;

public class ProxyDemo1 {

    @Test
    public void demo1(){
        Waiter waiter = new Waitress();
        // waiter.server();
        
        // 使用动态代理:Proxy.newProxyInstance();
        /**
         * ClassLoader :类加载器.
         * Class[]     :被增强的对象实现的所有接口
         * InvocationHandler    :处理类.
         */
        // 第一个参数:
        ClassLoader classLoader = waiter.getClass().getClassLoader();
        // 第二个参数:
        Class[] interfaces = waiter.getClass().getInterfaces();
        // 第三个参数:
        
        Waiter waiter2 = (Waiter)Proxy.newProxyInstance(classLoader, interfaces, new MyInvocationHandler(waiter));
        waiter2.server();
        // 说明现在调用代理对象的任何方法的时候,都是相当于调用invoke,InvocationHandler中的invoke都会执行.
        String s = waiter2.sayHello("James");
        System.out.println(s);
    }
}

class MyInvocationHandler implements InvocationHandler{

    private Waiter waiter;

    public MyInvocationHandler(Waiter waiter){
        this.waiter = waiter;
    }
    
    @Override
    /**
     * 方法的参数:
     * * proxy    :产生的代理对象.
     * * method    :当前正在调用的目标类的方法.
     * * params    :正在执行的方法中的参数.
     */
    public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
        // System.out.println("InvocationHandler invoke 执行了...");
        // System.out.println("微笑=========");
        // waiter.sayHello("James");
        // System.out.println(method.getName());
        if("server".equals(method.getName())){
            System.out.println("微笑==========");
            Object obj = method.invoke(waiter, params);
            System.out.println("bye==========");
            return obj;
        }else{
            return method.invoke(waiter, params);
        }
    }
}
ProxyDemo1

  实现:采用注解,不用配置web.xml,主要是在GennericEncodingFilter的方法上做了修改:

package com.boomoom.encoding;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter(urlPatterns="/*")
public class GenericEncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        final HttpServletRequest req = (HttpServletRequest) request;
        // 增强req:
        HttpServletRequest myReq = (HttpServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(),
                req.getClass().getInterfaces(), new InvocationHandler() {
                    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 判断执行的方法是否是getParameter:
                        if("getParameter".equals(method.getName())){
                            // 调用的是getParameter:需要增强这个方法.
                            // 判断请求方式是GET还是POST:
                            String type = req.getMethod();
                            if("get".equalsIgnoreCase(type)){
                                String value = (String) method.invoke(req, args);
                                value = new String(value.getBytes("ISO-8859-1"),"UTF-8");
                                return value;
                            }else if("post".equalsIgnoreCase(type)){
                                req.setCharacterEncoding("UTF-8");
                            }
                        }
                        return method.invoke(req, args);
                    }
                });
        
        chain.doFilter(myReq, response);
    }

    @Override
    public void destroy() {

    }

}
GenericEncodingFilter
原文地址:https://www.cnblogs.com/boomoom/p/10140544.html