JavaWeb之过滤器

Posted WWWYC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaWeb之过滤器相关的知识,希望对你有一定的参考价值。

 

时间:

 

Talk is cheap  Show me the code 


JavaWeb三大组件:
    Servlet、Listener、Filter
    都需要在web.xml中进行配置,Listener中的两个感知监听器不需要配置。


——过滤器概述

    过滤器是JavaWeb的三大组件之一,它与Servlet很相似,不过过滤器是用来拦截请求的,而不是处理请求的。

    当用户请求某个Servlet或其他资源(JSP、css、html)时,会先执行部署在这个请求上的Filter,如果Filter“放行”,那么会继续执行用户请求的资源,如果Filter“不放行”,那么就不会执行用户请求的资源。

    其实可以这么理解,当用户请求某个Servlet时,Tomcat会去执行注册在这个请求上的Filter,然后是否“放行”由Filter来决定,可以理解为,Filter来决定是否调用Servlet,当执行完成Servlet的代码后,还会执行FilterChain中doFilter()方法后面的代码。


——编写一个过滤器

Filter是单例的。

1、编写一个类,并且实现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;
 
/**
 * 编写过滤器
 *  1、写一个类,并且实现Filter接口
 *  2、在web.xml中进行相关配置
 * 
 * @author 31067
 * 
 */
public class AFilter implements Filter {
    /**
     * 在销毁之前执行,用来对非内存资源进行释放
     */
    @Override
    public void destroy() {
        System.out.println("销毁");
    }
 
    /**
     * 每次过滤时都会执行
     */
    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException {
        System.out.println("拦截");
        chain.doFilter(request, response);
        System.out.println("放行了");
    }

    /**
     * 在Filter创建后马上调用,用来做初始化操作
     */
    @Override
    public void init(FilterConfig arg0) throws ServletException {
        System.out.println("出生");
    }
}

2、在web.xml文件中进行配置

    <filter>
        <filter-name>xxx</filter-name>
        <filter-class>com.wyc.web.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>xxx</filter-name>
        <url-pattern>/*</url-pattern>    //通常会使用通配符进行url-pattern的配置  /web/*
    </filter-mapping> 



——Filter的生命周期

1、Filter接口生命周期方法:

    *   void init(FilterConfig config)
        在服务器启动时会创建Filter的实例对象,并且每个类型的Filter只会创建一个实例对象,也就是说Filter是单例的。
        在创建完Filter实例后,会马上调用init()方法完成初始化操作,这个方法只会被执行一次。

    *   void destory()
        服务器会在创建Filter对象之后,把Filter放到内存中一直使用,通常不会销毁它,一般会在服务器关闭时销毁Filter对象。
        在销毁Filter对象之前,服务器会调用Filter对象的destory()方法。

    *   void doFilter(ServletRequest , ServletResponse , FilterChain)
        这个方法会在用户每次访问“目标资源<url-pattern>/index.jsp</url-pattern>”时执行。
        如果需要“放行”,那么需要调用FilterChain对象的doFilter(ServletRequest, ServletResponse)方法,如果不调用该方法,则无法请求目标资源。 



2、生命周期方法中的参数

    FilterConfig:
        *   获取初始化参数:getInitParameter();
        *   获取所有初始化参数的名称:Enumeration getInitParameterNames()。
        *   获取过滤器名称:getFilterName();
        *   获取application:getServletContext();(获取当前上下文对象)

    FilterChain:
        *   doFilter(ServletRequest, ServletResponse)
            执行“放行”功能,使请求可以访问到资源。
            相当于调用了请求Servlet的Service方法。
            如果当前过滤器是最后一个过滤器,那么调用chain.doFilter()方法表示执行目标资源,如果不是最后一个过滤器,那么chain.doFilter()表示执行下一个过滤器的doFilter()方法。

    面试:Filter接口的doFilter()方法和FilterChain的doFilter()方法有什么区别?




——过滤器的四种拦截方式 

拦截方式:
    *   请求:DISPATCHER
    *   转发:FORWARD
    *   包含:INCLUDE
    *   错误:ERROR

    如果不写,则默认<dispatcher>REQUEST</dispatcher>,如果只写一种,则默认拦截方式就不存在了。

拦截方式需要在<filter-mapping>中进行配置:

    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>    // 默认拦截方式
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>


——过滤器的执行顺序


<filter-mapping>中的配置执行顺序决定了过滤器的执行顺序。

    先配置先执行。
    XML配置如下:
    <web-app>

        <filter>
            <filter-name>AFilter</filter-name>
            <filter-class>com.wyc.web.filter.AFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>AFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
 
        <filter>
            <filter-name>BFilter</filter-name>
            <filter-class>com.wyc.web.filter.BFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>BFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app> 


    执行效果如下:(与栈相似,当chain的doFilter()方法执行完毕后,还会继续执行剩余代码)
        AFilter...start

        BFilter...start
        index.jsp
        BFilter...end
        AFilter...end


——Filter的应用场景

1、执行目标资源之前做预处理工作,例如设置编码,这种操作通常都会放行,只是在目标资源执行之前做一些准备工作。
    几乎在所有的Servlet中都需要写:request.setCharacterEncoding();,这就可以把代码放到过滤器中。

2、通过条件判断是否放行,例如校验当前用户是否已经登录,或者用户IP是否被禁用。

3、在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理。
    也称为回程拦截。


——Filter设置目标资源

在web.xml文件中部署Filter时,可以通过“*”来执行目标资源:
    <filter-mapping>
        <filter-name>myfilter</filter-name>
        <url-pattern>/*</url-pattern>    // 表示过滤所有资源
    </filter-mapping>

这一特性与Servlet完全相同,通过这一特性,可以在用户访问敏感资源时,执行过滤器,例如:<url-pattern>/admin/*</url-pattern>,可以把所有管理员才能访问的资源放到/admin路径下,这时就可以通过过滤器来校验用户身份了。

还可以为<filter-mapping>指定目标资源为某个Servlet,例如:

    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.wyc.servlet.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>
 
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.wyc.filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilger</filter-name>
        <servlet-name>MyServlet</servlet-name>    // 访问指定的Servlet
    </filter-mapping>

    当用户访问:localhost:8080/FilterDemo/MyServlet时,会执行名称为MyServlet的Servlet,这时会执行绑定的过滤器。 


    <filter-mapping>中可以写以下四种标签:
        <filter-name>
        <url-pattern>
        <dispatcher>
        <servlet-name>

——Filter小结

1、Filter接口的三个方法:
    *   void init(FilterConfig config)
        在Tomcat启动时被调用。

    *   void destory()
        在Tomcat关闭时被调用。

    *   void doFilter(ServletRequest, ServletResponse, FilterChain)
        每次请求时都会调用该方法。


2、FilterConfig类
    与ServletConfig相似,用来获取Filter的初始化参数。
    *   ServletContext getServletContext()
        获取Servlet上下文对象。

    *   String getFilterName()
        获取Filter的配置名称。

    *   String getInitParameter(String name)
        获取Filter的初始化参数,与<init-param>元素对应。

    *   Enumeration getInitParameterNames()
        获取所有初始化参数的名称。

3、FilterChain类
    *   void doFilter(ServletRequest, ServletResponse)
        “放行”方法,表示执行下一个过滤器,或者执行目标资源。可以在调用FilterChain的doFilter()方法前后添加语句。在FilterChain的doFilter()方法之前的语句会在目标资源执行前执行,在FilterChain的doFilter()方法之后的语句会在目标资源执行后执行。


4、四中拦截方式:
    REQUEST:拦截直接请求方式
    FORWARD:拦截请求转发方式
    INCLUDE:拦截请求包含方式
    ERROR:拦截错误转发方式

    默认是REQUEST。

——分IP统计网站的访问次数

说明:网站统计每个IP地址访问本网站的次数。

分析:
    因为一个网站可能有多个页面,无论哪个页面被访问,都要统计访问次数,所以使用过滤器最为方便。
    因为需要分IP进行统计,所以可以在过滤器中创建一个Map,使用IP作为key,访问次数为value。当有用户访问时,获取请求的IP,如果IP在Map中存在,说明以前访问过,那么在访问次数上+1即可,如果IP在Map中不存在,那么设置value为1。

    整个网站只需要一个Map即可。
    Map什么时候创建,将Map保存到那里?
        >   Map需要在Filter中使用,也要在页面中使用,所以保存到ServletContext中(application)
        >   可以放到ServletContextListener监听器中,在服务器启动时完成创建,并保存到ServletContext中。

代码:

监听器:


public class IPListener implements ServletContextListener {
 
    /**
     * 在服务器启动时创建Map,保存到ServletContext中
     */
    public void contextInitialized(ServletContextEvent sce) {
        // 创建Map
        Map<String, Integer> map = new LinkedHashMap<String, Integer>();
 
        // 得到ServletContext
        ServletContext sc = sce.getServletContext();
 
        // 将map保存到ServletContext中
        sc.setAttribute("ipMaps", map);
    }
 
    public void contextDestroyed(ServletContextEvent arg0) {
 
    }
}

过滤器:

/**
 * 从ServletContext中获取Map对象
 * 从request中得到请求客户端的IP
 * 进行统计工作,将结果保存到Map中
 * 
 * @author 31067
 * 
 */
public class IPFilter implements Filter {
    private FilterConfig config;
    public void destroy() {
 
    }
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        /*
         * 1、得到ServletContext中的Map
         * 2、从request中获取当前客户端的IP地址
         * 3、查看Map中是否存在这个IP对应的访问次数,如果存在,把次数+1再保存
         * 4、如果IP不存在,那么说明是第一次访问,设置访问次数为1
         */
 
        // 1、得到ServletContext
        ServletContext app = this.config.getServletContext();
        // 得到Map
        Map<String, Integer> map = (Map<String, Integer>) app.getAttribute("ipMaps");
 
        /*
         * 2、获取客户端的IP地址
         */
        String ip = request.getRemoteAddr();
 
        /*
         * 3、进行判断
         */
        if (map.get(ip) == null) {
            map.put(ip, 1);
        } else {
            int count = map.get(ip);
            map.put(ip, count + 1);
        }
        chain.doFilter(request, response);
    }
 
    /**
     * 在服务器启动时就会执行本方法,而且本方法只执行一次
     */
    public void init(FilterConfig config) throws ServletException {
        this.config = config;
    }
 



——粗粒度权限控制

拦截是否登录、拦截用户名、权限拦截。

基于角色的访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。

说明:
    给出三个页面:index.jsp  user.jsp  admin.jsp
    *   index.jsp:任何用户都可以访问,没有限制。
    *   user.jsp:只有登录用户才能访问。
    *   admin.jsp:只有管理员才能访问。

代码:

Servlet:

public class LoginServlet extends HttpServlet {
 
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        /*
         * 1、获取用户名
         * 2、判断用户名中是否包含wyc
         *   >  如果包含,就是管理员
         *   >  如果不包含,就是普通会员
         * 3、要把登录的用户名称保存到Session域中
         * 4、转发到index.jsp
         */
        String username = null;
        String n = request.getParameter("username");
 
        // 判断用户名是否为空并且是否为空字符串
        if(n != null && !n.trim().isEmpty()){
            username = n;
        }
 
        // 如果用户名不为null,则进行判断,避免空指针异常
        if(username != null && username.contains("wyc")){
            request.getSession().setAttribute("admin", username);
        } else {
            request.getSession().setAttribute("username", username);
        }
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }
}


过滤器:

UserFilter:过滤/user/*路径下的请求。

public class UserFilter implements Filter {
    public void destroy() {
    }
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 
        /*
         * 1、得到Session
         * 2、判断session域中是否存在admin,如果存在,则放行
         * 3、判断session域中是否存在username,如果存在,则放行
         * 4、如果都不存在,进行拦截操作,并将页面转发到index.jsp
         */
        HttpServletRequest req = (HttpServletRequest)request;
        String name = (String)req.getSession().getAttribute("admin");
 
        if(name != null) {
            chain.doFilter(request, response);
            return;
        }
        name = (String)req.getSession().getAttribute("username");
        if(name != null) {
            chain.doFilter(request, response);
            return;
        } else {
            request.setAttribute("msg", "请先登录");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
 
    public void init(FilterConfig fConfig) throws ServletException {
    }
}



AdminFilter:过滤/admin/*路径下的请求。

public class AdminFilter implements Filter {
 
    public void destroy() {
    }
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        /*
         * 1、得到session
         * 2、判断session域中是否存在admin,如果存在,则放行
         */
        HttpServletRequest req = (HttpServletRequest)request;
        String name = (String)req.getSession().getAttribute("admin");
        if(name != null) {
            chain.doFilter(request, response);
            return;
        } else {
            request.setAttribute("msg", "管理员,请先登录");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
 
    public void init(FilterConfig fConfig) throws ServletException {
    }
}
 


——解决全站字符乱码问题(POST和GET中文编码问题)


POST请求:
    >   request.setCharacterEncoding("utf-8");
GET请求:
    >   String username = request.getParameter("username");
    >   username = new String(username.getBytes("iso-8859-1"), "utf-8");

过滤器:

Encodingfilter:

public class EncodingFilter implements Filter {
 
    public void destroy() {
    }
 
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 处理POST请求编码
        request.setCharacterEncoding("utf-8");
 
        /*
         * 处理GET请求的编码问题
         */
 
        /*
         * 将原请求对象转换为自己的HttpServletRequest对象
         * 1、写一个request的装饰类
         * 2、增强getParameter()方法
         * 3、在“放行”时使用自己的request对象
         */
 
        /*
         * 判断请求方式
         * 不同的请求方式应该使用不同的编码处理方式
         */
        HttpServletRequest req = (HttpServletRequest)request;
 
        if(req.getMethod().equals("GET")){
            EncodingRequest er = new EncodingRequest(req);
            /*
             * 将实现了HttpServletRequest接口的对象传入
             */
            chain.doFilter(er, response);
        } else if(req.getMethod().equals("POST")) {
            chain.doFilter(request, response);
        }
    }
 
    public void init(FilterConfig fConfig) throws ServletException {
    }
}


HttpServletRequest装饰类:

可以将此类保存为工具类,只需要在web.xml中进行配置即可使用。

/**
 * 装饰request
 * 
 * @author 31067
 * 
 */
public class EncodingRequest extends HttpServletRequestWrapper {
    private HttpServletRequest request;
 
    public EncodingRequest(HttpServletRequest request) {
        /*
         * 父类实现了“一切拜托你”
         */
        super(request);
        this.request = request;
    }
 
    @Override
    public String getParameter(String name) {
        String value = request.getParameter(name);
        /*
         * 处理编码问题
         */
        try {
            value = new String(value.getBytes("iso-8859-1"), "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return value;
    }
}



——页面静态化

说明:
    你到“当当”搜索最多的是什么分类呢?没错,就是Java分类。当你去搜索Java分类时,“当当”会不会去查询数据库呢?当然会了,不查询数据库怎么获取Java分类下的图书呢?其实每天都会有很多人去搜索“Java分类”的图书,每次都需要访问数据库,这会有性能上的缺失,如果是访问静态页面(HTML)那么就会快得多了,静态页面本身就比动态页面快很多倍,而且动态页面总是要去查询数据库,这会更加的降低速度。

    页面静态化是把动态页面生成的HTML保存到服务器的文件上,然后当有相同请求时,不再去执行动态页面,而是直接给用户响应上次已经生成的静态页面,而且静态页面还有助于搜索引擎找到你。


步骤:

1、写一个小项目,图书管理系统
    页面:
        *   jsp:index.jsp
            链接页面,有四个超链接。
            >   查询所有
            >   查看SE分类
            >   查看EE分类
            >   查看框架分类
        *   show.jsp
            显示查询结果。

    Servlet:
        BookServlet
            >   findAll()
                查看所有图书
            >   findByCategory(int category)
                按分类查询

    BookService:
        略

    BookDao:
        >   List<Book> findAll()
        >   List<Book> findByCategory(int category)

    domain:
        Book类

    SQL脚本:

        create database bookdb;
 
        create table book (
            id char(32) primary key,
            name varchar(100),
            price numeric(10, 2),
            category int
        );
 
        insert into book values (‘b1‘, ‘JavaSE_1‘, 10, 1);
        insert into book values (‘b2‘, ‘JavaSE_2‘, 15, 1);
        insert into book values (‘b3‘, ‘JavaSE_3‘, 20, 1);
        insert into book values (‘b4‘, ‘JavaSE_4‘, 25, 1);
 
        insert into book values (‘b5‘, ‘JavaEE_1‘, 30, 2);
        insert into book values (‘b6‘, ‘JavaEE_2‘, 35, 2);
        insert into book values (‘b7‘, ‘JavaEE_3‘, 40, 2);
 
        insert into book values (‘b8‘, ‘Java_frameword_1‘, 45, 3);
        insert into book values (‘b9‘, ‘Java_frameword_2‘, 50, 3);
 
        select * from book;

===============================================================================

com.wyc.book.dao.BookDao

import java.sql.SQLException;
import java.util.List;
 
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
 
import com.wyc.book.domain.Book;
import com.wyc.jdbc.TxQueryRunner;
 
public class BookDao {
    private QueryRunner qr = new TxQueryRunner();
 
    public List<Book> findAll() {
 
        String sql = "select * from book";
 
        try {
            return qr.query(sql, new BeanListHandler<Book>(Book.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
 
    public List<Book> findByCategory(int category) {
 
        String sql = "select * from book where category = ?";
        Object[] params = { category };
 
        try {
            return qr.query(sql, new BeanListHandler<Book>(Book.class), params);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}


===============================================================================

com.wyc.book.domain.Book

public class Book {
    private String id;
    private String name;
    private double price;
    private int category;
 
    @Override
    public String toString() {
        return "Book [id=" + id + ", name=" + name + ", price=" + price + ", categroy=" + category + "]";
    }
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public double getPrice() {
        return price;
    }
 
    public void setPrice(double price) {
        this.price = price;
    }
 
    public int getCategory() {
        return category;
    }
 
    public void setCategory(int category) {
        this.category = category;
    }
}

===============================================================================

com.wyc.book.service.BookService

import java.util.List;
 
import com.wyc.book.dao.BookDao;
import com.wyc.book.domain.Book;
 
public class BookService {
 
    private BookDao bookDao = new BookDao();
 
    public List<Book> findAll(){
        return bookDao.findAll();
    }
 
    public List<Book> findByCategory(int category){
        return bookDao.findByCategory(category);
    }
}

===============================================================================

com.wyc.book.web.servlet.BookServlet

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import com.wyc.book.service.BookService;
import com.wyc.servlet.BaseServlet;
 
public class BookServlet extends BaseServlet {
 
    private BookService bookService = new BookService();
 
    public String findAll(HttpServletRequest request, HttpServletResponse response) {
        request.setAttribute("bookList", bookService.findAll());
        return "f:show.jsp";
    }
 
    public String findByCategory(HttpServletRequest request, HttpServletResponse response) {
        String value = request.getParameter("category");
        int category = Integer.parseInt(value);
        request.setAttribute("bookList", bookService.findByCategory(category));
        return "f:show.jsp";
    }
}

===============================================================================

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
 
<c3p0-config>
    <!-- 默认配置 -->
    <default-config>
        <!-- 连接四大参数配置 -->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/bookdb</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">Admin123</property>
        <!-- 连接池参数配置 -->
        <property name="acquireIncrement">3</property>
        <property name="initialPoolSize">10</property>
        <prpperty name="minPoolSize">2</prpperty>
        <property name="maxPoolSize">10</property>
    </default-config>
</c3p0-config>

===============================================================================

index.jsp

<body>
    <h1>链接页面</h1>
    <a href="<c:url value=‘/BookServlet?method=findAll‘ />">查询所有</a>
    <a href="<c:url value=‘/BookServlet?method=findByCategory&category=1‘ />">查询SE</a>
    <a href="<c:url value=‘/BookServlet?method=findByCategory&category=2‘ />">查询EE</a>
    <a href="<c:url value=‘/BookServlet?method=findByCategory&category=3‘ />">查询Framework</a>
</body>

===============================================================================

show.jsp

<body>
    <h1 align="center">图书列表</h1>
    <table border="1" align="center" width="50%">
        <tr>
            <th>书名</th>
            <th>单价</th>
            <th>分类</th>
        </tr>
    <c:forEach items="${requestScope.bookList }" var="book">
        <tr>
            <td>${book.name }</td>
            <td>${book.price }</td>
            <c:if test="${book.category eq 1 }">
                <td><font color="green">JavaSE</font></td>
            </c:if>
            <c:if test="${book.category eq 2 }">
                <td><font color="red">JavaEE</font></td>
            </c:if>
            <c:if test="${book.category eq 3 }">
                <td><font color="blue">JavaFramework</font></td>
            </c:if>
        </tr>
    </c:forEach>
    </table>
</body>

===============================================================================
 


2、页面静态化

首次访问时去数据库获取数据,然后把数据保存到HTML页面中。
第二次访问时,就不再去访问数据库了,而是直接显示HTML。

给出一个过滤器,把Servlet请求的资源所做的输出,保存到HTML中,然后再重定向到HTML页面,当第二次访问时,这个HTML已经存在,那么直接重定向即可,不需要再次访问Servlet了。




com.wyc.book.web.filter.StaticFilter

import java.io.File;
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;
import javax.servlet.http.HttpServletResponse;
 
public class StaticFilter implements Filter {
 
    private FilterConfig config;
 
    public void destroy() {
    }
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse)response;
 
        /*
         * 1、第一次访问时,要查找请求对应的HTML页面是否存在,如果存在,则重定向到指定页面
         * 2、如果HTML不存在,则放行,把Servlet访问数据库后输出给客户端的数据保存到一个HTML页面中,再重定向到HTML页面
         */
 
        /*
         * 一、获取Category参数,来判断请求的页面
         *   category有四种可能:
         *   >   null -->null.html
         *   >   1 --> 1.html
         *   >   2 --> 2.html
         *   >   3 --> 3.html
         * HTML页面保存的路径,在htmls目录下
         * 
         * 判断对应的HTML文件是否存在,如果存在,直接重定向
         */
        String category = request.getParameter("category");
        // 得到对应的文件名称
        String htmlPage = category + ".html";
 
        // 得到文件的存放目录,获取的是真实目录
        String htmlPath = config.getServletContext().getRealPath("/htmls");

        // 创建文件
        File destFile = new File(htmlPath, htmlPage);
 
        // 如果文件存在,则重定向到该文件
        if(destFile.exists()) {
            // 重定向,重定向要使用完整的绝对路径
            resp.sendRedirect(req.getContextPath() + "/htmls/" + htmlPage);
            return;
        }
 
        /*
         * 二、如果HTML文件不存在,需要生成HTML页面并保存
         * 1、放行,show.jsp页面会做出输出操作,可以将输出的内容输出到指定的HTML页面,而不是输出到客户端
         * 2、调包response,让它的getWriter()与一个HTML文件绑定,而不是客户端,那么show.jsp的输出操作就输出到了指定的HTML文件中了
         */
 
        // 传递文件的绝对路径
        StaticResponse sr = new StaticResponse(resp, destFile.getAbsolutePath());
 
        /*
         * 放行,即生成了HTML文件
         * 
         * 在放行(输出)之前,需要解决乱码问题:
         *   因为所有的数据都是通过show.jsp页面进行输出的,所以可以在show.jsp页面中添加如下meta标签
         *   <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
         */
        chain.doFilter(request, sr);
 
        // 此时HTML文件已经存在了,可以重定向到指定页面了
        sr.sendRedirect(req.getContextPath() + "/htmls/" + htmlPage);
    }
 
    public void init(FilterConfig fConfig) throws ServletException {
        this.config = fConfig;
    }
}



===============================================================================

com.wyc.book.web.filter.StaticResponse

HttpServletResponse装饰类 


import java.io.PrintWriter;
 
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
 
public class StaticResponse extends HttpServletResponseWrapper {
 
    private PrintWriter pw;
 
    /*
     * String path:指向HTML文件的路径
     */
    public StaticResponse(HttpServletResponse response, String path) {
        super(response);
 
        try {
 
            // 创建一个HTML文件并创建一个与HTML文件路径绑定在一起的流对象
            this.pw = new PrintWriter(path, "utf-8");
 
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
 
    public PrintWriter getWriter(){
        // 返回一个与HTML绑定在一起的printWriter对象
        // JSP会使用它进行输出,这样数据都会输出到指定的HTML文件中了
        return pw;
    }
}





































































































































































































































































































































































以上是关于JavaWeb之过滤器的主要内容,如果未能解决你的问题,请参考以下文章

javaWeb--之--过滤器(filter)篇

JavaWeb 之 Filter:过滤器

JavaWeb详解(第三篇)之Servlet基础简介-过滤器Filter&Listener监听器

JavaWeb之Filter&Listener

javaweb之session过期验证

java_day30_javaWeb三大组件之过滤器Filter