spring mvc DispatcherServlet詳解前傳---HttpServletBean類


從上章里我們已經看到:

   DispatcherServlet extends FrameworkServlet

   FrameworkServlet extends HttpServletBean implements ApplicationContextAware

那么HttpServletBean作為DispatcherServlet的父類,起到了一個什么作用呢?

spring中這樣描述的:

/**
 * Simple extension of {@link javax.servlet.http.HttpServlet} which treats
 * its config parameters ({@code init-param} entries within the
 * {@code servlet} tag in {@code web.xml}) as bean properties.
 *
 * <p>A handy superclass for any type of servlet. Type conversion of config
 * parameters is automatic, with the corresponding setter method getting
 * invoked with the converted value. It is also possible for subclasses to
 * specify required properties. Parameters without matching bean property
 * setter will simply be ignored.
 *
 * <p>This servlet leaves request handling to subclasses, inheriting the default
 * behavior of HttpServlet ({@code doGet}, {@code doPost}, etc).
 *
 * <p>This generic servlet base class has no dependency on the Spring
 * {@link org.springframework.context.ApplicationContext} concept. Simple
 * servlets usually don't load their own context but rather access service
 * beans from the Spring root application context, accessible via the
 * filter's {@link #getServletContext() ServletContext} (see
 * {@link org.springframework.web.context.support.WebApplicationContextUtils}).
 *
 * <p>The {@link FrameworkServlet} class is a more specific servlet base
 * class which loads its own application context. FrameworkServlet serves
 * as direct base class of Spring's full-fledged {@link DispatcherServlet}.*/

我們可以從HttpServletBean的繼承關系來分析它的作用:

HttpServletBean extends HttpServlet
implements EnvironmentCapable, EnvironmentAware

1. 繼承了javax.servlet.http.HttpServlet

簡單的說HttpServletBean是javax.servlet.http.HttpServlet類的簡單擴展,在web.xml文件中<servlet>標簽的下一級標簽中通過<init-param>來配置該servlet的參數。實例如下:

    <!-- This servlet must be loaded first to configure the log4j
     system and create the WebApplicationContext
     -->
    <servlet>
        <servlet-name>config</servlet-name>
        <servlet-class>org.springframework.framework.web.context.ContextLoaderServlet</servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.framework.web.context.XMLWebApplicationContext</param-value>           
        </init-param>    
        <init-param>
            <param-name>log4jPropertiesUrl</param-name>
            <param-value>/WEB-INF/log4j_PRODUCTION.properties</param-value>           
        </init-param>    
        <!-- This is essential -->
        <load-on-startup>1</load-on-startup>
      </servlet>

  2. 繼承了EnvironmentAware

    EnvironmentAware到底起了什么作用呢?這需要我們首先了解一下Aware接口的作用:

/**
 * Marker superinterface indicating that a bean is eligible to be
 * notified by the Spring container of a particular framework object
 * through a callback-style method. Actual method signature is
 * determined by individual subinterfaces, but should typically
 * consist of just one void-returning method that accepts a single
 * argument.
 *
 * <p>Note that merely implementing {@link Aware} provides no default
 * functionality. Rather, processing must be done explicitly, for example
 * in a {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}.
 * Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
 * and {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory}
 * for examples of processing {@code *Aware} interface callbacks.
 *
 * @author Chris Beams
 * @since 3.1
 */
public interface Aware {

}

容器中定義的Bean一般不需要了解容器的狀態或者直接使用容器,但是在某些情況下,是需要在Bean中直接對IOC容器進行操作的,這時候,就需要在Bean中設定對容器的感知。Spring IOC容器也提供了該功能,它是通過特定的Aware接口來完成的。這個比較抽象,我們來從代碼來理解吧:

從spring-beans模塊中我發現有三個實現了Aware接口,它們分別是:

BeanNameAware: Interface to be implemented by beans that want to be aware of their bean name in a bean factory. Note that it is not usually recommended that an object depend on its bean name, as this represents a potentially brittle dependence on external configuration, as well as a possibly unnecessary dependence on a Spring API.

BeanFactoryAware: Interface to be implemented by beans that wish to be aware of their owning {@link BeanFactory}.For example, beans can look up collaborating beans via the factory (Dependency Lookup). Note that most beans will choose to receive references to collaborating beans via corresponding bean properties or constructor arguments (Dependency Injection).

BeanClassLoaderAware: Callback that allows a bean to be aware of the bean {@link ClassLoader class loader}; that is, the class loader used by the present bean factory to load bean classes. This is mainly intended to be implemented by framework classes which  have to pick up application classes by name despite themselves potentially being loaded from a shared class loader.

上面三個接口分別實現了響應的set方法:

public interface BeanNameAware extends Aware {
    void setBeanName(String name);
}
public interface BeanFactoryAware extends Aware {
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
public interface BeanClassLoaderAware extends Aware {
    void setBeanClassLoader(ClassLoader classLoader);
}

從上述三個例子中,我們可以看到實現了Aware接口,bean就可以在spring容器中使用相應的對象。

那么我們來詳細分析一個EnvironmentAware接口:

/**
 * Interface to be implemented by any bean that wishes to be notified
 * of the {@link Environment} that it runs in.
 *
 * @author Chris Beams
 * @since 3.1
 */
public interface EnvironmentAware extends Aware {

    /**
     * Set the {@code Environment} that this object runs in.
     */
    void setEnvironment(Environment environment);

}

我們來看一下HttpServletBean下的setEnvironment方法實現。

    /**
     * {@inheritDoc}
     * @throws IllegalArgumentException if environment is not assignable to
     * {@code ConfigurableEnvironment}.
     */
    @Override
    public void setEnvironment(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        this.environment = (ConfigurableEnvironment) environment;
    }

    /**
     * {@inheritDoc}
     * <p>If {@code null}, a new environment will be initialized via
     * {@link #createEnvironment()}.
     */
    @Override
    public ConfigurableEnvironment getEnvironment() {
        if (this.environment == null) {
            this.environment = this.createEnvironment();
        }
        return this.environment;
    }

    /**
     * Create and return a new {@link StandardServletEnvironment}. Subclasses may override
     * in order to configure the environment or specialize the environment type returned.
     */
    protected ConfigurableEnvironment createEnvironment() {
        return new StandardServletEnvironment();
    }

從上述代碼中我們可以看到默認情況下的

environment=new StandardServletEnvironment()
StandardServletEnvironment 后續章節講到,這里我們僅僅看作Spring抽象了一個Environment來表示環境配置。

3. 繼承了EnvironmentCapable

/**
 * Interface indicating a component that contains and exposes an {@link Environment} reference.
 *
 * <p>All Spring application contexts are EnvironmentCapable, and the interface is used primarily
 * for performing {@code instanceof} checks in framework methods that accept BeanFactory
 * instances that may or may not actually be ApplicationContext instances in order to interact
 * with the environment if indeed it is available.
 *
 * <p>As mentioned, {@link org.springframework.context.ApplicationContext ApplicationContext}
 * extends EnvironmentCapable, and thus exposes a {@link #getEnvironment()} method; however,
 * {@link org.springframework.context.ConfigurableApplicationContext ConfigurableApplicationContext}
 * redefines {@link org.springframework.context.ConfigurableApplicationContext#getEnvironment
 * getEnvironment()} and narrows the signature to return a {@link ConfigurableEnvironment}.
 * The effect is that an Environment object is 'read-only' until it is being accessed from
 * a ConfigurableApplicationContext, at which point it too may be configured.
 *
 * @author Chris Beams
 * @since 3.1
 * @see Environment
 * @see ConfigurableEnvironment
 * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment()
 */
public interface EnvironmentCapable {

    /**
     * Return the {@link Environment} associated with this component.
     */
    Environment getEnvironment();

}

4. Environment 環境配置信息

EnvironmentCapable 接口和EnvironmentAware分別實現抽象了
Environment getEnvironment();
void setEnvironment(Environment environment);

 

它的主要幾個實現如下所示:

MockEnvironment:模擬的環境,用於測試時使用;

StandardEnvironment:標准環境,普通Java應用時使用,會自動注冊System.getProperties() 和 System.getenv()到環境;

public class StandardEnvironment extends AbstractEnvironment {

    /** System environment property source name: {@value} */
    public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

    /** JVM system properties property source name: {@value} */
    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); //System.getProperties();
        propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));//System.getenv()
    }

}

其中

StandardServletEnvironment:標准Servlet環境,其繼承了StandardEnvironment,Web應用時使用,除了StandardEnvironment外,會自動注冊ServletConfig(DispatcherServlet)、ServletContext及JNDI實例到環境;

public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {

    /** Servlet context init parameters property source name: {@value} */
    public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

    /** Servlet config init parameters property source name: {@value} */
    public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

    /** JNDI property source name: {@value} */
    public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";


    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
        propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
        }
        super.customizePropertySources(propertySources);
    }

    @Override
    public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) {
        WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
    }

}

5. 小結

    HttpServletBean分別實現了HttpServlet,EnvironmentCapable,EnvironmentAware.

   簡單擴展HttpServlet,給各種類型的servlet提供了一個便利的超類,提供了對屬性的操作。

  關於屬性操作,會在下一步文件中介紹。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM