• SpringMVC源码--DispatcherServlet 初始化


    DispatcherServlet初始化

    SpringMVC运行在Servlet容器中(tomcat、jetty),是对servlet的封装, 所以整个生命周期与Servlet是有关系的。

    【Servlet】

    public void init(ServletConfig config)
    
    • 1

    The servlet container calls the init method exactly once after instantiating the servlet. The init method must complete successfully before the servlet can receive any requests.

    Servlet 容器会精确调用一次servlet的init 方法,用于执行一些初始化的操作。

    在这里插入图片描述

    DispatcherServlet是SpringMVC中的核心类,继承于Servlet,故在Servlet容器中会触发一次init方法。

    调用链如下:
    在这里插入图片描述

    DispatcherServlet.onRefresh(ApplicationContext context)

    	protected void onRefresh(ApplicationContext context) {
    	    // 初始化SpringMVC组件
    		initStrategies(context);
    	}
    
    • 1
    • 2
    • 3
    • 4

    initStrategies 方法是DispatcherServlet 成员变量初始化的核心代码。

    	protected void initStrategies(ApplicationContext context) {
    	    // 初始化form-data 中的文件上传
    		initMultipartResolver(context);
    		// 国际化
    		initLocaleResolver(context);
    		// 主题
    		initThemeResolver(context);
    		// 映射器
    		initHandlerMappings(context);
    		// 处理器
    		initHandlerAdapters(context);
    		// 异常处理器
    		initHandlerExceptionResolvers(context);
    		initRequestToViewNameTranslator(context);
    		// 视图解析器
    		initViewResolvers(context);
    		initFlashMapManager(context);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    初始化成员变量

    执行 initStrategies中的方法会DispatcherServlet中的成员变量赋值。

    我们以 handlerMappings 初始化为例。

    
    	/** List of HandlerMappings used by this servlet. */
    	@Nullable
    	private List<HandlerMapping> handlerMappings;
    
    • 1
    • 2
    • 3
    • 4

    对应的方法

    	private void initHandlerMappings(ApplicationContext context) {
    		this.handlerMappings = null;
          
    		if (this.detectAllHandlerMappings) {
    			Map<String, HandlerMapping> matchingBeans =
    					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
    			if (!matchingBeans.isEmpty()) {
    				this.handlerMappings = new ArrayList<>(matchingBeans.values());
    				AnnotationAwareOrderComparator.sort(this.handlerMappings);
    			}
    		}
    		else {
    			//......................
    		}
    
    		if (this.handlerMappings == null) {
    			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    detectAllHandlerMappings 默认为true, 代码会进入if分支。

    private boolean detectAllHandlerMappings = true
    
    • 1

    if分支逻辑很简单, 从IOC容器中获取类型为HandlerMapping.class 的Bean集合。

    默认实现

    回想之前的项目开发,我们没有配置HandlerMapping为什么依旧可以处理请求呢?SpringMVC中配置了默认的HandlerMapping。

    this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    
    • 1
    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    		String key = strategyInterface.getName();
    		String value = defaultStrategies.getProperty(key);
    		//...... 创建实例并返回	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    defaultStrategies 又是什么东西啊?

    private static final Properties defaultStrategies;
    
    • 1

    看定义发现是Properties , 所以defaultStrategies 会加载一个properties配置文件。

    	ClassPathResource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);
    	
    	defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    
    • 1
    • 2
    • 3

    以下为截取的部分数据:

    • key: org.springframework.web.servlet.HandlerMapping,
    • value 是 HandlerMapping 字符串,多个逗号隔开。

    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,
    org.springframework.web.servlet.function.support.RouterFunctionMapping

    看到这种全限定的类名,我们应该可以猜到, 可以通过Class.forName(“”) , cls.newInstance() 创建实例。

    但是上面方式的创建有一个问题就是,脱离了IOC容器的管理,这样如果存在初始化方法也需要手动触发。 所以SpringMVC 调用AutowireCapableBeanFactory的createBean 方式创建实例, 这个创建流程特别复杂,就不展开了。

    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
    Object strategy = createDefaultStrategy(context, clazz);
    strategies.add((T) strategy);
    
    • 1
    • 2
    • 3
    protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
    	return context.getAutowireCapableBeanFactory().createBean(clazz);
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    最佳云存储平台推荐:安全可靠的选择解析
    【react】什么是fiber?fiber解决了什么问题?从源码角度深入了解fiber运行机制与diff执行
    Java在算法题中的输入问题
    【2022年中总结】我走得很慢,但我从不后退
    SpringBoot整合JWT
    CSP-J2023入门组第二轮T2:公路
    Jmeter参数化 —— 循环断言多方法
    java-net-php-python-jsp网上拍卖系统计算机毕业设计程序
    污水处理厂3D数字孪生三维可视系统降低设备风险隐患
    【代码随想录】算法训练计划13
  • 原文地址:https://blog.csdn.net/yamadeee/article/details/125894812