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实例的。

JSP中的Servlet及Filter的更多相关文章

  1. 从零开始的Spring Boot(2、在Spring Boot中整合Servlet、Filter、Listener的方式)

    在Spring Boot中整合Servlet.Filter.Listener的方式 写在前面 从零开始的Spring Boot(1.搭建一个Spring Boot项目Hello World):http ...

  2. SpringBoot中使用Servlet,Filter,Listener

    项目最近在替换之前陈旧的框架,改用SpringBoot进行重构,初接触,暂时还没有用到Servlet,Filter,Listener的地方,但在之前回顾Servlet的生命周期时,https://ww ...

  3. jsp中使用Servlet查询SQLSERVER数据库中的表的信息,并且打印在屏幕上

    jsp中使用Servlet查询SQLSERVER数据库中的表的信息,并且打印在屏幕上 1.JavaBean的使用 package com.zheng; public class BookBean { ...

  4. Spring Boot中使用Servlet与Filter

    在Spring Boot中使用Servlet,根据Servlet注册方式的不同,有两种使用方式.若使用的是Servlet3.0+版本,则两种方式均可使用:若使用的是Servlet2.5版本,则只能使用 ...

  5. Web.xml中设置Servlet和Filter时的url-pattern匹配规则

    一.servlet容器对url的匹配过程: 当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://loca ...

  6. IDEA使用技巧,如何在JSP中创建Servlet“小程序”

    步骤 1.新建一个java类,实现Servlet接口 2.实现接口中的抽象方法: 3.在web.xml文件中配置好servlet <web-app ......> <servlet& ...

  7. SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - 自定义Servlet、Filter、Listener是如何注册到Tomcat容器中的?(SpringBoot实现SpringMvc的原理)

    上一篇我们讲了SpringBoot中Tomcat的启动过程,本篇我们接着讲在SpringBoot中如何向Tomcat中添加Servlet.Filter.Listener 自定义Servlet.Filt ...

  8. Servlet、Filter、Listener、Interceptor

    首先,JSP/Servlet规范中定义了Servlet.Filter.Listener这三种角色,并没有定义Interceptor这个角 色,Interceptor是某些MVC框架中的角色,比如Str ...

  9. j2ee Servlet、Filter、Listener

    首先,JSP/Servlet规范中定义了Servlet.Filter.Listener这三种角色,并没有定义Interceptor这个角色,Interceptor是某些MVC框架中的角色,比如Stru ...

随机推荐

  1. 1.4 基础知识——GP2.2 计划 与 GP2.8 计划跟踪

    摘要: CMMI有计划(PP)及计划跟踪(PMC)两个PA,而某一个PA又有GP2.2计划及GP2.8计划跟踪两个GP,看上去是挺“神奇”也挺让人“困惑”的事情. 正文: GP2.2 Establis ...

  2. Newtonsoft.Json 把对象转换成json字符串

    var resultJson = new { records = rowCount, page = pageindex, //总页数=(总页数+页大小-1)/页大小 total = (rowCount ...

  3. BFS、DFS与选课问题(拓扑排序)

    1选课问题 Leetcode上有这样一道题:有代号0,1,2……n-1的n门课程.其中选择某些课程需要另一些课程作为前提条件.用一组pair来表示这些条件:[1,0],[1,2],表示如果要选修课程1 ...

  4. WPF学习之路(五) 实例:写字板(续)

    WordPad 2.0 上一期实现了一虽然建议但是功能比较全面的Wordpad程序,但是程序代码略显繁琐,这一期更新改进版. MainWindows.xaml 添加 <Window.Comman ...

  5. log的简单说明

    log的简单说明 @(NS3相关)[core][log] NS3中的日志功能是非常完善与灵活,大家有需要显示一些调试或者警告信息时最好使用log,不再使用标准输入来输出中间信息. 头文件:ns3/lo ...

  6. Stanford coursera Andrew Ng 机器学习课程编程作业(Exercise 2)及总结

    Exercise 1:Linear Regression---实现一个线性回归 关于如何实现一个线性回归,请参考:http://www.cnblogs.com/hapjin/p/6079012.htm ...

  7. expr

    将变量的值当作数值处理而不是字符串,shell支持6种算术运算符:加+, 减-,乘\*,除/, 取余%, 括号\( \),每种算术运算符的两边都要有空格 $echo "Res = $(exp ...

  8. WIN32 API编程之 tap顺序

    用CreateWindow 函数创建的控件,如果想使用tap键切换,最简单的做法是:主窗口有WS_EX_CONTROLPARENT扩展属性,控件有WS_TAPSTOP属性. 然后最重要的是,在处理消息 ...

  9. DevOps Workshop 研发运维一体化第一场(微软亚太研发集团总部)

    准备了近两周,写了大量的操作手册,设计了大量的动手实验场景,终于在中关村的微软大厦完成了两天的DevOps培训. 最初报名160人,按照之前的培训经验,一般能到一半就不错了,没想到这次现场登记人员就超 ...

  10. 玩转Windows Azure存储服务——网盘

    存储服务是除了计算服务之外最重要的云服务之一.说到云存储,大家可以想到很多产品,例如:AWS S3,Google Drive,百度云盘...而在Windows Azure中,存储服务却是在默默无闻的工 ...