文章出處

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 }
View Code

在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>
View Code

第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 }
View Code

注意下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>
View Code

 第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 }
View Code

代碼很簡單,在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>
View Code

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 }
View Code

為了對比,再來一個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 }
View Code

這里,我們希望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>
View Code

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>
View Code

對比下剛才的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 }
View Code

關鍵在于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 }
View Code

本質上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 }
View Code

在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>
View Code

最后是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>
View Code

注:web.xml中的servlet-name節點值,必須于spring beans配置文件中的bean id一致,因為ServletProxy是根據ServletName來查找Bean實例的。

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()