時間:
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;
}
}