主要內(nèi)容:
- filter的部署
- 包裝設(shè)計(jì)模式對(duì)request和response進(jìn)行包裝增強(qiáng)
一、Filter的部署-注冊(cè)Filter
<filter-name>:指定過(guò)濾器的名字,內(nèi)容不能為空。<filter-class>: 指定過(guò)濾器的完整的限定類(lèi)名<init-param>指定初始化參數(shù),其子元素<param-name>指定參數(shù)的名字,<param-value>指定參數(shù)的值。可以使用FilterConfig接口對(duì)象來(lái)訪(fǎng)問(wèn)初始化參數(shù)。-
<filter-mapping>設(shè)置一個(gè)Filter所負(fù)責(zé)攔截的資源。一個(gè)Filter攔截的資源可以通過(guò)兩種方式來(lái)指定:Servlet名稱(chēng)和資源訪(fǎng)問(wèn)的請(qǐng)求路徑。-
<filter-name>子元素用于設(shè)置Filter注冊(cè)名稱(chēng),和上面一樣。 -
<url-pattern>設(shè)置Filter所攔截的請(qǐng)求路徑;/表示攔截所有,.jsp表示攔截jsp文件。等等。 -
<dispatcher>指定過(guò)濾器所攔截的資源被Servlet容器調(diào)用的方式,可以是REQUEST,INCLUDE,F(xiàn)ORWARD,ERROR之一(必須是大寫(xiě)),默認(rèn)是REQUEST。用戶(hù)可以設(shè)置多個(gè)<dispatcher>子元素來(lái)指定Filter對(duì)資源的多種調(diào)用方式進(jìn)行攔截。
-
-
<dispatcher>子元素可以設(shè)置的值及其意義-
REQUEST:當(dāng)用戶(hù)直接訪(fǎng)問(wèn)頁(yè)面時(shí),web容器將會(huì)調(diào)用過(guò)濾器。如果目標(biāo)資源是通過(guò)RequestDispatcher的include或forward方法訪(fǎng)問(wèn)時(shí),那么該過(guò)濾器就不會(huì)被調(diào)用。 -
INCLUDE:如果目標(biāo)資源是通過(guò)RequestDispatcher的include方法訪(fǎng)問(wèn)時(shí),那么該過(guò)濾器將被調(diào)用,除此之外,該過(guò)濾器將不會(huì)被調(diào)用。 -
FORWARD:如果目標(biāo)資源是通過(guò)RequestDispatcher的forward方法訪(fǎng)問(wèn)時(shí),那么該過(guò)濾器將被調(diào)用,除此之外,該過(guò)濾器將不會(huì)被調(diào)用。 -
ERROR:如果目標(biāo)資源是通過(guò)聲明式異常處理機(jī)制調(diào)用時(shí),那么該過(guò)濾器將被調(diào)用。除此之外,過(guò)濾器不會(huì)被調(diào)用。
-
示例:
FilterDemo4.java
package cn.itcast.web.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;
public class FilterDemo4 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("hahah");
}
@Override
public void destroy() {
}
}
配置:
<filter>
<filter-name>FilterDemo4</filter-name>
<filter-class>cn.itcast.web.filter.FilterDemo4</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo4</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/2.jsp</location>
</error-page>
1.jsp
<body>
<%
int x = 1/0;
%>
</body>
2.jsp
<body>
error page
</body>
說(shuō)明:
- 1.本來(lái)我們是在
1.jsp中配置errorPage="/2.jsp",但是這樣配置不起作用。于是我們?cè)?code>web.xml中進(jìn)行配置。試驗(yàn)時(shí)我們?cè)L問(wèn)1.jsp,那么就會(huì)被過(guò)濾器攔截下來(lái),不會(huì)到達(dá)2.jsp。 - 2.在以后的開(kāi)發(fā)中我們經(jīng)常要攔截forward轉(zhuǎn)發(fā)的資源,注意在配置文件中進(jìn)行配置,不然是不會(huì)起作用的。在上個(gè)例子中我們可以看到。
二、filter高級(jí)開(kāi)發(fā)
由于開(kāi)發(fā)人員在Filter中可以得到代表用戶(hù)請(qǐng)求和響應(yīng)的request、response對(duì)象,因此在編程中可以使用Decorator(裝飾器)模式對(duì)這些對(duì)象進(jìn)行包裝,再把包裝后的對(duì)象傳給目標(biāo)資源,從而實(shí)現(xiàn)一些特殊需求。
注意:有四種方式訪(fǎng)問(wèn)web資源,正常的通過(guò)瀏覽器直接訪(fǎng)問(wèn)(request方式)、forward方式、include方式和error方式。
2.1回顧包裝開(kāi)發(fā)模式
之前我們?cè)诠P記20中講到過(guò)包裝開(kāi)發(fā)模式,這里再次回顧一下。使用包裝設(shè)計(jì)模式對(duì)BufferedReader類(lèi)進(jìn)行包裝增強(qiáng)。
BufferedReaderWrapper.java
package cn.itcast.demo;
import java.io.BufferedReader;
import java.io.IOException;
//使用包裝設(shè)計(jì)模式對(duì)BufferedReader類(lèi)進(jìn)行包裝增強(qiáng)
/*
* 1.實(shí)現(xiàn)與被增強(qiáng)對(duì)象相同的接口,如果接口方法太多,也可以繼承一個(gè)類(lèi)
* 2.定義一個(gè)變量記住被增強(qiáng)對(duì)象
* 3.定義一個(gè)構(gòu)造器,接收被增強(qiáng)對(duì)象
* 4.覆蓋需要增強(qiáng)的方法
* 5.對(duì)于不想增強(qiáng)的方法,直接調(diào)用被增強(qiáng)對(duì)象的方法
*
* */
import java.io.Reader;
public class BufferedReaderWrapper extends BufferedReader {
private BufferedReader br;
private int linenum = 1;
public BufferedReaderWrapper(BufferedReader br) {
super(br);
// 子類(lèi)在使用構(gòu)造函數(shù)的時(shí)候會(huì)調(diào)用父類(lèi)的構(gòu)造函數(shù),但是
// 這里不知道調(diào)用父類(lèi)哪個(gè)構(gòu)造函數(shù),于是就調(diào)用無(wú)參構(gòu)造
// 函數(shù),但是父類(lèi)又沒(méi)有無(wú)參構(gòu)造函數(shù),這樣就會(huì)報(bào)錯(cuò),所
// 以這里我們要指定調(diào)用父類(lèi)哪個(gè)構(gòu)造函數(shù)
this.br = br;
}
@Override
public String readLine() throws IOException {// 增強(qiáng)此方法
String line = br.readLine();
if (line == null) {
return line;
}
return linenum++ + ":" + line;
}
}
測(cè)試:TestBufferedReaderWrapper.java
package cn.itcast.demo;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestBufferedReaderWrapper {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("src/cn/itcast/demo/BufferedReaderWrapper.java"));
BufferedReaderWrapper wrapper = new BufferedReaderWrapper(br);
/*String line = null;
while((line = wrapper.readLine()) != null ){
System.out.println(line);
}*/
FileWriter fw = new FileWriter("D:\\1.java");
String line = null;
while((line = wrapper.readLine()) != null ){
fw.write(line + "\r\n");//注意要換行
}
fw.close();
wrapper.close();
br.close();
}
}
說(shuō)明:BufferedReader類(lèi)中的readLine方法可以讀取一行文本,這里我們想讓readLine讀取一行文本時(shí)還在本行最前面加上行號(hào),于是我們使用包裝設(shè)計(jì)模式對(duì)此方法進(jìn)行包裝。注意:這里當(dāng)此方法讀到空行的時(shí)候返回的是"",而不是null。
記住:能用包裝設(shè)計(jì)模式就不要用子類(lèi)的方式
2.2Decorator(包裝)設(shè)計(jì)模式
-
當(dāng)某個(gè)對(duì)象的方法不適應(yīng)業(yè)務(wù)需求時(shí),通常有兩種方式可以對(duì)方法進(jìn)行增強(qiáng):
- 編寫(xiě)子類(lèi),覆蓋需要增強(qiáng)的方法
- 使用Decorator設(shè)計(jì)模式對(duì)方法進(jìn)行增強(qiáng)
- 使用動(dòng)態(tài)代理(這里先不講)
-
在實(shí)際開(kāi)發(fā)中遇到需要增強(qiáng)對(duì)象的方法時(shí),到底選擇用哪種方式
- 沒(méi)有具體的定式,不過(guò)有一種情況下,必須使用Decorator設(shè)計(jì)模式,即被增強(qiáng)對(duì)象,開(kāi)發(fā)人員只能得到它的對(duì)象,無(wú)法得到它的class文件。
- 比如request、response對(duì)象,開(kāi)發(fā)人員之所以在Servlet中能通過(guò)sun公司定義的
HttpServletRequest\HttpServletResponse接口去操作這些對(duì)象,是因?yàn)閠omcat服務(wù)器廠(chǎng)商編寫(xiě)了request、response接口的實(shí)現(xiàn)類(lèi)。Web服務(wù)器在調(diào)用Servlet時(shí),會(huì)用這些接口的實(shí)現(xiàn)類(lèi)創(chuàng)建出對(duì)象,然后傳遞給Servlet程序。 - 此種情況下,由于開(kāi)發(fā)人員根本不知道服務(wù)器廠(chǎng)商編寫(xiě)的request、response接口的實(shí)現(xiàn)類(lèi)是哪個(gè),在程序中只能拿到其提供的對(duì)象,因此就只能采用Decorator設(shè)計(jì)模式對(duì)這些對(duì)象進(jìn)行增強(qiáng)。
Decorator設(shè)計(jì)模式的實(shí)現(xiàn)
1.首先看需要被增強(qiáng)對(duì)象繼承了什么接口或父類(lèi),編寫(xiě)一個(gè)類(lèi)也去繼承這些接口或父類(lèi)。
2.在類(lèi)中定義一個(gè)變量,變量類(lèi)型即需要增強(qiáng)對(duì)象類(lèi)型。
3.在類(lèi)中定義一個(gè)構(gòu)造函數(shù),接收需要增強(qiáng)的對(duì)象。
4.覆蓋需要增強(qiáng)的方法,編寫(xiě)增強(qiáng)的代碼。
使用此設(shè)計(jì)模式為BufferedReader類(lèi)的readLine方法添加行號(hào)的功能。在上面的例子中我們可以看到。
三、對(duì)request對(duì)象的增強(qiáng)(工程day18_2)
- ServletAPI中提供了一個(gè)request對(duì)象的Decorator設(shè)計(jì)模式的默認(rèn)實(shí)現(xiàn)類(lèi)
HttpServletRequestWrapper(HttpServletRequestWrapper類(lèi)實(shí)現(xiàn)了request接口中的所有方法,但這些方法的內(nèi)部實(shí)現(xiàn)都是僅僅調(diào)用了一下所包裝的request對(duì)象的對(duì)應(yīng)方法)以避免用戶(hù)在對(duì)request對(duì)象進(jìn)行增強(qiáng)時(shí)需要實(shí)現(xiàn)request接口中的所有方法(這樣太麻煩)。 - 使用Decorator模式包裝request對(duì)象,完全解決get、post請(qǐng)求方式下的亂碼問(wèn)題(前面有過(guò)這樣一個(gè)例子,但是那個(gè)例子中只能解決post方式下的亂碼問(wèn)題)。
3.1 包裝request對(duì)象,解決亂碼問(wèn)題
過(guò)濾器:CharacterEncodingFilter.java
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 這里我們先解決post方式的亂碼問(wèn)題
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 解決get方式的亂碼問(wèn)題
MyCharacterEncodingRequest requestWrapper = new MyCharacterEncodingRequest(
request);
chain.doFilter(requestWrapper, response);
}
@Override
public void destroy() {
}
}
class MyCharacterEncodingRequest extends HttpServletRequestWrapper {
// 這里我們使用一個(gè)變量記住Servlet傳遞的request對(duì)象。
private HttpServletRequest request;
public MyCharacterEncodingRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
// 增強(qiáng)此方法,此方法得到表單提交的數(shù)據(jù)
public String getParameter(String name) {
String value = this.request.getParameter(name);
if (value == null) {// 如果為空直接返回空即可
return null;
}
if (!this.request.getMethod().equalsIgnoreCase("get")) {
// 如果不是get方式則沒(méi)必要轉(zhuǎn)換
return value;
}
try {
/*
* value = new
* String(value.getBytes("ISO8859-1"),"UTF-8");//手工轉(zhuǎn)換,不要寫(xiě)死
*/
value = new String(value.getBytes("ISO-8859-1"),
this.request.getCharacterEncoding());// 和Request設(shè)置的編碼一致
return value;// 這里使用ISO-8859-1沒(méi)有解決亂碼,這里瀏覽器
// 提交的本就是UTF-8編碼,不需要轉(zhuǎn)換
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
說(shuō)明:由于使用request.setCharacterEncoding("UTF-8");方式對(duì)get方式提交的數(shù)據(jù)無(wú)效,所以之前那個(gè)過(guò)濾器只能解決post方式提交的數(shù)據(jù)亂碼問(wèn)題。既然這種方式不能解決get方式提交的數(shù)據(jù)的亂碼問(wèn)題,那么我們可以將HttpServletRequest包裝之后再給用戶(hù)使用。這里我們主要是對(duì)其方法getParameter進(jìn)行增強(qiáng)。這樣就解決了get方式提交的亂碼問(wèn)題。
3.2 包裝request對(duì)象,實(shí)現(xiàn)html標(biāo)簽轉(zhuǎn)義功能
(E:\apache\apache-tomcat-8.0.28-src\webapps\examples\WEB-INF\classes\util\HTMLFilter.java提供相應(yīng)的例子)
HtmlFilter .java
public class HtmlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
MyHtmlRequest myHtmlRequest = new MyHtmlRequest(request);
chain.doFilter(myHtmlRequest, response);
}
@Override
public void destroy() {
}
}
class MyHtmlRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
public MyHtmlRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if(value == null){
return null;
}
return filter(value);
}
public static String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
3.3 包裝request對(duì)象,實(shí)現(xiàn)對(duì)臟話(huà)進(jìn)行過(guò)濾
DirtyFilter.java
public class DirtyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
DirtyRequest dirtyRequest = new DirtyRequest(request);
chain.doFilter(dirtyRequest, response);
}
@Override
public void destroy() {
}
}
class DirtyRequest extends HttpServletRequestWrapper{
private List<String> dirtyWords = Arrays.asList("sb","畜生");
private HttpServletRequest request ;
public DirtyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if(value == null){
return null;
}
for(String dirtyWord : dirtyWords){
if(value.contains(dirtyWord)){
value = value.replace(dirtyWord, "***");
}
}
return value;
}
}
四、包裝response對(duì)象
4.1 包裝response對(duì)象,實(shí)現(xiàn)壓縮響應(yīng)
GzipFilter.java
package cn.itcast.web.filter;
//解決全站的壓縮問(wèn)題,對(duì)Response進(jìn)行增強(qiáng),但是這個(gè)過(guò)濾器只是解決壓縮字符,需要把數(shù)據(jù)都寫(xiě)到內(nèi)存中去,如果是下載一個(gè)大文件,內(nèi)存可能會(huì)崩,這里我們可以在
//配置文件中設(shè)定只對(duì)字符壓縮有效,這攔截jsp,js,css,html
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class GzipFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
BufferResponse myResponse = new BufferResponse(response);
chain.doFilter(request, myResponse);
byte out[] = myResponse.getBuffer();//先獲得數(shù)據(jù)
ByteArrayOutputStream bout = new ByteArrayOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(bout);//進(jìn)行壓縮
gout.write(out);
gout.close();//一定要關(guān)閉,這樣數(shù)據(jù)才會(huì)從緩存中寫(xiě)入到底層流中去
byte gzip[] = bout.toByteArray();//從底層流中取得數(shù)據(jù)
response.setHeader("content-encoding", "gzip");//這里需要告訴瀏覽器這是一個(gè)壓縮數(shù)據(jù)
response.setContentLength(gzip.length);
response.getOutputStream().write(gzip);//寫(xiě)出到瀏覽器
}
@Override
public void destroy() {}
}
/*OutputStream out = response.getOutputStream();
out.write("aaaaaa".getBytes());
*之后servlet在使用Response的時(shí)候其實(shí)是使用我們自己定義的Response,而調(diào)用的getOutputStream方法也是我們自己定義的,這個(gè)方法返回的是
*MyServletOutputStream,然后調(diào)用write方法也是調(diào)用我們自己定義的write方法,此方法是將數(shù)據(jù)寫(xiě)到ByteArrayOutputStream底層流中。
*
*/
class BufferResponse extends HttpServletResponseWrapper{
private HttpServletResponse response;
private ByteArrayOutputStream bout = new ByteArrayOutputStream();//字節(jié)流
private PrintWriter pw;
public BufferResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
//這里我們對(duì)此方法進(jìn)行了增強(qiáng),不管是圖片還是文本等數(shù)據(jù)都會(huì)進(jìn)行壓縮,但是如果直接訪(fǎng)問(wèn)jsp卻不會(huì),因?yàn)閖sp一般是調(diào)用getWriter
//方法,所以這里我們需要對(duì)getWriter方法進(jìn)行增強(qiáng)
return new MyServletOutputStream(bout);
}
@Override
public PrintWriter getWriter() throws IOException {
/*return new PrintWriter(bout);//因?yàn)镻rintWriter有接受一個(gè)底層流的構(gòu)造函數(shù),所以這里我們不需要重寫(xiě)一個(gè),但是這個(gè)方法也是一個(gè)包裝類(lèi)
//這個(gè)類(lèi)當(dāng)緩存沒(méi)有寫(xiě)滿(mǎn)的時(shí)候是不會(huì)講數(shù)據(jù)寫(xiě)到底層流中去,所以這里我們需要強(qiáng)制關(guān)閉此類(lèi)*/
//pw = new PrintWriter(bout);//jsp中的漢字是一個(gè)字符流,這里會(huì)將其先轉(zhuǎn)換為字節(jié)流,查的碼表是gb2312的碼表,但是我們?cè)O(shè)置的碼表是UTF-8
//而PrintWriter有一個(gè)接受一個(gè)字符流的方法,而字符流就會(huì)有設(shè)置碼表的方法,而OutputStreamWriter是字符流到字節(jié)流的一個(gè)轉(zhuǎn)換流,里面就可以指定碼表
pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding()));
return pw;
}
public byte[] getBuffer(){
try{
if(pw != null){
pw.close();
}
if(bout != null){
bout.flush();
return bout.toByteArray();
}
return null;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class MyServletOutputStream extends ServletOutputStream{
private ByteArrayOutputStream bout ;
public MyServletOutputStream(ByteArrayOutputStream bout) {
this.bout = bout;
}
@Override
public void write(int arg0) throws IOException {
this.bout.write(arg0);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener listener) {}
}
說(shuō)明:
- 這個(gè)包裝類(lèi)的實(shí)現(xiàn)有點(diǎn)難理解,這里我們?cè)敿?xì)說(shuō)明一下。這里是實(shí)現(xiàn)數(shù)據(jù)的壓縮之后再輸出給瀏覽器,而我們完全可以在servlet中先從緩存中拿到數(shù)據(jù)壓縮之后再輸出,但是那樣的話(huà)每個(gè)需要壓縮資源的servlet都需要編寫(xiě)重復(fù)的代碼,所以這里我們使用過(guò)濾器進(jìn)行簡(jiǎn)化。
- 首先服務(wù)器將資源寫(xiě)給瀏覽器的時(shí)候(注意這里和request過(guò)濾器的方向是反的),會(huì)被這個(gè)瀏覽攔截到。攔截到之后我們對(duì)response進(jìn)行增強(qiáng)。
- 而一般會(huì)調(diào)用
getOutputStream方法和getWriter方法向?yàn)g覽器中寫(xiě)數(shù)據(jù),于是這里我們對(duì)這兩個(gè)方法進(jìn)行增強(qiáng)。 - 方法
getOutputStream會(huì)返回一個(gè)ServletOutputStream流,我們需要增強(qiáng),我們要讓此方法返回一個(gè)我們自己定義的一個(gè)流,于是對(duì)此類(lèi)也進(jìn)行包裝。 - 方法
getWriter中我們將自己定義的流傳遞給PrintWriter方法。 - 我們將數(shù)據(jù)壓縮之后存入到底層流中,之后用戶(hù)在調(diào)用
getOutputStream方法和getWriter時(shí)拿到的數(shù)據(jù)就是我們壓縮之后的數(shù)據(jù)。 - 其實(shí)整個(gè)過(guò)程就是當(dāng)用戶(hù)調(diào)用方法向?yàn)g覽器輸出數(shù)據(jù)的時(shí)候我們將response的相關(guān)方法進(jìn)行增強(qiáng)(實(shí)現(xiàn)數(shù)據(jù)壓縮)之后再去調(diào)用真正response的方法進(jìn)行輸出,這樣就可以實(shí)現(xiàn)壓縮。
配置:
<filter>
<filter-name>GzipFilter</filter-name>
<filter-class>cn.itcast.web.filter.GzipFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
注意:這里我們需要配置FORWARD和REQUEST,用于攔截forward請(qǐng)求。因?yàn)榇蠖鄶?shù)時(shí)候我們都是轉(zhuǎn)發(fā)過(guò)來(lái)的請(qǐng)求。
4.2 包裝response對(duì)象,緩存數(shù)據(jù)到內(nèi)存
CacheFiltet.java
public class CacheFilter implements Filter {
//實(shí)際開(kāi)發(fā)中我們可以使用一些專(zhuān)業(yè)的緩存工具
private Map<String, byte[]> map = new HashMap<String, byte[]>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//1.得到用戶(hù)請(qǐng)求的uri
String uri = request.getRequestURI();
//2.看換出中有沒(méi)有uri對(duì)應(yīng)的數(shù)據(jù)
byte b[] = map.get(uri);
//3.如果有,則直接拿緩存的數(shù)據(jù)送給瀏覽器,程序返回
if(b != null){
response.getOutputStream().write(b);
return ;
}
//4.如果沒(méi)有,讓目標(biāo)資源執(zhí)行,并捕獲目標(biāo)資源的輸出
BufferResponse1 myresResponse1 = new BufferResponse1(response);
chain.doFilter(request, myresResponse1);
byte out[] = myresResponse1.getBuffer();
//5.把資源的數(shù)據(jù)以用戶(hù)請(qǐng)求的uri的關(guān)鍵字保存到緩存中
map.put(uri, out);
//6.把數(shù)據(jù)送給瀏覽器
response.getOutputStream().write(out);
}
@Override
public void destroy() {}
}
class BufferResponse1 extends HttpServletResponseWrapper{
private HttpServletResponse response;
private ByteArrayOutputStream bout = new ByteArrayOutputStream();//字節(jié)流
private PrintWriter pw;
public BufferResponse1(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new MyServletOutputStream1(bout);
}
@Override
public PrintWriter getWriter() throws IOException {
pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding()));
return pw;
}
public byte[] getBuffer(){
try{
if(pw != null){
//如果pw不為空,則我們需要關(guān)閉一下,讓其將數(shù)據(jù)從緩存寫(xiě)到底層流中去
pw.close();
}
if(bout != null){
bout.flush();
return bout.toByteArray();
}
return null;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class MyServletOutputStream1 extends ServletOutputStream{
private ByteArrayOutputStream bout ;
public MyServletOutputStream1(ByteArrayOutputStream bout) {
this.bout = bout;
}
@Override
public void write(int arg0) throws IOException {
//其實(shí)write方法有三種重載形式,但是其內(nèi)部都是調(diào)用的這種形式,所以我們只需要重載這種形式即可
this.bout.write(arg0);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener listener) {}
}
說(shuō)明:
- 對(duì)于頁(yè)面中很少更新的數(shù)據(jù),例如商品分類(lèi),為避免每次都要從數(shù)據(jù)庫(kù)查詢(xún)分類(lèi)數(shù)據(jù),因此可以把分類(lèi)數(shù)據(jù)緩存在內(nèi)存或文件中,以此來(lái)減輕數(shù)據(jù)庫(kù)壓力,提高系統(tǒng)響應(yīng)速度。相關(guān)書(shū)名直接看程序中的注釋即可,這里不再細(xì)說(shuō)。
五、動(dòng)態(tài)代理
在java里,每個(gè)對(duì)象都有一個(gè)類(lèi)與之對(duì)應(yīng)。
現(xiàn)在要生成某一個(gè)對(duì)象的代理對(duì)象,這個(gè)代理對(duì)象也要通過(guò)一個(gè)類(lèi)來(lái)生成,所以首先要編寫(xiě)用于生成代理對(duì)象的類(lèi)。
-
如何編寫(xiě)生成代理對(duì)象的類(lèi),兩個(gè)要素:
- 代理誰(shuí)
- 如何生成代理對(duì)象
代理誰(shuí)?
設(shè)計(jì)一個(gè)類(lèi)變量,以及一個(gè)構(gòu)造函數(shù),記住代理類(lèi)代理哪個(gè)對(duì)象。如何生成代理對(duì)象?
設(shè)計(jì)一個(gè)方法生成代理對(duì)象(在方法內(nèi)編寫(xiě)代碼生成代理對(duì)象是此處編程的難點(diǎn))-
java提供了一個(gè)Proxy類(lèi),調(diào)用它的newInstance方法可以生成某個(gè)對(duì)象的代理對(duì)象,使用該方法生成代理對(duì)象時(shí),需要三個(gè)參數(shù):
- 1.生成代理對(duì)象使用哪個(gè)類(lèi)裝載器
- 2.生成哪個(gè)對(duì)象的代理對(duì)象,通過(guò)接口指定
- 3.生成的代理對(duì)象的方法里干什么事,由開(kāi)發(fā)人員編寫(xiě)handler接口的實(shí)現(xiàn)來(lái)指定。
-
初學(xué)者必須理解(記住)
- Proxy類(lèi)負(fù)責(zé)創(chuàng)建代理對(duì)象時(shí),如果指定了handler(處理器),那么不管用戶(hù)調(diào)用代理對(duì)象的什么方法,該方法都是調(diào)用處理器的invoke方法。
- 由于invoke方法被調(diào)用需要三個(gè)參數(shù):代理對(duì)象、方法、方法的參數(shù),因此不管代理對(duì)象哪個(gè)方法調(diào)用處理器的invoke方法,都必須把自己所在的對(duì)象、自己(調(diào)用invoke方法的方法)、方法的參數(shù)傳遞進(jìn)來(lái)。
六、動(dòng)態(tài)代理應(yīng)用
在動(dòng)態(tài)代理技術(shù)里,由于不管用戶(hù)調(diào)用代理對(duì)象的什么方法,都是調(diào)用開(kāi)發(fā)人員編寫(xiě)的處理器的invoke方法(這相當(dāng)于invoke方法攔截到了代理對(duì)象的方法調(diào)用)。
并且,開(kāi)發(fā)人員通過(guò)invoke方法的參數(shù),才可以在攔截的同時(shí),知道用戶(hù)調(diào)用的是什么方法,因此利用這兩個(gè)特征,就可以實(shí)現(xiàn)一些特殊需求。例如:攔截用戶(hù)的訪(fǎng)問(wèn)請(qǐng)求,以檢查用戶(hù)是否有訪(fǎng)問(wèn)權(quán)限、動(dòng)態(tài)為某個(gè)對(duì)象添加額外的功能。