spring MVC -- controller参数的解析

376 查看

spring给我们带来了什么?

spring IoC、AOP、Transaction这些都是很重要的特性,但是这篇这些都不是主角,主要来谈谈springMVC是如何对请求参数进行解析封装的,以及简单的介绍spring是怎么进行http请求路由的。

0x01 springMVC工程的建立

本篇就不过多介绍springMVC的工程的新建过程,此次调试springMVC源码新建的工程目录如下,通过maven管理的工程:

0x02 spring是如何接入web容器的

在进行springMVC的开发过程中我们都会在web.xml文件中进行下面一些配置,但是这些配置都是用来干嘛的呢?

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <!-- Spring配置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:config/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- logback配置 LogbackConfigListener由logback-ext-spring提供 -->
    <context-param>
        <param-name>logbackConfigLocation</param-name>
        <param-value>classpath:logconfig/logback.xml</param-value>
    </context-param>
    <listener>
        <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
    </listener>

    <!-- Spring MVC配置 -->
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/applicationMvcContext.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app> 

这里着重介绍ContextLoaderListener和DispatcherServlet。

  1. ContextLoaderListener是spring提供的类,为了在web容器中建立IoC容器而服务的。它实现了ServletContextListener这个接口,通过这个接口的方法实现servlet生命周期的回调,在这个过程中会通过XmlWebApplicationContext去加载bean的配置创建IoC容器。

  2. DispatcherServlet作为一个前端控制器,他会去加载另一个bean的配置形成一个IoC容器,这个IoC会将ContextLoaderListener加载的IoC容器作为父容器,这样的好处是从DispatcherServlet容器中getBean也能取到父容器中的bean,他会先去父容器中看有没有,如果找到直接返回了。DispatcherServlet 主要处理HTTP的请求分发(HandlerMapping),对controller、viewresolver、view进行管理。DispatcherServlet接收到请求由HandlerMapping进行匹配,匹配成功后交由controller进行业务逻辑的处理,业务逻辑处理完成后交由viewresolver进行数据的解析同时找到对应的view,最终由DispatcherServlet将view的结果render到浏览器进行解析。

0x03 controller的参数是如何被解析的

  • 在开发过程中遇到下面这样的controller方法怎么也得到不到us值,

    public String printWelcome(ModelMap model,ArrayList<String> us){}

  • 如果改成下面这样就可以正常得到us的值

    public String printWelcome(ModelMap model,String us){}

这是为什么?怎么解决?

  • 搜索后发现这样可以解决问题,但是sowhat?

    public String printWelcome(ModelMap model,@RequestParam("us[]") List<String> us){}

  • 所以需要来剖析下springMVC的源代码了,发现spring是通过下面这个方法进行controller参数解析的。

    org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments

解析参数值的代码(里面好多if else啊):

从这个图片可以看到通过if去判断paramName对应controller方法中的@RequestParam,pathVarName对应@PathVariable,没有加任何修饰会进入到attrName这个分支里面去解析参数。在解析的过程中会通过函数的参数类型去组装对象传递到RequestMapping类的方法中。

  • 如果是下面这样的方法参数又会怎样

    public String printWelcome(ModelMap model,@RequestParam("us[]") List<User> us){}

  • User是自定义的参数,spring会对这种参数进行一个特殊的处理处理方法如下:

    org.springframework.beans.TypeConverterDelegate#convertIfNecessary

在这个方法中有一段处理的代码,会根据List内部元素类型去判断类型是不准确并赋值。

到这里应该和controller相关的注解可以随便用了吧?

0x04 感想

在学校期间一直用着C、C++进行开发,其实也没怎么开发,只是作为装逼的一种手段,如今踏入工作岗位开始离C、C++很远了,又开始了0基础JAVA开发。作为一个门外汉来观望spring。
spring的东西太多了,也很权威大家基本上都用,所以后面还学要加强这方面的探索和学习,期待后面自己还能将spring的学习感想记录下来。这篇是在进行开发的过程中遇到上面提到的一个问题所以想深入了解其背后的实现方式,未来继续spring。

0x05 参考

在学习spring的过程中可以参考计文柯著的spring技术内幕,里面结合代码讲解了spring的各个重要的技术,需要一定的基础再去看比较好,值得推荐。