Java學習路線-49:Servlet過濾器Filter

課時1 過濾器的入門

JavaWeb三大組件
1、都需要在web.xml中進行配置
Servlet
Filter
Listener

2、過濾器
會在一組資源(jsp, servlet, css, html等等)的前面執行
可以讓請求得到目標資源,也可以不讓請求達到
過濾器有攔截請求的能力

3、編寫過濾器
(1)實現Filter接口
(2)在web.xml中進行配置
(3)Filter是單例的

4、配置web.xml

<web-app>
    <filter>
        <filter-name>FilerName</filter-name>
        <filter-class>FilerClass</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilerName</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

繼承示例

package com.pengshiyu.filtrer;

import javax.servlet.*;
import java.io.IOException;

public class Afilter implements Filter {
    /**
     * 創建之後馬上執行,用來做初始化
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * 每次過濾都會執行
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, 
        FilterChain filterChain) throws IOException, ServletException {
        System.out.println("進入過濾器");
        // 調用後序方法
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("離開過濾器");
    }

    /**
     * 銷燬之前的調用,用來釋放資源
     */
    @Override
    public void destroy() {

    }
}

FilterConfig -> 與ServletConfig相似
獲取初始化參數 getInitParameter()
獲取過濾器名稱 getFilterName()
獲取application getServletContext()

FilterChain
放行,執行後序方法 doFilter()

課時2 多個過濾器的執行順序

執行下一個過濾器或目標資源
FilterChain.doFilter()

Afilter進入過濾器
Bfilter進入過濾器
getAge
Bfilter離開過濾器
Afilter離開過濾器

課時3 四種攔截方式

請求 REQUEST 默認
轉發 FORWARD
包含 INCLUDE
錯誤 ERROR

<filter-mapping>
    <filter-name>FilerName</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

頁面出錯

<error-page>
    <error-code>500</error-code>
    <location>500.html</location>
</error-page>

課時4 使用filter-mapping控制多個Filter的執行順序

filter-mapping的配置順序決定過濾器執行順序

課時5 Filter的應用場景、Filter的目標資源、小結

預處理:執行目標資源之前做預處理工作,例如設置編碼
攔截:通過條件判斷是否放行,例如用戶登錄校驗
回程攔截:目標資源執行之後,做一些後序的特殊處理工作,例如目標資源輸出的數據進行處理

直接指定servlet-name

<filter-mapping>
    <filter-name>FilerName</filter-name>
    <servlet-name>ServletName</servlet-name>
</filter-mapping>

小結
Filter3個方法
FilterChain類
4中攔截方式

課時6 案例1:分IP統計訪問次數

數據結構:

ip count
192.168.0.1 32
192.168.0.2 22

統計工作在所有資源之前都執行,使用Filter
這個過濾器只做統計,不做攔截
數據Map<String, Integer>
Map保存到ServletContext中
從request中獲取客戶端ip

使用監聽器創建 map
AListener.java

package com.pengshiyu.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.LinkedHashMap;
import java.util.Map;

public class AListener implements ServletContextListener {
    // 服務器啓動時創建map
    public void contextInitialized(ServletContextEvent sce) {
        Map<String, Integer> map = new LinkedHashMap<String, Integer>();
        sce.getServletContext().setAttribute("map", map);
    }

    public void contextDestroyed(ServletContextEvent sce) {
    }
}

使用過濾器統計數據
AFilter.java

package com.pengshiyu.filter;

import javax.servlet.*;
import java.io.IOException;
import java.util.Map;

public class AFilter implements Filter {
    private FilterConfig config;

    /**
     * 創建之後馬上執行,用來做初始化
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.config = filterConfig;
    }

    /**
     * 每次過濾都會執行
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
        FilterChain filterChain) throws IOException, ServletException {
        ServletContext app = this.config.getServletContext();
        Map<String, Integer> map = (Map<String, Integer>)app.getAttribute("map");

        String ip  =  request.getRemoteAddr();
        System.out.println("ip: " + ip);

        if(map.containsKey(ip)){
            Integer count = map.get(ip);
            map.put(ip, count+1);
        } else{
            map.put(ip, 1);
        }

        // 放行
        filterChain.doFilter(request, response);

    }

    /**
     * 銷燬之前的調用,用來釋放資源
     */
    @Override
    public void destroy() {

    }
}

顯示數據
BServlet.java

package com.pengshiyu.servlet;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

public class BServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
    throws IOException {
        ServletContext app = getServletContext();
        Map<String, Integer> map = (Map<String, Integer>) app.getAttribute("map");
        
        response.setContentType("text/html; charset=UTF-8");
        response.getWriter().println(map.toString());

    }
}


配置監聽器和過濾器生效
web.xml

<?xml version="1.0" encoding="utf-8"?>

<web-app>
    <servlet>
        <servlet-name>BServlet</servlet-name>
        <servlet-class>com.pengshiyu.servlet.BServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BServlet</servlet-name>
        <url-pattern>/b</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>com.pengshiyu.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>com.pengshiyu.listener.AListener</listener-class>
    </listener>
</web-app>

課時7 案例2:粗粒度權限管理

基於角色的權限控制RBAC
tb_user
tb_role
tb_userrole
tb_menu
tb_rolemenu

web.xml

<?xml version="1.0" encoding="utf-8"?>

<web-app>
    <servlet>
        <servlet-name>AServlet</servlet-name>
        <servlet-class>com.pengshiyu.servlet.AServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>com.pengshiyu.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
<!--        不能將過濾器設置在login.html上,不然沒法登錄了-->
        <filter-name>AFilter</filter-name>
        <url-pattern>/hello.html</url-pattern>
    </filter-mapping>

</web-app>

AServlet.java

package com.pengshiyu.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class AServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
        String username = request.getParameter("username");
        System.out.println("post: " + username);
        // 設置session
        request.getSession().setAttribute("username", username);
        // 跳轉頁面
        request.getRequestDispatcher("hello.html").forward(request, response);
    }
}

過濾器進行簡單的權限校驗
AFilter.java

package com.pengshiyu.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class AFilter implements Filter {
    private FilterConfig config;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.config = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse response, 
        FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;

        String username = (String) request.getSession().getAttribute("username");
        System.out.println("filter: " + username);

        if(username != null){
            // 放行
            filterChain.doFilter(request, response);
        } else{
            // 跳轉到登錄頁
            request.getRequestDispatcher("login.html").forward(request, response);
        }
    }

    @Override
    public void destroy() {

    }
}

課時8 案例3:全站編碼問題

// post編碼
request.setCharacterEncoding("utf-8");

// get編碼
String username = request.getParameter("username");
username = new String(username.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);

// 響應編碼
response.setContentType("text/html; charset=UTF-8");

HttpServletRequest裝飾類
EncodingRequest.java

package com.pengshiyu.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.nio.charset.StandardCharsets;

// 裝飾器
public class EncodingRequest extends HttpServletRequestWrapper {

    public EncodingRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getParameter(String name) {
        // 處理編碼問題
        String value = super.getParameter(name);
        value = new String(value.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
        return value;

    }
}

過濾器AFilter.java

package com.pengshiyu.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class AFilter implements Filter {

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
        FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String method = httpServletRequest.getMethod();

        // 設置響應編碼
        response.setContentType("text/html; charset=UTF-8");

        if ("GET".equals(method)) {
            // 放行
            EncodingRequest encodingRequest = new EncodingRequest(httpServletRequest);
            filterChain.doFilter(encodingRequest, response);
        } else if ("POST".equals(method)) {
            request.setCharacterEncoding("utf-8");
            filterChain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {

    }
}

響應處理AServlet.java

package com.pengshiyu.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
        System.out.println(request.getParameter("name"));
        response.getWriter().print("你好");
    }
}

web.xml

<?xml version="1.0" encoding="utf-8"?>

<web-app>
    <!-- 註冊 Servlet,幫助web服務器反射該類 -->
    <servlet>
        <servlet-name>AServlet</servlet-name>
        <servlet-class>com.pengshiyu.servlet.AServlet</servlet-class>
    </servlet>
    <!-- 映射 Servlet 資源,用url-pattern元素標示 URL -->
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>com.pengshiyu.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
<!--        不能將過濾器設置在login.html上,不然沒法登錄了-->
        <filter-name>AFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

課時9 案例4:頁面靜態化之準備工作(圖書管理小項目)

功能:
查詢所有
按分類查看

BookServlet
    findAll()       查詢全部
    findByCategory() 按分類查詢

BookService: 省略

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

Book:
    bid
    bname
    price
    category

靜態化:
第一次訪問從數據庫取數據,保存到html中
第二次之後訪問就直接從html中讀取,不再從數據庫中取數據

數據準備:

create table tb_book(
    bid int primary key auto_increment,
    bname varchar(50),
    price decimal(10, 2),
    category int
);

insert into tb_book(bname, price, category) values("Java", 12, 1);
insert into tb_book(bname, price, category) values("Python", 12, 1);
insert into tb_book(bname, price, category) values("JavaScript", 12, 1);
insert into tb_book(bname, price, category) values("Go", 12, 1);

insert into tb_book(bname, price, category) values("三國演義", 12, 2);
insert into tb_book(bname, price, category) values("西遊記", 12, 2);
insert into tb_book(bname, price, category) values("水滸傳", 12, 2);
insert into tb_book(bname, price, category) values("紅樓夢", 12, 2);

創建對應的Book類

package com.pengshiyu.bean;

public class Book {
    private int bid;
    private String bname;
    private double price;
    private int category;

    public Book() {
    }

    public int getBid() {
        return bid;
    }

    public void setBid(int bid) {
        this.bid = bid;
    }

    public String getBname() {
        return bname;
    }

    public void setBname(String bname) {
        this.bname = bname;
    }

    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;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bid=" + bid +
                ", bname='" + bname + '\'' +
                ", price=" + price +
                ", category=" + category +
                '}';
    }
}

BookDao.java

package com.pengshiyu.dao;

import com.pengshiyu.bean.Book;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import util.TxQueryRunner;

import java.sql.SQLException;
import java.util.List;

public class BookDao {
    private QueryRunner qr = new TxQueryRunner();

    public  List<Book> findAll() {
        String sql = "select * from tb_book";

        try {
            List<Book> list = qr.query(sql, new BeanListHandler<Book>(Book.class));
            System.out.println(list);
            return list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public  List<Book> findByCategory(int category) {
        String sql = "select * from tb_book where category = ?";

        try {
            return qr.query(sql, new BeanListHandler<Book>(Book.class), category);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

BookServlet

package com.pengshiyu.servlet;

import com.pengshiyu.dao.BookDao;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class BookServlet extends BaseServlet {
    private BookDao bookDao = new BookDao();

    public void findAll(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        request.setAttribute("bookList", bookDao.findAll());
        request.getRequestDispatcher("book.jsp").forward(request, response);
    }

    public void findByCategory(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int category = Integer.parseInt(request.getParameter("category"));
        request.setAttribute("bookList", bookDao.findByCategory(category));
        request.getRequestDispatcher("book.jsp").forward(request, response);

    }
}

用到的工具類 TxQueryRunner.java

package util;

import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

public class TxQueryRunner extends QueryRunner {

    @Override
    public int[] batch(String sql, Object[][] params) throws SQLException {
        Connection con = JdbcUtil.getConnection();
        int[] result = super.batch(con, sql, params);
        JdbcUtil.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
            throws SQLException {
        Connection con = JdbcUtil.getConnection();
        T result = super.query(con, sql, rsh, params);
        JdbcUtil.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
        Connection con = JdbcUtil.getConnection();
        T result = super.query(con, sql, rsh);
        JdbcUtil.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql) throws SQLException {
        Connection con = JdbcUtil.getConnection();
        int result = super.update(con, sql);
        JdbcUtil.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object param) throws SQLException {
        Connection con = JdbcUtil.getConnection();
        int result = super.update(con, sql, param);
        JdbcUtil.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object... params) throws SQLException {
        Connection con = JdbcUtil.getConnection();
        int result = super.update(con, sql, params);
        JdbcUtil.releaseConnection(con);
        return result;
    }
}

JdbcUtil.java

package util;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class JdbcUtil {
    // 需要配置c3p0-config.xml
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    // 返回連接對象
    public static Connection getConnection() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    // 返回連接池對象
    public static DataSource getDataSource() {
        return dataSource;
    }

    // 釋放連接
    public static void releaseConnection(Connection connection) {

        try {
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

book.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<h2>圖書列表</h2>
分類:
<a href="book?method=findAll">全部</a>
<a href="book?method=findByCategory&category=1">第一類</a>
<a href="book?method=findByCategory&category=2">第二類</a>

<table border="1">
    <tr>
        <th>ID</th>
        <th>書名</th>
        <th>價格</th>
        <th>分類</th>
    </tr>
    <c:forEach items="${bookList}" var="book">
        <tr>
            <td>${book.bid}</td>
            <td>${book.bname}</td>
            <td>${book.price}</td>
            <td>${book.category}</td>
        </tr>
    </c:forEach>
</table>

配置文件

pom.xml

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

web.xml

<servlet-mapping>
    <servlet-name>BookServlet</servlet-name>
    <url-pattern>/book</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>BookServlet</servlet-name>
    <servlet-class>com.pengshiyu.servlet.BookServlet</servlet-class>
</servlet>

c3p0-config.xml

<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
    <!-- 這是默認配置信息 -->
    <default-config>
        <!-- 連接四大參數配置 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/data</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <!-- 池參數配置 -->
        <property name="acquireIncrement">2</property>
        <property name="initialPoolSize">2</property>
        <property name="minPoolSize">2</property>
        <property name="maxPoolSize">10</property>
    </default-config>
</c3p0-config>

訪問路徑
http://localhost:8080/demo/book?method=findAll
http://localhost:8080/demo/book?method=findByCategory&category=1

課時10 案例4:頁面靜態化之如果文件存在直接重定向到html

使用一個過濾器,把servlet請求的資源輸出保存到html中
第二次訪問資源的時候,如果已存在就直接重定向到html文件

課時11 案例5:頁面靜態之生成html頁面

CacheFilter.java

package com.pengshiyu.filter;

import com.pengshiyu.response.StaticResponse;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;

public class CacheFilter implements Filter {
    private FilterConfig config;
    private final String cacheFileName = "cache";
    private String cacheFilePath = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.config = filterConfig;
        this.cacheFilePath = this.config.getServletContext().getRealPath(this.cacheFileName);

        File file = new File(this.cacheFilePath);
        if(file.exists()){
            file.mkdir();
        }
        
    }

    /**
     * 訪問路徑
     * http://localhost:8080/demo/book?method=findByCategory&category=4
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;

        String category = request.getParameter("category");

        String filepath = this.cacheFilePath + "/" + category + ".html";
        File file = new File(filepath);

        // 如果頁面不存在就緩存頁面
        if(!file.exists()){
            StaticResponse staticResponse = new StaticResponse(response, filepath);
            filterChain.doFilter(request, staticResponse);
        }

        System.out.println("文件存在了");
        request.getRequestDispatcher(this.cacheFileName + "/" + category + ".html").forward(request, response);

    }

    @Override
    public void destroy() {

    }
}

StaticResponse.java

package com.pengshiyu.response;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;

public class StaticResponse extends HttpServletResponseWrapper {
    private PrintWriter pw;

    public StaticResponse(HttpServletResponse response, String filename) throws FileNotFoundException {
        super(response);
        this.pw = new PrintWriter(filename);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        // 掉包輸出流
        return this.pw;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章