1. 程式人生 > >Web filter中如何引用Spring的bean

Web filter中如何引用Spring的bean

今天寫了寫了陣微信公眾號玩,Spring+Struts搭的,有個需求是簽名驗證,需要在本地快取一個使用者ticket,而獲取ticket的邏輯在某個spring service中實現的。

對所有的jsp頁面,想要注入對應的資訊,而且不配置struts的話,想了想,最好用filter。實現了filter之後,準備注入service,這個時候啟動webproject發現,service為null.怎麼回事?為什麼spring環境中明明通過@Component注入了對應的FIlter,並且通過@Autowired注入了對應的Service,為何Web請求的時候獲取不到對應的Service?

應該有這麼幾個問題需要我搞清楚:1.web 載入的時候有filter,listener,servlet等元素,他們是如何載入的,順序如何,是不是filter的環境spring 管不到。2.我的配置是如何啟動?3.應該如何解決這個問題,即如何在filter中使用service?

一、Filter,Listener,Servlet對比

首先這三個東東來自web.xml,那麼web.xml是幹嘛的?

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
</web-app>

第一行給出了XML頭,用來定義字元編碼和xml版本,一般的xml檔案都有這個。緊接著是web-app元素,這個元素的xmlns,即xml namespace是定義該元素所支援的子元素的。可以看出,在web.xml中是namespace是來自於sun的javaee頁面。


可以看出上面配置的,是06年釋出的2.5標準。而最新的已經是13年4月釋出的3.1。我看了下web-app_3.1.xsd,其中引入了

</pre><pre name="code" class="html"><xsd:include schemaLocation="web-common_3_1.xsd"/>

這個檔案中定義這些元素:

 <xsd:element name="filter"
                   type="javaee:filterType"/>
      <xsd:element name="filter-mapping"
                   type="javaee:filter-mappingType"/>
      <xsd:element name="listener"
                   type="javaee:listenerType"/>
      <xsd:element name="servlet"
                   type="javaee:servletType"/>
      <xsd:element name="servlet-mapping"
                   type="javaee:servlet-mappingType"/>
可以看到這幾個元素都定義了name和type,這個type又是在哪兒定義的呢?原來web-common_3_1.xsd又引入了javaee.xsd和jsp_2_3.xsd。在javaee.xsd中,定義了這些type。

1.filterType和filterMappingTYpe的定義(在web-common_3.1.xsd中):

  <xsd:complexType name="filterType">
    <xsd:annotation>
      <xsd:documentation>

<span style="font-size:14px;"><span style="color:#3366ff;">        <strong>The filterType is used to declare a filter in the web
        application. The filter is mapped to either a servlet or a
        URL pattern in the filter-mapping element, using the
        filter-name value to reference.</strong></span><span style="color:#ff0000;"> </span></span>Filters can access the
        initialization parameters declared in the deployment
        descriptor at runtime via the FilterConfig interface.
        
        Used in: web-app
        
      </xsd:documentation>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:group ref="javaee:descriptionGroup"/>
      <xsd:element name="filter-name"
                   type="javaee:filter-nameType"/>
      <xsd:element name="filter-class"
                   type="javaee:fully-qualified-classType"
                   minOccurs="0"
                   maxOccurs="1">
        <xsd:annotation>
          <xsd:documentation>

            The fully qualified classname of the filter.
            
          </xsd:documentation>
        </xsd:annotation>
      </xsd:element>
      <xsd:element name="async-supported"
                   type="javaee:true-falseType"
                   minOccurs="0"/>
      <xsd:element name="init-param"
                   type="javaee:param-valueType"
                   minOccurs="0"
                   maxOccurs="unbounded">
        <xsd:annotation>
          <xsd:documentation>

            The init-param element contains a name/value pair as
            an initialization param of a servlet filter
            
          </xsd:documentation>
        </xsd:annotation>
      </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="id"
                   type="xsd:ID"/>
  </xsd:complexType>
<xsd:complexType name="filter-mappingType">
    <xsd:annotation>
      <xsd:documentation>

        Declaration of the filter mappings in this web
        application is done by using filter-mappingType. 
        The container uses the filter-mapping
      <span style="background-color: rgb(255, 255, 255);"><span style="color:#000099;">  <strong>declarations to decide which filters to apply to a request,
        and in what order.</strong> </span></span>The container matches the request URI to
        a Servlet in the normal way. To determine which filters to
        apply it matches filter-mapping declarations either on
        servlet-name, or on url-pattern for each filter-mapping
        element, depending on which style is used. The order in
        which filters are invoked is the order in which
        filter-mapping declarations that match a request URI for a
        servlet appear in the list of filter-mapping elements.The
        filter-name value must be the value of the filter-name
        sub-elements of one of the filter declarations in the
        deployment descriptor.
        
      </xsd:documentation>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:element name="filter-name"
                   type="javaee:filter-nameType"/>
      <xsd:choice minOccurs="1"
                  maxOccurs="unbounded">
        <xsd:element name="url-pattern"
                     type="javaee:url-patternType"/>
        <xsd:element name="servlet-name"
                     type="javaee:servlet-nameType"/>
      </xsd:choice>
      <xsd:element name="dispatcher"
                   type="javaee:dispatcherType"
                   minOccurs="0"
                   maxOccurs="5"/>
    </xsd:sequence>
    <xsd:attribute name="id"
                   type="xsd:ID"/>
  </xsd:complexType>


2.listener定義在javaee_7.xsd.
  <xsd:complexType name="listenerType">
    <xsd:annotation>
      <xsd:documentation>

        <span style="background-color: rgb(255, 255, 255);"><span style="color:#000099;"><strong>The listenerType indicates the deployment properties for a web
        application listener bean.</strong></span></span>
        
      </xsd:documentation>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:group ref="javaee:descriptionGroup"/>
      <xsd:element name="listener-class"
                   type="javaee:fully-qualified-classType">
        <xsd:annotation>
          <xsd:documentation>

            The listener-class element declares a class in the
            application must be registered as a web
            application listener bean. The value is the fully
            qualified classname of the listener class.
            
          </xsd:documentation>
        </xsd:annotation>
      </xsd:element>
    </xsd:sequence>
    <xsd:attribute name="id"
                   type="xsd:ID"/>
  </xsd:complexType>

3.Servlet定義在web-commmon_3.1.xsd中,
 <xsd:complexType name="servletType">
    <xsd:annotation>
      <xsd:documentation>

        <span style="background-color: rgb(255, 255, 255);"><span style="color:#000099;"><strong>The servletType is used to declare a servlet.</strong></span></span>
        It contains the declarative data of a
        servlet. If a jsp-file is specified and the load-on-startup
        element is present, then the JSP should be precompiled and
        loaded.
        
        Used in: web-app
        
      </xsd:documentation>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:group ref="javaee:descriptionGroup"/>
      <xsd:element name="servlet-name"
                   type="javaee:servlet-nameType"/>
      <xsd:choice minOccurs="0"
                  maxOccurs="1">
        <xsd:element name="servlet-class"
                     type="javaee:fully-qualified-classType">
          <xsd:annotation>
            <xsd:documentation>

              The servlet-class element contains the fully
              qualified class name of the servlet.
              
            </xsd:documentation>
          </xsd:annotation>
        </xsd:element>
        <xsd:element name="jsp-file"
                     type="javaee:jsp-fileType"/>
      </xsd:choice>
      <xsd:element name="init-param"
                   type="javaee:param-valueType"
                   minOccurs="0"
                   maxOccurs="unbounded"/>
      <xsd:element name="load-on-startup"
                   type="javaee:load-on-startupType"
                   minOccurs="0">
        <xsd:annotation>
          <xsd:documentation>

            The load-on-startup element indicates that this
            servlet should be loaded (instantiated and have
            its init() called) on the startup of the web
            application. The optional contents of these
            element must be an integer indicating the order in
            which the servlet should be loaded. If the value
            is a negative integer, or the element is not
            present, the container is free to load the servlet
            whenever it chooses. If the value is a positive
            integer or 0, the container must load and
            initialize the servlet as the application is
            deployed. The container must guarantee that
            servlets marked with lower integers are loaded
            before servlets marked with higher integers. The
            container may choose the order of loading of
            servlets with the same load-on-start-up value.
            
          </xsd:documentation>
        </xsd:annotation>
      </xsd:element>
      <xsd:element name="enabled"
                   type="javaee:true-falseType"
                   minOccurs="0"/>
      <xsd:element name="async-supported"
                   type="javaee:true-falseType"
                   minOccurs="0"/>
      <xsd:element name="run-as"
                   type="javaee:run-asType"
                   minOccurs="0"/>
      <xsd:element name="security-role-ref"
                   type="javaee:security-role-refType"
                   minOccurs="0"
                   maxOccurs="unbounded"/>
      <xsd:element name="multipart-config"
                   type="javaee:multipart-configType"
                   minOccurs="0"
                   maxOccurs="1"/>
    </xsd:sequence>
    <xsd:attribute name="id"
                   type="xsd:ID"/>
  </xsd:complexType>


<!-- **************************************************** -->

  <xsd:complexType name="servlet-mappingType">
    <xsd:annotation>
      <xsd:documentation>

        The servlet-mappingType defines a mapping between a
        servlet and a url pattern. 
        
        Used in: web-app
        
      </xsd:documentation>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:element name="servlet-name"
                   type="javaee:servlet-nameType"/>
      <xsd:element name="url-pattern"
                   type="javaee:url-patternType"
                   minOccurs="1"
                   maxOccurs="unbounded"/>
    </xsd:sequence>
    <xsd:attribute name="id"
                   type="xsd:ID"/>
  </xsd:complexType>


<!-- **************************************************** -->

  <xsd:complexType name="servlet-nameType">
    <xsd:annotation>
      <xsd:documentation>

        The servlet-name element contains the canonical name of the
        servlet. Each servlet name is unique within the web
        application.
        
      </xsd:documentation>
    </xsd:annotation>
    <xsd:simpleContent>
      <xsd:extension base="javaee:nonEmptyStringType"/>
    </xsd:simpleContent>
  </xsd:complexType>

看完這3個元素的定義,總結一句話,他們是幹啥的,servlet呢是定義servlet的,listener呢是定義listener的,filter是定義filter的。扯了半天蛋,其實要了解web.xml中的這幾個元素,還是得看看其它資料,先看看百度百科吧,Servlet(http://baike.baidu.com/link?url=PK8201TxZwwQZKAwLiu9Mob6rPo2AnApub5uT8PCPoigifBFGdGyFMQeyuBL3e6KWdTpQkxpxZwPJGvwgHLF8K),Filter(http://baike.baidu.com/link?url=Zb9NDPAOIgkyy2OvquA7pLapG58QlM0O1fTBlIIVdFk8NoJXhuC2fKJnOnksnNaf62T7936X8isYIvDHWp59iq).然後就知道了,servlet可以看成一個簡單的服務提供者,filter則是一個在請求到達服務提供者之前對request進行預處理的過濾器,而listener則是在web應用啟動的時候進行初始化,然後攔截相關的請求,只有當應用重新部署或者停止的時候才幹掉。

這幾個元素都是通過實現幾個接口才能拓展,這幾個介面分別是:

1.FIlter.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package javax.servlet;

import java.io.IOException;

/**
 * A filter is an object that performs filtering tasks on either the
 * request to a resource (a servlet or static content), or on the response
 * from a resource, or both.
 * 
 * <p>Filters perform filtering in the <code>doFilter</code> method.
 * Every Filter has access to a FilterConfig object from which it can obtain
 * its initialization parameters, and a reference to the ServletContext which
 * it can use, for example, to load resources needed for filtering tasks.
 *
 * <p>Filters are configured in the deployment descriptor of a web
 * application.
 *
 * <p>Examples that have been identified for this design are:
 * <ol>
 * <li>Authentication Filters
 * <li>Logging and Auditing Filters
 * <li>Image conversion Filters
 * <li>Data compression Filters
 * <li>Encryption Filters
 * <li>Tokenizing Filters
 * <li>Filters that trigger resource access events
 * <li>XSL/T filters
 * <li>Mime-type chain Filter
 * </ol>
 *
 * @since Servlet 2.3
 */

public interface Filter {

    /** 
     * Called by the web container to indicate to a filter that it is
     * being placed into service.
     *
     * <p>The servlet container calls the init
     * method exactly once after instantiating the filter. The init
     * method must complete successfully before the filter is asked to do any
     * filtering work.
     * 
     * <p>The web container cannot place the filter into service if the init
     * method either
     * <ol>
     * <li>Throws a ServletException
     * <li>Does not return within a time period defined by the web container
     * </ol>
     */
    public void init(FilterConfig filterConfig) throws ServletException;
	
	
    /**
     * The <code>doFilter</code> method of the Filter is called by the
     * container each time a request/response pair is passed through the
     * chain due to a client request for a resource at the end of the chain.
     * The FilterChain passed in to this method allows the Filter to pass
     * on the request and response to the next entity in the chain.
     *
     * <p>A typical implementation of this method would follow the following
     * pattern:
     * <ol>
     * <li>Examine the request
     * <li>Optionally wrap the request object with a custom implementation to
     * filter content or headers for input filtering
     * <li>Optionally wrap the response object with a custom implementation to
     * filter content or headers for output filtering
     * <li>
     * <ul>
     * <li><strong>Either</strong> invoke the next entity in the chain
     * using the FilterChain object
     * (<code>chain.doFilter()</code>),
     * <li><strong>or</strong> not pass on the request/response pair to
     * the next entity in the filter chain to
     * block the request processing
     * </ul>
     * <li>Directly set headers on the response after invocation of the
     * next entity in the filter chain.
     * </ol>
     */
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException;


    /**
     * Called by the web container to indicate to a filter that it is being
     * taken out of service.
     *
     * <p>This method is only called once all threads within the filter's
     * doFilter method have exited or after a timeout period has passed.
     * After the web container calls this method, it will not call the
     * doFilter method again on this instance of the filter.
     *
     * <p>This method gives the filter an opportunity to clean up any
     * resources that are being held (for example, memory, file handles,
     * threads) and make sure that any persistent state is synchronized
     * with the filter's current state in memory.
     */
    public void destroy();
}

Servlet.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package javax.servlet;

import java.io.IOException;


/**
 * Defines methods that all servlets must implement.
 *
 * <p>A servlet is a small Java program that runs within a Web server.
 * Servlets receive and respond to requests from Web clients,
 * usually across HTTP, the HyperText Transfer Protocol. 
 *
 * <p>To implement this interface, you can write a generic servlet
 * that extends
 * <code>javax.servlet.GenericServlet</code> or an HTTP servlet that
 * extends <code>javax.servlet.http.HttpServlet</code>.
 *
 * <p>This interface defines methods to initialize a servlet,
 * to service requests, and to remove a servlet from the server.
 * These are known as life-cycle methods and are called in the
 * following sequence:
 * <ol>
 * <li>The servlet is constructed, then initialized with the <code>init</code> method.
 * <li>Any calls from clients to the <code>service</code> method are handled.
 * <li>The servlet is taken out of service, then destroyed with the 
 * <code>destroy</code> method, then garbage collected and finalized.
 * </ol>
 *
 * <p>In addition to the life-cycle methods, this interface
 * provides the <code>getServletConfig</code> method, which the servlet 
 * can use to get any startup information, and the <code>getServletInfo</code>
 * method, which allows the servlet to return basic information about itself,
 * such as author, version, and copyright.
 *
 * @author 	Various
 *
 * @see 	GenericServlet
 * @see 	javax.servlet.http.HttpServlet
 *
 */


public interface Servlet {

    /**
     * Called by the servlet container to indicate to a servlet that the 
     * servlet is being placed into service.
     *
     * <p>The servlet container calls the <code>init</code>
     * method exactly once after instantiating the servlet.
     * The <code>init</code> method must complete successfully
     * before the servlet can receive any requests.
     *
     * <p>The servlet container cannot place the servlet into service
     * if the <code>init</code> method
     * <ol>
     * <li>Throws a <code>ServletException</code>
     * <li>Does not return within a time period defined by the Web server
     * </ol>
     *
     *
     * @param config			a <code>ServletConfig</code> object 
     *					containing the servlet's
     * 					configuration and initialization parameters
     *
     * @exception ServletException 	if an exception has occurred that
     *					interferes with the servlet's normal
     *					operation
     *
     * @see 				UnavailableException
     * @see 				#getServletConfig
     *
     */

    public void init(ServletConfig config) throws ServletException;
    
    

    /**
     *
     * Returns a {@link ServletConfig} object, which contains
     * initialization and startup parameters for this servlet.
     * The <code>ServletConfig</code> object returned is the one 
     * passed to the <code>init</code> method. 
     *
     * <p>Implementations of this interface are responsible for storing the 
     * <code>ServletConfig</code> object so that this 
     * method can return it. The {@link GenericServlet}
     * class, which implements this interface, already does this.
     *
     * @return		the <code>ServletConfig</code> object
     *			that initializes this servlet
     *
     * @see 		#init
     *
     */

    public ServletConfig getServletConfig();
    
    

    /**
     * Called by the servlet container to allow the servlet to respond to 
     * a request.
     *
     * <p>This method is only called after the servlet's <code>init()</code>
     * method has completed successfully.
     * 
     * <p>  The status code of the response always should be set for a servlet 
     * that throws or sends an error.
     *
     * 
     * <p>Servlets typically run inside multithreaded servlet containers
     * that can handle multiple requests concurrently. Developers must 
     * be aware to synchronize access to any shared resources such as files,
     * network connections, and as well as the servlet's class and instance 
     * variables. 
     * More information on multithreaded programming in Java is available in 
     * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
     * the Java tutorial on multi-threaded programming</a>.
     *
     *
     * @param req 	the <code>ServletRequest</code> object that contains
     *			the client's request
     *
     * @param res 	the <code>ServletResponse</code> object that contains
     *			the servlet's response
     *
     * @exception ServletException 	if an exception occurs that interferes
     *					with the servlet's normal operation 
     *
     * @exception IOException 		if an input or output exception occurs
     *
     */

    public void service(ServletRequest req, ServletResponse res)
	throws ServletException, IOException;
	
	

    /**
     * Returns information about the servlet, such
     * as author, version, and copyright.
     * 
     * <p>The string that this method returns should
     * be plain text and not markup of any kind (such as HTML, XML,
     * etc.).
     *
     * @return 		a <code>String</code> containing servlet information
     *
     */

    public String getServletInfo();
    
    

    /**
     *
     * Called by the servlet container to indicate to a servlet that the
     * servlet is being taken out of service.  This method is
     * only called once all threads within the servlet's
     * <code>service</code> method have exited or after a timeout
     * period has passed. After the servlet container calls this 
     * method, it will not call the <code>service</code> method again
     * on this servlet.
     *
     * <p>This method gives the servlet an opportunity 
     * to clean up any resources that are being held (for example, memory,
     * file handles, threads) and make sure that any persistent state is
     * synchronized with the servlet's current state in memory.
     *
     */

    public void destroy();
}


ServletContextListener.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package javax.servlet;

import java.util.EventListener;

/** 
 * Interface for receiving notification events about ServletContext
 * lifecycle changes.
 *
 * <p>In order to receive these notification events, the implementation
 * class must be either declared in the deployment descriptor of the web
 * application, annotated with {@link javax.servlet.annotation.WebListener},
 * or registered via one of the addListener methods defined on
 * {@link ServletContext}.
 *
 * <p>Implementations of this interface are invoked at their
 * {@link #contextInitialized} method in the order in which they have been
 * declared, and at their {@link #contextDestroyed} method in reverse
 * order.
 *
 * @see ServletContextEvent
 *
 * @since Servlet 2.3
 */
public interface ServletContextListener extends EventListener {

    /**
     * Receives notification that the web application initialization
     * process is starting.
     *
     * <p>All ServletContextListeners are notified of context
     * initialization before any filters or servlets in the web
     * application are initialized.
     *
     * @param sce the ServletContextEvent containing the ServletContext
     * that is being initialized
     */
    public void contextInitialized(ServletContextEvent sce);

    /**
     * Receives notification that the ServletContext is about to be
     * shut down.
     *
     * <p>All servlets and filters will have been destroyed before any
     * ServletContextListeners are notified of context
     * destruction.
     *
     * @param sce the ServletContextEvent containing the ServletContext
     * that is being destroyed
     */
    public void contextDestroyed(ServletContextEvent sce);
}
這三個介面都在package javax.servlet下。

好吧,差不多該看看起初提出的問題了,web 載入的時候有filter,listener,servlet等元素,他們是如何載入的,順序如何,是不是filter的環境spring 管不到?

Listener的載入是在應用一開始啟動的時候,多個listener之間是沒有順序的,如果有多個listener需要順序載入,可以考慮把多個listener合併成一個。(eg:http://www.iteye.com/problems/33443)Filter的載入是按照在web.xml中的先後順序的,其中的init方法,按照的是filter的順序,doFilter方法是按照filter-mapping的順序,如果多個filter被一次請求匹配到,則是按照一個filter棧的邏輯進行呼叫。Servlet的載入順序則沒有前後之別,是請求匹配到哪個就進行哪個的載入。 它們3個之間的順序關係是,可以看這個(http://www.oschina.net/code/snippet_52678_44994),啟動-->listener-->filter-->servlet--->訪問-->filter-->servlet-->關閉-->servlet--->filter-->listener。

那麼spring bean是否可以在filter中載入到?

從上面的啟動順序可以看出,filter呼叫的時候,Spring是通過listener進行載入的,肯定是已經啟動了。那麼為什麼我直接把Filter新增Spring的@Component註解,通過@Autowired卻載入不到呢?這是由於,在spring對對應的bean進行掃描的時候,filter還沒有執行初始化,filter的載入是在listener之後的,那麼想要在filter中載入到spring的環境,那麼可以通過init方法進行注入,或者在doFilter的時候進行注入(這樣成本較高,每次都得注入)。具體實現在第三部分。

二、我的配置是如何啟動的。

程式碼:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<!-- 載入SPRING配置檔案 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/spring.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- STRUTS2配置 -->
	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
		<init-param>
		    <param-name>config</param-name>  
        	<param-value>struts-default.xml,struts-plugin.xml,struts2/struts.xml,struts2/struts-redis.xml</param-value>  
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>*</url-pattern>
	</filter-mapping>
	
	<!-- 過濾器可以作用於jsp而攔截器只能攔截action. -->
	<filter>
        <filter-name> wechat_web_filter</filter-name>
        <filter-class>
        	com.marsyoung.filter.WeChatJspFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>wechat_web_filter</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
<pre name="code" class="plain"><span style="font-family: Arial, Helvetica, sans-serif;">	<!-- 超時時間,半小時 --></span>
<session-config> <session-timeout>30</session-timeout> </session-config><welcome-file-list><welcome-file>/jsp/index.jsp</welcome-file></welcome-file-list></web-app>

日誌:
九月 01, 2015 2:57:44 下午 org.apache.coyote.AbstractProtocol init
資訊: Initializing ProtocolHandler ["http-nio-8080"]
九月 01, 2015 2:57:44 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
資訊: Using a shared selector for servlet write/read
九月 01, 2015 2:57:44 下午 org.apache.coyote.AbstractProtocol init
資訊: Initializing ProtocolHandler ["ajp-nio-8009"]
九月 01, 2015 2:57:44 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
資訊: Using a shared selector for servlet write/read
九月 01, 2015 2:57:44 下午 org.apache.catalina.startup.Catalina load
資訊: Initialization processed in 998 ms
九月 01, 2015 2:57:44 下午 org.apache.catalina.core.StandardService startInternal
資訊: Starting service Catalina
九月 01, 2015 2:57:44 下午 org.apache.catalina.core.StandardEngine startInternal
資訊: Starting Servlet Engine: Apache Tomcat/8.0.24
九月 01, 2015 2:57:45 下午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
資訊: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [105] milliseconds.
九月 01, 2015 2:57:52 下午 org.apache.jasper.servlet.TldScanner scanJars
資訊: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
九月 01, 2015 2:57:52 下午 org.apache.catalina.core.ApplicationContext log
資訊: No Spring WebApplicationInitializer types detected on classpath
九月 01, 2015 2:57:52 下午 org.apache.catalina.core.ApplicationContext log
資訊:<strong> <span style="color:#ff0000;">Initializing Spring root WebApplicationContext</span></strong>
2015-09-01 14:57:56.812  INFO Program init successful.
九月 01, 2015 2:57:58 下午 org.apache.coyote.AbstractProtocol start
資訊: Starting ProtocolHandler ["http-nio-8080"]
九月 01, 2015 2:57:58 下午 org.apache.coyote.AbstractProtocol start
資訊: Starting ProtocolHandler ["ajp-nio-8009"]
九月 01, 2015 2:57:58 下午 org.apache.catalina.startup.Catalina start
資訊: Server startup in 13985 ms

額,日誌中只打出了spring listener進行載入的這一句,前面是tomcat進行啟動。其實啟動過程就是,載入那個context-param,然後載入listener,然後listener中載入spring.xm的配置,引入了其中配置的資料來源bean,Service Bean,以及Struts Action Bean。之後在載入Filter。

三、如何寫在Filter中載入Spring環境。

package com.marsyoung.filter;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.Map.Entry;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.marsyoung.constants.WeiXinConstants;
import com.marsyoung.service.WeChatService;

/**
 * 微信驗證過濾器
 * 
 * @author Mars
 * 
 */
public class WeChatJspFilter implements Filter {

	Logger logger = LoggerFactory.getLogger(WeChatJspFilter.class);
	private static final String EQUAL_SIGN = "=";
	private static final String PLUS_SIGN = "+";
	private static final String AND = "&";

	WeChatService weChatService;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		ServletContext servletContext = filterConfig.getServletContext();
		WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
		weChatService = wac.getBean(WeChatService.class);
	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		// HttpSession session = request.getSession();
		String timestamp = (System.currentTimeMillis() + "").substring(0, 10);// 時間戳
		String nonce = create_nonce_str();
		String createSignature = null;
		try {
			createSignature = weChatService.getJsapiTicket();
		} catch (Exception e) {
			logger.info("獲取簽名失敗,{}", e);
		}
		request.setAttribute("signature", createSignature);// 這個值得設定應該放到Action中去。
		request.setAttribute("timestamp", timestamp);
		request.setAttribute("appId", WeiXinConstants.WeiXin_AppID);
		request.setAttribute("nonce", nonce);
		log(request, response);
		chain.doFilter(request, response);
	}

	private void log(HttpServletRequest request, HttpServletResponse response) {
		String remoteHost = request.getHeader("x-real-ip"); // 獲取客戶端的主機名
		if (remoteHost == null) {
			remoteHost = "“沒有獲取到客戶端IP”";
		}
		String requestURL = request.getRequestURL().toString(); // 獲取客戶端請求的URL
		Map<String, String[]> paramsMap = request.getParameterMap(); // 獲取所有的請求引數

		/*
		 * 獲取所有引數的名值對資訊的字串表示,儲存在變數paramsStr中
		 */
		StringBuilder paramsStrSb = new StringBuilder();
		if (paramsMap != null && paramsMap.size() > 0) {
			Set<Entry<String, String[]>> paramsSet = paramsMap.entrySet();
			for (Entry<String, String[]> param : paramsSet) {
				StringBuilder paramStrSb = new StringBuilder();
				String paramName = param.getKey(); // 引數的名字
				String[] paramValues = param.getValue(); // 引數的值
				if (paramValues.length == 1) { // 引數只有一個值,絕大多數情況
					paramStrSb.append(paramName).append(EQUAL_SIGN).append(paramValues[0]);
				} else {
					paramStrSb.append(paramName).append(EQUAL_SIGN);
					for (String paramValue : paramValues) {
						paramStrSb.append(paramValue);
						paramStrSb.append(PLUS_SIGN);
					}
					paramStrSb.deleteCharAt(paramStrSb.length() - 1);
				}
				paramsStrSb.append(paramStrSb).append(AND);
			}
			paramsStrSb.deleteCharAt(paramsStrSb.length() - 1);
		}
		String paramsStr = paramsStrSb.toString();
		logger.info("收到來自" + remoteHost + "的請求,URL:" + requestURL + ",引數:" + paramsStr);
	}

	private static String create_nonce_str() {
		return UUID.randomUUID().toString();
	}

	@Override
	public void destroy() {

	}

}