asp.net中,如果開發人員想自己處理http請求響應,可以利用HttpHandler來滿足這一要求;類似的,如果要攔截所有http請求,可以使用HttpMoudle。java的web開發中,也有類似的處理機制,與HttpHandler應對的是HttpServlet,與HttpModule對應的則是Filter。
一、HttpServlet
先看一個簡單的示例:

1 package com.cnblogs.yjmyzz.servlet; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class SampleServlet extends HttpServlet { 11 12 private static final long serialVersionUID = 7065409287377444221L; 13 14 public SampleServlet(){ 15 System.out.println("SampleServlet is initialized!"); 16 } 17 18 protected void doGet(HttpServletRequest request, 19 HttpServletResponse response) throws ServletException, IOException { 20 21 response.getWriter().append("<h1>SampleServlet.doGet() is called!</h1>"); 22 23 } 24 25 protected void doPost(HttpServletRequest request, 26 HttpServletResponse response) throws ServletException, IOException { 27 28 response.getWriter() 29 .append("<h1>SampleServlet.doPost() is called!</h1>"); 30 31 } 32 33 }
在HttpServlet中,程序員得自己控制所有要在頁面上輸出的內容,類似ASP.NET HttpHandler中Response.Write(...)一樣。
自定義的Servlet必須在web.xml中注冊才能使用,參考下面的配置片段:

1 <servlet> 2 <servlet-name>Sample</servlet-name> 3 <servlet-class>com.cnblogs.yjmyzz.servlet.SampleServlet</servlet-class> 4 <load-on-startup>1</load-on-startup> 5 </servlet> 6 <servlet-mapping> 7 <servlet-name>Sample</servlet-name> 8 <url-pattern>/A/*</url-pattern> 9 </servlet-mapping>
第2行與第7行的servlet-name要一致;url-pattern表示該Servlet要攔截的url,如果寫成"/*",則表示攔截所有url請求;load-on-startup是可選節點,如果該節點值>0時,webapp一啟動就會自動實例化該Servlet,否則將延時到第一次訪問被攔截的url時,才會被實例化。
如果web.xml中同時注冊了多個Servlet,且都指定了load-on-startup,將按照load-on-startup節點值從小到大的優先級順序,依次實例化所有注冊的Servlet。
如果多個Servlet同時攔截了相同的url,則根據它們出現在web.xml中的順序,僅最后出現的Servlet具有攔截處理權。
二、Filter
還是先來一個最基本的示例

1 package com.cnblogs.yjmyzz.filter; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 public class AnotherFilter implements Filter { 13 14 @Override 15 public void destroy() { 16 // TODO Auto-generated method stub 17 18 } 19 20 @Override 21 public void doFilter(ServletRequest reqeust, ServletResponse response, 22 FilterChain chain) throws IOException, ServletException { 23 response.getWriter().append("<h1>AnotherFilter.doFilter is called!</h1>"); 24 chain.doFilter(reqeust, response); 25 } 26 27 @Override 28 public void init(FilterConfig arg0) throws ServletException { 29 // TODO Auto-generated method stub 30 31 } 32 33 }
注意下24行,開發人員自定義的處理完成后,最后記得調用chain.doFilter(reqeust, response),因為每一次http請求的完整處理通常會有很多個Filter按順序協作完成,這些Filter形成一個”鏈式結構“,這一行的作用,就是當自己的處理完成后,繼續交給Filter鏈中的下一個Filter去處理。
同樣,Filter也必須在web.xml中注冊方能使用:

1 <filter> 2 <filter-name>Filter2</filter-name> 3 <filter-class>com.cnblogs.yjmyzz.filter.AnotherFilter</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>Filter2</filter-name> 7 <url-pattern>/*</url-pattern> 8 </filter-mapping>
第2行與第6行的filter-name要保持一致;url-pattern為要攔截的url;如果一個web.xml中同時注冊多個Filter,所有這些Filter都將起作用,處理的順序按照在web.xml中出現的順序,先出現的Filter先處理。
如果web.xml中同時注冊了Servlet、Filter,且攔截的url相同時,Filter先處理,之后才輪到Servlet處理。
三、參數注入
通常在寫Servlet、Filter時,有時候需要從外界獲取一些參數,先來看下Filter的參數處理:
a) Filter基本String參數注入

1 package com.cnblogs.yjmyzz.filter; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 public class AnotherFilter implements Filter { 13 // 定義參數變量 14 private String someParamter; 15 16 @Override 17 public void destroy() { 18 19 } 20 21 @Override 22 public void doFilter(ServletRequest reqeust, ServletResponse response, 23 FilterChain chain) throws IOException, ServletException { 24 response.getWriter().append( 25 "<h1>AnotherFilter.doFilter is called!" + someParamter 26 + "</h1>"); 27 chain.doFilter(reqeust, response); 28 } 29 30 @Override 31 public void init(FilterConfig cfg) throws ServletException { 32 // 取得傳入的參數 33 someParamter = cfg.getInitParameter("someParameter"); 34 35 } 36 37 }
代碼很簡單,在init方法中接收參數即可,這個參數是從哪里傳進來的呢?看下面的web.xml配置

1 <filter> 2 <filter-name>Filter2</filter-name> 3 <filter-class>com.cnblogs.yjmyzz.filter.AnotherFilter</filter-class> 4 <init-param> 5 <param-name>someParameter</param-name> 6 <param-value>HelloWorld</param-value> 7 </init-param> 8 </filter>
init-param節點就是答案
b) Filter復雜對象的參數注入
如果要傳的參數是一個復雜對象,上面的方法就不太適合(當然:你可以把對象序列化成json字符串,然后到init中接收,再反序列,理論上也可行,但是比較感覺比較怪。)
先定義一個參數對象:

1 package com.cnblogs.yjmyzz.filter; 2 3 public class SampleData { 4 5 private String someField; 6 7 public String getSomeField() { 8 return someField; 9 } 10 11 public void setSomeField(String someField) { 12 this.someField = someField; 13 } 14 15 }
為了對比,再來一個Filter

1 package com.cnblogs.yjmyzz.filter; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 import org.springframework.beans.factory.annotation.Autowired; 13 14 public class SampleFilter implements Filter { 15 16 @Autowired 17 SampleData someData; 18 19 @Override 20 public void destroy() { 21 22 } 23 24 @Override 25 public void doFilter(ServletRequest reqeust, ServletResponse response, 26 FilterChain chain) throws IOException, ServletException { 27 response.getWriter().append( 28 "<h1>SampleFilter.doFilter is called!" 29 + someData.getSomeField() + "</h1>"); 30 chain.doFilter(reqeust, response); 31 } 32 33 @Override 34 public void init(FilterConfig filterConfig) throws ServletException { 35 36 } 37 38 public SampleData getSomeData() { 39 return someData; 40 } 41 42 public void setSomeData(SampleData someData) { 43 this.someData = someData; 44 } 45 46 }
這里,我們希望SomeFilter在運行時,能動態注入一個SomeData實例。下面是配置部分:

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 <bean id="someData" class="com.cnblogs.yjmyzz.filter.SampleData"> 7 <property name="someField" value="abc"></property> 8 </bean> 9 10 <bean id="sampleFilter" class="com.cnblogs.yjmyzz.filter.SampleFilter"> 11 <property name="someData" ref="someData"></property> 12 </bean> 13 14 </beans>
spring的xml配置中,先定義好SomeFilter的bean,然后是web.xml的Filter配置:

1 <filter> 2 <description>Filter1</description> 3 <display-name>Filter1</display-name> 4 <filter-name>Filter1</filter-name> 5 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 6 <init-param> 7 <param-name>targetBeanName</param-name> 8 <param-value>sampleFilter</param-value> 9 </init-param> 10 </filter> 11 12 <filter-mapping> 13 <filter-name>Filter1</filter-name> 14 <url-pattern>/*</url-pattern> 15 </filter-mapping>
對比下剛才的Filter配置,有幾個變化:
filter-class 換成了 org.springframework.web.filter.DelegatingFilterProxy
init-param 節點通過targetBeanName 這個參數名,將sampleFilter bean動態注入
再來看看Servlet的參數注入,spring并沒有提供類似DelegatingServletProxy的代理類,所以只能自己動手了,下面是二種常見做法:
a) 通過init方法,實現Servlet的Spring bean注入

1 package com.cnblogs.yjmyzz.servlet; 2 3 import java.io.IOException; 4 5 import javax.servlet.*; 6 import javax.servlet.http.*; 7 import org.springframework.web.context.WebApplicationContext; 8 import org.springframework.web.context.support.WebApplicationContextUtils; 9 10 import com.cnblogs.yjmyzz.filter.SampleData; 11 12 public class SampleServlet extends HttpServlet { 13 14 private static final long serialVersionUID = 7065409287377444221L; 15 16 SampleData someData; 17 18 public SampleServlet() { 19 System.out.println("SampleServlet is initialized!"); 20 } 21 22 protected void doGet(HttpServletRequest request, 23 HttpServletResponse response) throws ServletException, IOException { 24 25 response.getWriter().append( 26 "<h1>SampleServlet.doGet() is called!" 27 + someData.getSomeField() + "</h1>"); 28 29 } 30 31 protected void doPost(HttpServletRequest request, 32 HttpServletResponse response) throws ServletException, IOException { 33 34 response.getWriter().append( 35 "<h1>SampleServlet.doPost() is called!</h1>"); 36 37 } 38 39 public void init() throws ServletException { 40 super.init(); 41 ServletContext servletContext = this.getServletContext(); 42 WebApplicationContext ctx = WebApplicationContextUtils 43 .getWebApplicationContext(servletContext); 44 someData = ctx.getBean("someData", SampleData.class); 45 } 46 }
關鍵在于init方法,通過Spring的WebApplicationContext拿到上下文,然后手動去獲取bean實例
b) 自己實現ServletProxy,實現注入
先定義ServletProxy代理類:

1 package com.cnblogs.yjmyzz.servlet; 2 3 import java.io.IOException; 4 5 import javax.servlet.*; 6 import javax.servlet.http.HttpServlet; 7 8 import org.springframework.web.context.WebApplicationContext; 9 import org.springframework.web.context.support.WebApplicationContextUtils; 10 11 public class HttpServletProxy extends HttpServlet { 12 13 private static final long serialVersionUID = 4358391761577767574L; 14 15 private String targetBean; 16 private HttpServlet proxy; 17 18 public void service(ServletRequest req, ServletResponse res) 19 throws ServletException, IOException { 20 proxy.service(req, res); 21 } 22 23 public void init() throws ServletException { 24 this.targetBean = getServletName(); 25 getServletBean(); 26 proxy.init(getServletConfig()); 27 } 28 29 private void getServletBean() { 30 WebApplicationContext wac = WebApplicationContextUtils 31 .getRequiredWebApplicationContext(getServletContext()); 32 this.proxy = (HttpServlet) wac.getBean(targetBean); 33 } 34 35 }
本質上ServletProxy也是一個Servlet,在init方法中,通過動態獲取servletName,利用Spring的WebApplicationContextt得到真正需要的Servlet Bean實例并保存在proxy變量中,最終對http執行處理的(即:調用service方法的),是proxy變量所指向的Servlet Bean實例。
定義真正需要使用的Servlet

1 package com.cnblogs.yjmyzz.servlet; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 import com.cnblogs.yjmyzz.filter.SampleData; 10 11 public class AnotherServlet extends HttpServlet { 12 13 private static final long serialVersionUID = -3797187540470927379L; 14 15 // 需要注入的Bean 16 SampleData someData; 17 18 public AnotherServlet() { 19 System.out.println("AnotherServlet is initialized!"); 20 } 21 22 protected void doGet(HttpServletRequest request, 23 HttpServletResponse response) throws ServletException, IOException { 24 25 response.getWriter().append( 26 "<h1>AnotherServlet.doGet() is called!" 27 + someData.getSomeField() + "</h1>"); 28 29 } 30 31 protected void doPost(HttpServletRequest request, 32 HttpServletResponse response) throws ServletException, IOException { 33 34 response.getWriter().append( 35 "<h1>AnotherServlet.doPost() is called!</h1>"); 36 37 } 38 39 public void setSomeData(SampleData someData) { 40 this.someData = someData; 41 } 42 43 }
在spring的beans配置文件中,配置該Servlet Bean

1 <bean id="someData" class="com.cnblogs.yjmyzz.filter.SampleData"> 2 <property name="someField" value="abc"></property> 3 </bean> 4 5 <bean id="anotherServlet" class="com.cnblogs.yjmyzz.servlet.AnotherServlet"> 6 <property name="someData" ref="someData"></property> 7 </bean>
最后是web.xml配置

1 <servlet> 2 <servlet-name>anotherServlet</servlet-name> 3 <servlet-class>com.cnblogs.yjmyzz.servlet.HttpServletProxy</servlet-class> 4 <load-on-startup>1</load-on-startup> 5 </servlet> 6 <servlet-mapping> 7 <servlet-name>anotherServlet</servlet-name> 8 <url-pattern>/A/*</url-pattern> 9 </servlet-mapping>
注:web.xml中的servlet-name節點值,必須于spring beans配置文件中的bean id一致,因為ServletProxy是根據ServletName來查找Bean實例的。
文章列表