zoukankan      html  css  js  c++  java
  • 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;
        }
    }
  • 相关阅读:
    英文字母打字速度测试游戏代码
    JS写一个JS解释器
    JS中try.. catch..的用法
    使用HTML CSS和JavaScript创建图像动画
    6个强大的CSS选择器
    TypeScript 3.9稳定版本新增功能
    10个JavaScript代码片段,使你更加容易前端开发。
    BZOJ.3811.玛里苟斯(线性基)
    Bluestein's Algorithm
    AGC 002E.Candy Piles(博弈论)
  • 原文地址:https://www.cnblogs.com/wwwwyc/p/6375341.html
Copyright © 2011-2022 走看看