1. 程式人生 > >Spring MVC原始碼分析之DispatcherServlet初始化過程

Spring MVC原始碼分析之DispatcherServlet初始化過程

DispatcherServelt本質是也是Servlet,由Servlet容器進行載入。

1.Servlet介面提供了Servlet的初始化方法:init(ServletConfig config)。

2.GenericServlet實現了方法init(ServletConfig config),此方法呼叫具體的初始化方法:init()。

3.HttpServletBean重寫了方法init(),並用修飾符final將方法變成了不可重寫,此處可以用模板方法來理解:初始化的邏輯過程在此方法中定義好了,具體的實現由子類來完成。

@Override
public final void init
() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new
ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 方法體為空,子類可以重寫實現擴充套件 initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } } // Let subclasses do whatever initialization they like.
// 由子類FrameworkServlet來完成 initServletBean(); }

4.FrameworkServlet重寫了initServletBean(),並用修飾符final將方法變成了不可重寫,此處也可以用模板方法來理解,
初始化的邏輯過程在此方法中定義好了,具體的實現由子類來完成。

@Override
protected final void initServletBean() throws ServletException {
    try {
        // 呼叫initWebApplicationContext()方法,完成初始化
        this.webApplicationContext = initWebApplicationContext();
        // 方法體為空,子類可以重寫實現擴充套件
        initFrameworkServlet();
    }
}

5.FrameworkServlet.initWebApplicationContext中呼叫的方法onRefresh(ApplicationContext context)的方法體也為空,由子類DispatcherServlet負責實現。

6.DispatcherServlet重寫了方法onRefresh,此方法呼叫具體的初始化過程initStrategies(ApplicationContext context)

/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

7.DispatcherServlet的核心初始化過程是由下面的方法負責完成的。

protected void initStrategies(ApplicationContext context) {
    // Spring MVC擴充套件點,核心的配置檔案,也就是預設核心元件在DispatcherServlet.properties中
    // 1. 檔案上傳解析類,配置檔案中沒有預設配置.
    // 要想實現檔案上傳的功能,需要我們在xml配置檔案中配置名multipartResolver名為multipartResolver的bean
    // 下面一行被註釋的程式碼告訴我們,沒有配置則不具備此功能
    // this.multipartResolver = null;
    // 此解析類需要實現介面MultipartResolver
    // 可以配置此類org.springframework.web.multipart.support.StandardServletMultipartResolver,作為檔案上傳的解析類
    // 當然也可以在xml檔案中配置定製化的實現MultipartResolver介面的檔案上傳解析類
    initMultipartResolver(context);
    // 2.國際化
    initLocaleResolver(context);
    // 3.主題解析
    initThemeResolver(context);
    // 4.請求到處理器的對映
    initHandlerMappings(context);
    // 5.根據Handler的型別定義不同的處理規則
    initHandlerAdapters(context);
    // 6.Handler異常處理
    initHandlerExceptionResolvers(context);
    // 7.view->jsp,預設DefaultRequestToViewNameTranslator(JSP檢視)
    initRequestToViewNameTranslator(context);
    // 8.檢視解析:View檢視解析成頁面,可以設定多個解析策略,預設:InternalResourceViewResolver(JSP)
    initViewResolvers(context);
    // 9.重定向屬性儲存集合管理器
    initFlashMapManager(context);
}

8.上面的程式碼中只解釋了檔案上傳元件,其他的元件的具體功能在後面的博文會有具體的描述。下面再解析一下預設元件的載入工程,在相應元件的初始化方法中,如果能在檢測到對應的bean元件例項,則直接使用;不能檢測到,則通過反射去建立預設配置檔案中配置的預設元件。

DispatcherServlet類載入的時候,會在靜態程式碼塊中解析預設配置檔案。

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);
        // 將預設配置解析到集合Properties中
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }
}

通過反射載入配置的預設元件。

@SuppressWarnings("unchecked")
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<>(classNames.length);
        for (String className : classNames) {
            try {
                // 反射方式ClassForName(className)載入Class物件
                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<>();
    }

DispatcherServlet的初始化方法,給我們提供了眾多的擴充套件點,讓我們可以靈活地定製DispatcherServlet服務(只需要實現其對應的介面並在xml中進行配置),去深入地把控和擴充套件DispatcherServlet處理Request請求的每一個步驟。