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请求的每一个步骤。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值