Spring MVC源碼解析

轉載請標明出處 http://coderknock.com

DispatcherServlet

XXXAware

XXXAware在Spring中該類接口用於通過Spring自動向XXXAware實現類中實現的setXXX(XXX xxx)方法中注入XXX對象,方便在實現類中調用XXX對象。
例如:
A類需要使用當前的ApplicationContext,那麼只要是A實現ApplicationContextAware接口,然後實現接口中的setApplicationContext(ApplicationContext applicationContext)方法,Spring就會自動調用setApplicationContext方法將applicationContext傳給A類。

XXXCapable

實現XXXCapable接口後說明該實現類具有提供XXX的能力,當Spring需要XXX時會通過該類的getXXX方法來獲取XXX對象。

Environment

Environment具體功能與Servlet中的ServletContext類似。
HttpServletBean中Environment使用的是StandardServletEnvironment。

    @Override
    public ConfigurableEnvironment getEnvironment() {
        if (this.environment == null) {
            this.environment = this.createEnvironment();
        }
        return this.environment;
    }

    protected ConfigurableEnvironment createEnvironment() {
        return new StandardServletEnvironment();
    }

StandardServletEnvironment中封裝了ServletContext、ServletConfig、JndiProperty、系統環境變量以及系統屬性。

StandardServletEnvironment{
    activeProfiles=[

    ],
    defaultProfiles=[
        default
    ],
    propertySources=[
        servletConfigInitParams,
        servletContextInitParams,
        jndiProperties,
        systemProperties,
        systemEnvironment
    ]
}

Environment
具體屬性大家可以調試一下查看

轉載請標明出處 http://coderknock.com

HttpServletBean類圖
Servlet在創建時會直接調用無參init方法
HttpServletBean的init方法如下:

    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }
        try {
            // 將web.xml中init-param參數封裝到pvs變量中,requiredProperties是指必須的一些參數,沒有的話會報錯
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            //bw是對DispatcherServlet的封裝
            //【BeanWrpper是Spring提供的用於操作JavaBean屬性的一個工具類,通過它封裝一個對象後可以直接修改對象的屬性】
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            //對DispatcherServlet做一些初始化的工作
            initBeanWrapper(bw);
            //將配置值設置到DispatcherServlet
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // 子類通過該方法初始化【該類中只是聲明瞭該方法】
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

    protected void initServletBean() throws ServletException {
    }

轉載請標明出處 http://coderknock.com

FrameworkServlet,通過之前HttpServletBean詳解,我們知道,FrameworkServlet是通過initServletBean來實現自身的初始化的。我們看一下initServletBean:

    @Override
    protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }

    protected void initFrameworkServlet() throws ServletException {
    }

可以看到這個方法中的核心語句:

this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();

其中initFrameworkServlet是供子類Override後實現自身的一些初始化工作【DispatcherServlet中並沒有對initFrameworkServlet進行Override】。
可見,FrameworkServlet在構建工程中主要作用就是初始化webApplicationContext:

protected WebApplicationContext initWebApplicationContext() {
//獲取rootContext
        WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        //判斷是否在構造方法中以及初始化了webApplicationContext
        if (this.webApplicationContext != null) {
            // 一個WebApplicationContext實例在構建實例的時候被注入
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    // 上下文尚未Refresh -> 配置並Refresh
                    // 設置父上下文,設置WebApplicationContext id等等
                    if (cwac.getParent() == null) {
                        // 如果上下文被注入時沒有明確的設置Parent,則在這裏設置
                        // 設置rootContext爲Parent【可能爲null】 
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            //如果在構建實例時沒有注入WebApplicationContext
            //查找WebApplicationContext是否已經存在於ServletContext中【通過配置在Servlet中的contextAttribute獲取】
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // 如果還是沒有WebApplicationContext,那麼創建一個
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // 如果還是沒有WebApplicationContext 既不是一個ConfigurableApplicationContext支持Refresh,並且WebApplicationContext注入時沒有Refresh- >手動觸發onRefresh【供子類Override】。
            onRefresh(wac);
        }

        if (this.publishContext) {
            // 將WebApplicationContex保存到ServletContext
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

    protected void onRefresh(ApplicationContext context) {
        // For subclasses: do nothing by default.
    }

initWebApplicationContext做了三件事:
1. 獲取Spring都得rootContext
Spring默認將rootContext設置於ServletContext的屬性中,屬性名爲ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

  1. 設置WebApplicationContext並根據情況調用onRefresh方法
    設置WebApplicationContext一共三種方法:
    一、構造方法傳入
    這種方法主要用於Servlet3.0以後的環境中,在程序中使用ServletContext.addServlet方式註冊Servlet,這時可以在新建FrameworkServlet和其子類時通過構造方法傳遞WebApplicationContext
    二、WebApplicationContext已經存在ServletContext中,這是隻要將DispatcherServlet配置是將WebApplicationContext配置到contextAttribute就可以。
    三、創建一個WebApplicationContext【最終調用下面的方法】
    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        //獲取要創建的類型
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        //檢查獲取到的類似是否是ConfigurableWebApplicationContext的子類
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name '" + getServletName() +
                    "': custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        //具體創建
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

        wac.setEnvironment(getEnvironment());
        wac.setParent(parent);
        //將servlet配置地址【web.xml contextConfigLocation設置】
        wac.setConfigLocation(getContextConfigLocation());

        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }

        protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            if (this.contextId != null) {
                wac.setId(this.contextId);
            }
            else {
                // Generate default id...
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
            }
        }

        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());
        //添加ContextRefreshListener
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

        // The wac environment's #initPropertySources will be called in any case when the context
        // is refreshed; do it eagerly here to ensure servlet property sources are in place for
        // use in any post-processing or initialization that occurs below prior to #refresh
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }

        postProcessWebApplicationContext(wac);
        applyInitializers(wac);
        wac.refresh();
    }
  1. 將WebApplicationContext設置到ServletContext中

總結一下可配置項:
1. contextAttribute:在ServletContext中,用作WebApplicationContext的屬性名稱
2. contextClass:創建的WebApplicationContext的類型
3. contextConfigLocation:SpringMVC配置文件的位置
4. publishContext:是否將WebApplicationContext設置到ServletContext中。

轉載請標明出處 http://coderknock.com

通過之前FrameworkServlet的分析,我們知道DispatcherServlet的入口方法是onRefresh。

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

onRefresh調用了initStrategies,initStrategies中初始化了九大組件,其中的initXXXX方法基本實現都是通過WebApplicationContext.getBean(名稱, class);獲取註冊的對應組件,如果找不到會通過getDefaultStrategy方法獲取默認的組件。

protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
        List<T> strategies = getDefaultStrategies(context, strategyInterface);
        if (strategies.size() != 1) {
            throw new BeanInitializationException(
                    "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
        }
        return strategies.get(0);
    }
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        String key = strategyInterface.getName();
        String value = defaultStrategies.getProperty(key);
        if (value != null) {
            String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
            List<T> strategies = new ArrayList<T>(classNames.length);
            for (String className : classNames) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                    Object strategy = createDefaultStrategy(context, clazz);
                    strategies.add((T) strategy);
                }
                catch (ClassNotFoundException ex) {
                    throw new BeanInitializationException(
                            "Could not find DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]", ex);
                }
                catch (LinkageError err) {
                    throw new BeanInitializationException(
                            "Error loading DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]: problem with class file or dependent class", err);
                }
            }
            return strategies;
        }
        else {
            return new LinkedList<T>();
        }
    }

    private static final Properties defaultStrategies;

    static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
        }
    }

getDefaultStrategy調用了getDefaultStrategies,而getDefaultStrategies是通過defaultStrategies來查找value值的。defaultStrategies對應的是DispatcherServlet.properties:

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

這裏的默認設置並不是最優配置,也不是Spring推薦配置,只是爲了防止沒有配置。
這些默認配置是在沒有配置時纔會使用,使用<mvc:annotation-driven/>之後並不會全部使用默認配置。<mvc:annotation-driven/>會配置HandlerMapping、HandlerAdapter、HandlerExceptionResolver等。

發佈了79 篇原創文章 · 獲贊 21 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章