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