springMVC源码分析

本文深入剖析Spring MVC框架的核心组件,包括ContextLoaderListener监听器的工作原理、DispatcherServlet的初始化及请求处理流程,揭示了Spring MVC如何整合Spring和处理HTTP请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、ContextLoaderListener
1.基本原理
(1**)实现了servlet-api中的ServletContextListener,实现了ContextInitialized方法和contextDestroyed方法**
(2)每个Web都对应一个ServletContext,应用启动时创建,应用关闭时销毁,全局唯一
(3)ServletContextListener的使用举例,在web.xml中配置监听器即可,初始化会执行contextInitialize方法, 关闭时会执行ContextDestroyed方法

public class TestController  implements ServletContextListener {
    private ServletContext servletContext;
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //应用启动的时候,就会执行这里初始化的方
        this.servletContext = servletContextEvent.getServletContext();
        System.out.println(this.servletContext.getContextPath());
        System.out.println(this.servletContext.getInitParameterNames());
        InputStream inputStream = this.servletContext.getResourceAsStream("test.xml");

    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}
  <listener>
    <listener-class>person.controller.TestController</listener-class>
  </listener>

2.源码分析
(1)ServletContextListener中的contextInitialized

	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}
//ContextLoader中
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
......//不能存在多个ServletContextListener,即在web.xml中配置的spring监听器只能有一个,否则报错
	if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}
		try {
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext); //创建webApplicationContext实例
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}

//存在servletContext中,key是 org.springframework.web.context.WebApplicationContext.ROOT,value是一个webApplicationContext的实例

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}
......//catch异常,以及各种日志
		}

(2)ContextLoader中createWebApplicationContext创建webApplicationContext实例

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> contextClass = determineContextClass(sc); //这里是决定webApplicationContext接口库具体的实现类,见下面
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //通过class.newInstance反射出对象实例
	}
//ContextLoader中有静态域,加载properties文件,配置的默认的实现类为 XmlWebApplicationContext
	static {
			try {
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}......
	}
protected Class<?> determineContextClass(ServletContext servletContext) {
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);//这里加载的是webxml中配置的WebApplicationcontext的实现类,通过context-param设置
		if (contextClassName != null) {
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}.....catch异常
		}
		else {//如果没有配置,则去加载properties中的配置项,获得Class对象
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
	}

(3) web.xml中的 param-name全部在ContextLoader中定义

 <!--这里的 param-name全部在ContextLoader中定义的-->
  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.GenericWebApplicationContext</param-value>
  </context-param>

二、DispatcherServlet
1.dispatcherServlet的继承关系图
在这里插入图片描述
2.dispatcherSlervlet的初始化 即init()方法的重写,在父类HttpServletBean中重写了
(1)init()

public final void init() throws ServletException {
//解析<init-param>属性中的值并封装
        PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try { //将当前的Servlet即DispatcherSerlvet封装成spring的bean
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                this.initBeanWrapper(bw); //钩子方法,留子类实现
                bw.setPropertyValues(pvs, true); //属性注入
            } 

        this.initServletBean(); //钩子方法,子类扩展用
    }

ps init()方法主要做的事情:
将web.xml中配置的init-param封装成PropertyValue类
将当前的Servlet封装成spring的bean,目的是将属性能够注入进去
属性注入
调用子类复写的钩子方法//即FrameWorkServlet中实现的方法,对监听器中创建的WebApplicationContext的实例继续进行补充初始化
(2)FrameworkServlet中重写了父类的HttpServletBean的方法,对监听器中创建的WebApplicationContext的实例继续进行补充初始化

   protected final void initServletBean() throws ServletException {
....//省略
            this.webApplicationContext = this.initWebApplicationContext(); //继续初始化webApplicationContext实例
            this.initFrameworkServlet();//钩子方法,留给子类实现
......//catch方法
    }
  protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        if (this.webApplicationContext != null) { //不为空则是直接通过实例化FrameworkServlet注入的属性
            wac = this.webApplicationContext;
                    this.configureAndRefreshWebApplicationContext(cwac); //配置webApplicationContext的属性
                }
        }

        if (wac == null) { //如果依然为空,则找到之前在ContextLoaderListener中初始化实例的webapplicationContext实例
            wac = this.findWebApplicationContext();
        }
        if (wac == null) { //如果还为空则通过反射的方式创建
            wac = this.createWebApplicationContext(rootContext);
        }
        if (!this.refreshEventReceived) {
            this.onRefresh(wac); //这是个模板方法,在子类dispatcherServlet中重写,初始化WebApplicationContext 的很多全局变量
        }
        if (this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
            }
        }
        return wac;
    }

(3).onrefresh 在dispatcherServlet中重写的方法,初始化web应用很多功能需要的全局变量

  protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }
//该方法调用的都是一些获取bean的方法
    protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context); //处理文件上传的bean
        this.initLocaleResolver(context); //处理国际化的bean
        this.initThemeResolver(context);//处理主题资源的bean,切换主题静态资源所在路径
        this.initHandlerMappings(context);//初始化handlerMapping,默认会加载所有的实现了handlermapping接口的bean
        this.initHandlerAdapters(context); //初始化HandlerAdapters
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }
//默认情况下是没有配置该bean的,如果需要做文件上传,需要配置,配置如下,如果没有配置,
    private void initMultipartResolver(ApplicationContext context) {
.....
        try {
            this.multipartResolver = (MultipartResolver)context.getBean("multipartResolver", MultipartResolver.class);
          }.......
    }
在配置文件中要配置改bean
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSizePerFile" value="3"/>
</bean>

ps :init方法中获取以上的bean,由此验证bean的加载是在ContextLoaderListener监听器中进行的,监听器的加载在dispatcherServlet之前
(4) this.initRequestToViewNameTranslator(context);//当controller中没有返回view对象或逻辑视图名称,且未向response中写数据时,spring会以默认方式提供一个视图名 ,方式是实现接口RequestToViewNameTranslator的getNiewName,然后通过id=viewNameTranslator配置bean的方式,将实现类配置进去,spring默认的实现类是DefaultRequestToViewNameTranslator

public class DefaultRequestToViewNameTranslator implements RequestToViewNameTranslator {
    private static final String SLASH = "/";
    private String prefix = "";//前缀
    private String suffix = "";//后缀
    private String separator = "/";
    private boolean stripLeadingSlash = true;//首字符分隔符时默认去除
    private boolean stripTrailingSlash = true;//结尾为分隔符则默认去除
    private boolean stripExtension = true; //请求路径中包含扩展名时默认去除扩展名,eg:test.html会去掉.html
    private UrlPathHelper urlPathHelper = new UrlPathHelper(); 
}
this.initViewResolvers(context);//初始化视图解析器

3.dispatcherServlet的逻辑处理
(1)doService中主要是一些准备工作,将init中初始化的一些类和listener中实例化的webapplicationcontext实例设置为request的属性

   protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
........
//init中初始化的东西 
          request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());//默认是xmlwebapplicationcontext
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); 
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        try {
            this.doDispatch(request, response);//将包装好的request交给doDispatch,见下面
        }.....    }

(2)doDispatcher最终处理完成的请求逻辑

  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request)
        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;
                try { //判断如果是MultipartContent类型的request则转换为multiPartHttpServletRequest,文件上传与普通request不一样
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest); //根据request信息寻找对应的handler,handler就是HandlerExecutionChain实例,见下面
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response); //如果没有,则返回404,见下面
                        return;
                    }//寻找适配器,遍历选择类型合适的适配器,适配器很重要,里面包含了处理请求的所有要素
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
......                      //拦截连的调用 ,见下面
                           if (!mappedHandler.applyPreHandle(processedRequest, response)) { 
                        return;
                    }
//处理handle并返回视图
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    this.applyDefaultViewName(processedRequest, mv); //这里通过request处理需要视图名称添加前缀后缀的情况
                    mappedHandler.applyPostHandle(processedRequest, response, mv); //postHandle后处理方法
                }//这里处理异常之后的的页面或者普通页面跳转,其中的render方法就是页面跳转
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) { //异常则处理触发器
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        }
        }
    }

ii.根据reqeust信息寻找对应的Handler,getHandler,系统启动时会将所有的映射类型的bean注册到handlerMappings中

   protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Iterator var2 = this.handlerMappings.iterator(); //对handlermapping实现类进行遍历
        HandlerExecutionChain handler;
        do {
            if (!var2.hasNext()) {
                return null;
            }
            HandlerMapping hm = (HandlerMapping)var2.next();
            handler = hm.getHandler(request);  //调用gethandler封装成HandlerExecutionChain,HandlerMapping的默认实现类是AbstractHandlerMapping
        } while(handler == null);

        return handler;
    }

iii.关于handlerMappings 的说明,DispatcherServlet中有个属性private List handlerMappings;
关于HandlerMapping类的spring注释说明这个接口被那些在beanName和request之间做映射的类实现,spring中有beanNameUrlHandlerMapping和defaultAnnotationHandlermapping两个实现类,一般不需要再开发;handlerMappings集合里一般也就是这两个实现类
iv.AbstractHandlerMapping中的getHandler实现

 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = this.getHandlerInternal(request); //根据url找到匹配的Controller即Handler返回,见下面
        if (handler == null) { //如果没有找到对应的Controller,那么会去查默认的处理器,spring中是可以配置默认的控制器的
            handler = this.getDefaultHandler();
        }
        if (handler == null) { //如果还是为空,则说明没有找到匹配的controller
            return null;
        } else { //string类型,说明是配置的bean的名称,则根据beanName去找bean,
            if (handler instanceof String) { 
                String handlerName = (String)handler;
                handler = this.getApplicationContext().getBean(handlerName);
            }
//封装并返回
            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
                executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
            }

            return executionChain;
        }
    }

v.AbstractHandlerMapping中的是个抽象方法,方法实现在AbstractUrlHandlerMapping

 protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);//截取路径,即剔除根路径
        Object handler = this.lookupHandler(lookupPath, request);//根据路径寻找Handler,这里的Handler一般是个HandlerEXecutionCChain对象,其中包括了请求的控制器,和请求的方法
        if (handler == null) {
            Object rawHandler = null;
            if ("/".equals(lookupPath)) { //路径如果是/,则使用RootHandler
                rawHandler = this.getRootHandler();
            }//没有则去找默认的控制器,一般都为空
            if (rawHandler == null) {
                rawHandler = this.getDefaultHandler();
            }
//String属于xml中配置的beanName
            if (rawHandler != null) {
                if (rawHandler instanceof String) {
                    String handlerName = (String)rawHandler;
                    rawHandler = this.getApplicationContext().getBean(handlerName);
                }
                this.validateHandler(rawHandler, request);
                handler = this.buildPathExposingHandler(rawHandler, lookupPath, lookupPath, (Map)null);//将原生的handler进行充实,将handler封装成了HandlerExecutionChain
            }
        }
..............//日志
        return handler;
    }

vi. 在DispatcherServlet中noHandlerFound处理没有找到Handler的情况,一个请求对应一个handler(HandlerExecutionChain实例),一般开发人员可以配置默认的控制器,当没有找到handler则去找默认的,都没有则走该方法

protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.throwExceptionIfNoHandlerFound) {
            throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), (new ServletServerHttpRequest(request)).getHeaders());
        } else {
            response.sendError(404); //前台返回404
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值