JSP中的Servlet及Filter
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 @Override15 public void destroy() {16 // TODO Auto-generated method stub17 18 }19 20 @Override21 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 @Override28 public void init(FilterConfig arg0) throws ServletException {29 // TODO Auto-generated method stub30 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 @Override17 public void destroy() {18 19 }20 21 @Override22 public void doFilter(ServletRequest reqeust, ServletResponse response,23 FilterChain chain) throws IOException, ServletException {24 response.getWriter().append(25 "<h1>AnotherFilter.doFilter is called!" + someParamter26 + "</h1>");27 chain.doFilter(reqeust, response);28 }29 30 @Override31 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 @Autowired17 SampleData someData;18 19 @Override20 public void destroy() {21 22 }23 24 @Override25 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 @Override34 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 = WebApplicationContextUtils43 .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 = WebApplicationContextUtils31 .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 // 需要注入的Bean16 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实例的。
共同学习,写下你的评论
评论加载中...
作者其他优质文章