SpringMVC -辅线

1. 运行流程

1.1 运行主流程

DispatcherServlet#doService(){
	//....
    doDispatch();//主体流程
    //....
}

DispatcherServlet#doDispatch(){
	//....
    //获取请求中指向的 Handler,返回 HandlerExecutionChain
    //HandlerExecutionChain中主要两个属性,【HandlerMapping对当前handler封装对象】+【当前Handler的拦截器数组】
    mappedHandler = getHandler(processedRequest);
    //....
    // 为当前Handler找一个合适的 HandlerAdapter,来执行此Handler
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    //....
}
DispatcherServlet#getHandler() {
    //遍历工厂中的 HandlerMapping, HandlerMapping中记录着Handler的访问地址
    //但只有认识@RequestMapping注解的HandlerMapping,才真正记录着各个handler的访问地址
    for (HandlerMapping hm : this.handlerMappings) {
        if (logger.isTraceEnabled()) {
            logger.trace(
                "Testing handler map ...);
        }
        // 从HandlerMapping中获取 请求路径对应的Handler
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {//如果当前HandlerMapping可以识别当前请求,则返回
            return handler;
        }
    }
    return null;
}
                
DispatcherServlet#getHandlerAdapter(){
    for (HandlerAdapter ha : this.handlerAdapters) {
        // ....
        if (ha.supports(handler)) {// 遍历多个Adapter,选择一个适合当前Handler的Adapter
            return ha;
        }
    }
}
DispatcherServlet#doDispatch(){
	//....
    //获取请求中指向的 Handler,返回 HandlerExecutionChain
    //HandlerExecutionChain中主要两个属性,【HandlerMapping对当前handler封装对象】+【当前Handler的拦截器数组】
    mappedHandler = getHandler(processedRequest);
    //....
    // 为当前Handler找一个合适的 HandlerAdapter,来执行此Handler
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    //....
    //执行拦截器的前置逻辑
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
    //紧接着,执行handler,返回一个ModelAndView
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    //.....
    //执行拦截器的后置逻辑
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    //.....
    //视图渲染,在此方法内部解析完视图渲染后,会调用:
    // mappedHandler.triggerAfterCompletion(request, response, null);//拦截器的最终逻辑
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// processDispatchResult会调用此方法,进行视图渲染
DispatcherServlet#render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response){
    // ....
    // 解析出一个View对象,其实此View已经在ModelAndView中,在ha执行完handler后就存在了
    View view = resolveViewName(...);
    // ....
    // 调用View的渲染方法,内部将跳转视图,并响应请求
    view.render(....);
    // 常见的View类型:InternalResourceView用户处理转发跳转,内部使用 request.getRequestDispatcher().forward()
    //               RedirectView 用于处理重定向,内部使用 response.sendRedirect()
}

1.2 启动细节

第一次访问时,DispatcherServlet启动,启动中会初始化很多,系统组件( HandlerMapping, HandlerAdapter … ),以及所有Controller。

ComponentScanBeanDefinitionParser 负责解析配置中的 context:component-scan,并扫描Controller,并创建
AnnotationDrivenBeanDefinitionParser 负责解析配置中的 mvc:annotation-driven,并创建对应组件

1.3 HandlerMapping 解析请求细节

每种HandlerMapping都有一方法getHandlerInternal(request)为当前请求匹配并返回一个Handler,

但返回类型不一。所谓返回类型不一,是指如果找到匹配的Handler,封装的方法不同,即封装后的对象类型不同。

比如RequestMappingHandlerMapping的返回的是一个HandlerMethod

比如BeanNameUrlHandlerMapping的返回直接是一个Controller对象

细节:

RequestMappingHandlerMapping中的getHandler方法,会调用getHandlerInternal()返回一个 HandlerMethod; 在getHandler方法中再封装进一个HandlerExecutionChain中。

BeanNameUrlHandlerMapping中的getHandler方法,会调用getHandlerInternal()方法,方法中会先找到对应的Controller类,然后直接封装为HandlerExecutionChain,并返回给getHandler方法。

HandlerExecutionChain 中主要封装两类信息:当前handler 、handler的拦截器数组。

@Controller("/abc")//不能定义多方法,且 "/abc"既是beanId,更是访问路径。BeanNameUrlHandlerMapping可以解析
public class CaptchaController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) 
        throws Exception {
        ModelAndView mav = new ModelAndView("index");
        return mav;
    }
}

1.4 HandlerAdapter解析Handler细节

HandlerExecutionChain在得到后,会遍历所有HandlerAdapter,通过调用HandlerAdapter中的supports(),判断哪个HandlerAdapter可以解析当前HandlerExecutionChain中持有的handler。

RequestMappingHandlerAdapter 可以识别 HandlerMethod,所以和RequestMappingHandlerMapping是一个组合

SimpleControllerHandlerAdapter 可以识别Controller类,所以和 BeanNameUrlHandlerMapping是一个组合

综上,HandlerMapping和HandlerAdapter是配套使用的。

ops:可以断点在DispatcherServlet的doDispatch方法中,

然后查看DispatcherServlet的 handlerMappings 和 handlerAdapters属性

2. SpringMVC工厂启动

DispatcherServlet父类的父类:
HttpServletBean#init(){
	//....
	// Let subclasses do whatever initialization they like.
	initServletBean();//初始化
	//....
}
FrameworkServlet#initServletBean(){
    //....
    this.webApplicationContext = initWebApplicationContext();//初始化工厂
    //....
}

FrameworkServlet#initFrameworkServlet(){
    //获取Spring工厂,准备将其作为父容器
    WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    //....
    if (wac == null) {
       // No context instance is defined for this servlet -> create a local one
       wac = createWebApplicationContext(rootContext);
    }
    //....
}

FrameworkServlet#createWebApplicationContext(){
   return createWebApplicationContext((ApplicationContext) parent);
}

FrameworkServlet#createWebApplicationContext(xx){
    //....
    // 创建springMVC工厂对象:XmlWebApplicationContext
    ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    wac.setEnvironment(getEnvironment());//设置环境参数
    wac.setParent(parent);//将spring容器,作为自己的父容器
    wac.setConfigLocation(getContextConfigLocation());//设置配置文件位置

    configureAndRefreshWebApplicationContext(wac);//启动SpringMVC工厂,创建其中的bean

    return wac;
}
//如上的启动过程中 configureAndRefreshWebApplicationContext(wac),会执行到:
DispatcherServlet#initStrategies(ApplicationContext context) {
    initMultipartResolver(context);//初始化上传解析器
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);//初始化 HandlerMappings
    initHandlerAdapters(context);//初始化 HandlerAdapters
    initHandlerExceptionResolvers(context);//初始化 异常解析器
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

3. Json处理

3.1 响应

View InternalResourceView(JSTlView) RedirectView FastJsonView xxxJacksonView

Json响应是靠 HttpMessageConverter,而不是View!!

// 使用了@ResponseBoy 后,Handler执行后不再返回一个ModelAndView,
// 而是在 前端控制器触发Handler执行时,handler执行的最后一步就已经响应了。所以没有后续的View过程。

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//从此处开始执行Handler

FastJsonHttpMessageConverter#writeInternal(){
    ByteArrayOutputStream outnew = new ByteArrayOutputStream();//字节输出流
    //......
    //将转换后的json,存入outnew
    int len = JSON.writeJSONString(outnew, //
                    fastJsonConfig.getCharset(), //
                    value, //
                    fastJsonConfig.getSerializeConfig(), //
                    //fastJsonConfig.getSerializeFilters(), //
                    allFilters.toArray(new SerializeFilter[allFilters.size()]),
                    fastJsonConfig.getDateFormat(), //
                    JSON.DEFAULT_GENERATE_FEATURE, //
                    fastJsonConfig.getSerializerFeatures());
    // ....
    // outnew将内容输出给 response.getOutputStream()
    outnew.writeTo(outputMessage.getBody());// outputMessage是response对象,getBody中 		
                                            // response.getOutputStream()
}

AbstractJackson2HttpMessageConverter#writeInternal(){
    // ....
    // 获得一个可以响应json的generator                                    response.getOutputStream()
    JsonGenerator gene = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
    // ....
    // 将json响应到客户端
    objectWriter.writeValue(generator, value);
}

3.2 收参

@RequestBody收参时,会调用如下核心方法,实现从json到java对象的转换

AbstractJackson2HttpMessageConverter#read(){//将json转换为java对象
    //....
}


FastJsonHttpMessageConverter#read(){//将json转换为java对象
    //....
}
 @RequestMapping(value="/users",method = RequestMethod.PUT)
@ResponseBody
public MyRequestStatus updateUser(@RequestBody User user) {
    System.out.println("update One user:"+user);
    MyRequestStatus status = new MyRequestStatus("update", "ok");
    return status;
}

在这里插入图片描述

	执行流程 DispatcherServlet  接收到请求后  找到一个可以识别当前路径的HandlerMappping 
	通过请求路径返回HandlerChain然后执行 HandlerChain 会依次调用拦截器的方法和Handler 并完成请求参数
	的封装 和相应JSON时(HttpMessageConverter) 的格式转化 handler 执行完毕 返回ModelAndVIew ViewResolver
	 通过ModeAndView中的viewName解析出一个View 对象 VIew完成转发或从定向  到jsp层

静态资源的访问 mvc:default-servlet-handler/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值