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

  • 相关阅读:
    容器与线程--更新
    js生成元素的事件不执行问题
    Webpack 笔记
    思考题——爬楼梯
    正则表达式笔记
    github + travis 自动构建 vue 项目到 gitpage
    css3 动画 vs js 动画
    深度哲学对工作的启发
    yml 文件操作方法
    布尔变量在项目中的应用
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/httpservlet-and-filter-in-jsp.html
Copyright © 2020-2023  润新知