springboot

179 阅读19分钟

Spring Boot

查看版本:java -versionmvn -v

找到mvn安装目录,配置如下依赖:

<mirrors>
    <mirror>
      <id>nexus-aliyun</id>
      <mirrorOf>central</mirrorOf>
      <name>Nexus aliyun</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
</mirrors>


<profiles>
    <profile>
      <id>jdk-1.8</id>
      <activation>
        <activeByDefault>true</activeByDefault>
        <jdk>1.8</jdk>
      </activation>
      <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
      </properties>
    </profile>
</profiles>

一、创建一个Maven项目

配置

在pom.xml中添加:

引入Spring Boot父项目

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.3.4.RELEASE</version>
</parent>

引入依赖:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

新建一个主程序类

新建 com.hzc.boot包以及 MainApplication.java

添加@SpringBootApplication注解(告诉spring boot 这是一个 spring boot应用),以及运行主程序的run方法。

/**
 * 主程序类
 * 这是一个SpringBoot应用
 */
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

写一个控制器

controller/HelloController

添加Controller注解:

@Controller
public class HelloController {
}

处理何种请求,@RequestMapping("/hello")

@Controller
public class HelloController {

    @ResponseBody
    @RequestMapping("/hello")
    public String handle01() {
        return "Hello, Spring Boot 2 !";
    }

}

添加注解@ResponseBody表示在body中返回,,而我们想让所有请求的返回都在body中,则可以把@ResponseBody` 放到HelloController上。

在Spring Boot中 @RestController 可以代替 @ResponseBody@Controller注解:

//@ResponseBody
//@Controller

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String handle01() {
        return "Hello, Spring Boot 2 !";
    }
}

运行

直接运行 MainApplication,在浏览器打开localhost:8080/hello

抽取配置文件

在Spring Boot中,我们把所有的配置文件都抽取在一个配置文件resources/application.properties(以前在tomcat中配置等等)

server.port=8888

官网查看:docs.spring.io/spring-boot…

打包部署

引入插件:

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

把项目打成jar包,直接在目标服务器执行即可

打Maven包

选择IDEA右侧 Maven -> 项目 -> Lifecycle -> 选中clean和package运行

在target中可以看到jar包,直接执行:java -jar com.hzc-1.0-SNAPSHOT.jar

去掉cmd的快速编辑模式

右键cmd,属性,取消 快速编辑模式

二、SpringBoot-依赖管理

父项目做依赖管理

依赖管理
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

上面项目的父项目如下:
<parent>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

它几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
开发导入starter场景启动器
  1. 见到很多spring-boot-starter-**就是某种场景
  2. 只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
  3. 更多SpringBoot所有支持的场景
  4. 见到的*-spring-boot-starter:第三方为我们提供的简化开发的场景启动器。
所有场景启动器最底层的依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.3.4RELEASE</version>
    <scope>compile</scope>
</dependency>

无需关注版本号,自动版本仲裁

  • 引入依赖默认都可以不写版本
  • 引入非版本仲裁的jar,要写版本号

可以修改默认版本号

  • 查看spring-boot-dependencies里面规定当前依赖的版本用的 key。
  • 在当前项目里面重写配置,如下面的代码
<properties>
	<mysql.version>5.1.43</mysql.version>
</properties>
IDEA快捷键
  • ctrl + shift + alt + u:以图的方式显示项目中依赖之间的关系。
  • alt + ins:创建新类、新包等。

自动配置特性

自动配好Tomcat

  • 引入Tomcat依赖
  • 配置Tomcat
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.3.4.RELEASE</version>
    <scope>compile</scope>
</dependency>

自动配好SpringMVC

  • 引入Spring MVC全套组件
  • 自动配好Spring MVC常用组件(功能)

自动配好Web常见功能,如:字符编码问题

  • SpringBoot帮我们配置好了所有web开发的常见场景(在容器中有就会生效)
public static void main(String[] args) {
    // 1. 返回我们的IOC容器
    ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
    
    // 2. 查看容器里面的组件
    String[] names = run.getBeanDefinitionNames();
    for(String name : names) {
        System.out.println(name);
    }
}

默认的包结构

  • 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
  • 无需以前的包扫描配置
  • 想要改变扫描路径
    • @SpringBootApplication(canBasePackages="com.lun")
    • @ComponentScan指定扫描路径
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.lun")

各种配置拥有默认值

  • 默认配置最终都是映射到某个类上,如:MultipartProperties
  • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象

按需加载所有自动配置项

  • 非常多的starter
  • 引入了哪些场景这个场景的自动配置才会开启
  • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

三、底层注解

1. 底层注解 @Configuration 讲解

基本使用

  • Full模式与Lite模式
  • 示例
/**
 * 1. 配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
 * 2. 配置类本身也是组件
 * 3. proxyBeanMethods: 代理bean的方法
 *      Full(proxyBeanMethods = true)、(保证每个@Bean方法被调用多少次返回的组件都是单实例的)(默认)
 *      Lite(proxyBeanMethods = false)(每个@Bean方法被调用多少次返回的组件都是新创建的)
 *      组件依赖
 */
@Configuration(proxyBeanMethods = true)  // 告诉SpringBoot这是一个配置类 ==配置文件
public class MyConfig {

    @Bean   // 给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回值,就是组件在容器中的实例
    public User user01() {
        User zhangsan = new User("zhangsan", 18);
        // user组件依赖了Pet组件
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom")
    public Pet tomcatPet() {
        return new Pet("tomcat");
    }
}

@Configuration测试代码:

/**
 * 主程序类
 * 这是一个SpringBoot应用
 */
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        // 1. 返回我们IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

        // 2. 查看容器里面的组件
        String[] names = run.getBeanDefinitionNames();
        for(String name : names) {
            System.out.println(name);
        }

        // 3. 从容器中获取组件

        Pet tom01 = run.getBean("tom", Pet.class);
        Pet tom02 = run.getBean("tom", Pet.class);
        System.out.println("组件:" + (tom01 == tom02));

        // 4. com.atguigu.boot.config.MyConfig...
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);

        // 如果@Configuration(proxyBeanMethods = true) 代理对象调用方法。SpringBoot总会检查这个组件是否在容器中
        // 保持组件单实例
        User user = bean.user01();
        User user1 = bean.user01();
        System.out.println(user == user1);

        User user01 = run.getBean("user01", User.class);
        Pet tom = run.getBean("tom", Pet.class);

        System.out.println("用户的宠物:" + (user01.getPet() == tom));
    }
}

最佳实战

  • 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
  • 配置 类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(默认)

IDEA快捷键:

  • Alt + Ins:生成getter、setter构造器等代码
  • Ctrl + Alt + B:查看类的具体实现代码。

2. 底层注解-@Import导入组件

@Bean@Component@Controller@Service@Repository,它们是spring的基本标签,在Spring Boot中并未改变它们原来的功能。

@Import({User.class, DBHelper.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名

@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false)	// 告诉SpringBoot这是一个配置累 == 配置文件
public class MyConfig {}

测试类:

// 1. 返回我们IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

// ...

// 5. 获取组件
String[] beanNamesForType = run.getBeanNamesForType(User.class);

for(String s : beanNamesForType) {
  System.out.println(s);
}

DBHelper bean1 = run.getBean(DBHelper.class);
System.out.println(bean1);

3. 【源码分析】 - 请求映射原理

图片

SpringMVC功能分析都从org.springframework.web.servlet.DispatcherServlet->doDispatch()

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
    try {
        ModelAndView mv = null;
        Exception dispatchException = null;
        
        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            
            // 找到当前请求使用哪个Handler (Controller的方法)处理
            mappedHandler = getHandler(processedRequest);
            
            // HandlerMapping: 处理器映射。/xxx->>xxxx
            ...
        }
    }
}

getHandler()方法下:

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if(this.handlerMappings != null) {
        for(HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if(handler != null) {
                return handler;
            }
        }
    }
    return null;
}

this.handlerMappings在Debug模式下展现的内容:

图片

其中,保存了所有@RequestMappinghandler的映射规则。

图片

所有的请求映射都在HandlerMapping中:

  • SpringBoot自动配置欢迎页的WelcomePageHandlerMapping。访问/能访问到index.html;
  • SpringBoot自动配置了默认的RequestMappingHandlerMapping。
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义HandlerMapping

IDEA快捷键:

  • Ctrl + Alt + U:以UML的类图展现类有哪些继承类,派生类以及实现哪些接口。
  • Ctrl + Alt + Shift + U:同上,区别在于上条快捷键结果在新页展现,而本条快捷键结果在弹窗展现。
  • Ctrl + H:以树形方式展现类层次结构图。

4. 常用参数注解使用

注解:

  • @PathVariable路径变量
  • @RequestHeader获取请求头
  • @RequestParam获取请求参数(指问号后的参数,url?a=1&b=2)
  • @CookieValue获取Cookie值
  • @RequestAttribute获取request域属性
  • @RequestBody获取请求体[POST]
  • @MatrixVariable矩阵变量
  • @ModelAttribute

使用用例:

@RestController
public class ParameterTestController {
    
    // car/2/owner/zhangsan
    @GetMapping("/car/{id}/owner/{username}")
    public Map<String, Object> getCar(@PathVariable("id") Integer id,
                                      @PathVariable("username") String name,
                                      @PathVariable Map<String, String> pv,
                                      @RequestHeader("User-Agent") String userAgent,
                                      @RequestHeader Map<String, String> params,
                                      @CookieValue("_ga") String _ga,
                                      @CookieValue("_ga") Cookie cookie) {
        Map<String, Object> map = new HashMap<>();
        
        // map.put("id", id);
        // map.put("name", name);
        // map.put("pv", pv);
        // map.put("userAgent", userAgent);
        // map.put("headers", header);
        map.put("age", age);
        map.put("inters", inters);
        map.put("params", params);
        map.put("_ga", ga);
        System.out.println(cookie.getName() + "===>" + cookie.getValue());
        return map;
    }
    
    @PostMapping("/save")
    public Map postMethod(@RequestBody String content) {
        Map<String, Object> map = new HashMap<>();
        map.put("content", content);
        return map;
    }
}

5. @RequestAttribute

@Controller
public class RequestController {
    
    @GetMapping("/goto")
    public String goToPage(HttpServletRequest request) {
        request.setAttribute("msg", "成功了...");
        request.setAttribute("code", 200);
        return "forward:/success";	// 转发到 /success请求
    }
    
    @GetMapping("/params")
    public String testParam(Map<String, Object> map,
                           Model model,
                           HttpServletRequest request,
                           HttpServletResponse response) {
        map.put("hello", "world666");
        model.addAttribute("world", "hello666");
        request.setAttribute("message", "HelloWorld");
        
        Cookie cookie = new Cookie("c1", "v1");
        response.addCookie(cookie);
        return "forward:/success";
    }
    
    // <------------------主角@RequestAttribute在这个方法
    @ResponseBody
    @GetMapping("/success")
    public Map success(@RequestAttribute(value = "msg", required = false) String msg,
                      @RequestAttribute(value = "code", required = false) Integer code,
                      HttpServletRequest request) {
        Object msg1 = request.getAttribute("msg");
        
        Map<String, Object> map = new HashMap<>();
        Object hello = request.getAttribute("hello");
        Object world = request.getAttribute("world");
        Object message = request.getAttribute("message");
        
        map.put("reqMethod_msg", msg1);
        map.put("annotation_msg", msg);
        map.put("hello", hello);
        map.put("world", world);
        map.put("message", message);
        
        return map;
    }
}

6. @MatrixVariable与UrlPathHelper

  1. 语法:请求路径:/cars/sell;low=34;brand=byd,audi,yd
  2. SpringBoot默认是禁用了矩阵变量的功能
    1. 手动开启:原理。对于路径的处理。UrlPathHelper的removeSemicolonContent设置为false,让其支持矩阵变量的。
  3. 举证变量必须有url路径变量才能被解析
手动开启矩阵变量

实现WebMvcConfigurer接口:

@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        // 不移除;后面的内容。矩阵变量功能就可以生效
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

创建返回WebMvcConfigurerBean:

@Configuration(proxyBeanMethods = false)
public class WebConfig {
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                // 不移除;后面的内容。矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        }
    }
}

@MatrixVariable的用例

@RestController
public class ParameterTestController {
    
    ///cars/sell;low=34;brand=byd,audi,yd
    @GetMapping("/cars/{path}")
    public Map carsSell(@MetrixVariable("low") Integer low,
                       @MatrixVar("brand") List<String> brand,
                       @PathVariable("path") String path) {
        Map<String, Object> map = new HashMap<>();
        
        map.put("low", low);
        map.put("brand", brand);
        map.put("path", path);
        return map;
    }
    
    // /boss/1;age=20/2;age=10
    
    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value = "age", pathVar = "bossId") Integer bossAge,
                   @MatrixVariable(value = "age", pathVar = "empId") Integer empAge) {
        Map<String, Object> map = new HashMap<>();
        
        map.put("bossAge", bossAge);
        map.put("empAge", empAge);
        return map;
    }
}

7. 【源码分析】- 各种类型参数解析原理

这要从DispatcherServlet开始说起:

public class DispatcherServlet extends FrameworkServlet {
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            
            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if(mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                
                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                ...
            }
        }
    }
}
  • HandlerMapping中找到能处理请求的Handler(Controller.method())。
  • 为当前Handler找一个适配器HandlerAdapter,用的最多的是RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值。
HandlerAdapter

默认会加载所有HandlerAdapter

public class DispatcherServlet extends FrameworkServlet {
    
    /** Detect all HandlerAdapters or just expect "handlerAdapter" bean?. */
    private boolean detectAllHandlerAdapters = true;
    
    // ...
    private void initHandlerAdatapters(ApplicationContext context) {
        this.handlerAdapters = null;
        
        if(this.detectAllHandlerAdapters) {
            // Find all HandlerAdapters in the ApplicationContext, including ancesotr contexts.
            Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
            if(!matchingBeans.isEmpty()) {
                this.handlerAdapters = new ArrayList<>(matchingBeans.values());
                // We keep HandlerAdapters in sorted order.
                AnnotationAwareOrderComparator.sort(this.handlerAdapters);
            }
        }
    }
}

有这些HandlerAdapter

图片

  1. 支持方法上标注@RequestMapping
  2. 支持函数式编程的
  3. ....
执行目标方法
public class DispatcherServlet extends FrameworkServlet {
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = null;
        
        // ...
        
        // Determin handler for the current request.
        mappedHandler = getHandler(processedRequest);
        if(mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
        }
        
        // Determin handler adapter for the current request.
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        
        // ...
        // 本节重点
        // Actually invoke the handler
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    }
}

HandlerAdapter接口实现类RequestMappingHandlerAdapter(主要用来处理@RequestMapping

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
    // ...
    
    // AbstractHandlerMethodAdapter类的方法,RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throw Exception {
        return handleInternal(request, response, (HandlerMethod) handler);
    }
    
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav;
        // handleInternal的核心
        mav = invokeHandlerMethod(request, response, handlerMethod);
        // ...
        return mav;
    }
}
参数解析器

确定将要执行的目标方法的每一个参数的值是什么;

SpringMVC目标方法能写多少种参数类型。取决于参数解析器argumentResolvers

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception{
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
        
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if(this.argumentResolvers != null) {	// <-----关注点
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        // ...
    }
}

this.argumentResolversafterPropertiesSet()方法内初始化

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
    
    @Nullable
    private HandlerMethodArgumentResolverComposite argumentResolvers;
    
    @Override
    public void afterPropertiesSet() {
        // ...
        if(this.argumentResolvers == null) {	// 初始化argumentResolvers
            List<HandlerMethodArgumentResolver> resolvers = getDefaultArumentResolvers();
            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        //...
    }
    
    // 初始化了一堆的实现HandlerMethodArgumentResolver接口的
    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
        
        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
        resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());
        
        // Type-based argment resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RedirectAttributesMethodArgumentResolver());
		resolvers.add(new ModelMethodProcessor());
		resolvers.add(new MapMethodProcessor());
		resolvers.add(new ErrorsMethodArgumentResolver());
		resolvers.add(new SessionStatusMethodArgumentResolver());
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
        if(KotlinDetector.isKotlinPresent()) {
            resolvers.add(new ContinuationHandlerMethodArgumentResolver());
        }
        
        // Custom arguments
        if(getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArumentResolvers());
        }
        
        // Catch-all
        resolvers.add(new PrincipalMethodArgumentResolver());
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));
        
        return resolvers;
    }
}

HandlerMethodArgumentResolverComposite类如下:(众多参数解析器argumentResolvers的包装类)。

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
    // ...
    public HandlerMethodArgumentResolverComposite addResolvers(
    	@Nullable HandlerMethodArgumentResolver... resolvers
    ) {
        if(resolvers != null) {
            Collections.addAll(this.argumentResolvers, resolvers);
        }
        return this;
    }
    // ...
}

我们看看HandlerMethodArgumentResolver的源码:

public interface HandlerMethodArgumentResolver {
    // 当期那解析器是否支持解析这种参数
    boolean supportsParameter(MethodParameter parameter);
    
    @Nullable	// 如果支持,就调用 resolveArgument
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
返回值处理器

ValueHandler

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
        
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if(this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if(this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // ...
    }
}

this.returnValueHandlersafterPropertiesSet()方法内初始化

public class RequestMappingHandlerAdapter extends AbstarctHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
    @Nullable
    private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
    
    @Override
    public void afterPropertiesSet() {
        // ...
        if(this.returnValueHandlers == null) {
            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
        }
    }
    
    // 初始化了一堆的实现HandlerMethodReturnValueHandler接口的
    private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
        List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
        
        // Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
				this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

        // Annotation-based return value types
        handlers.add(new ServletModelAttributeMethodProcessor(false));
        handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
        
        // Multi-purpose return value types
        handlers.add(new ViewNameMethodReturnValueHandler());
        handlers.add(new MapMethodProcessor());
        
        // Custom return value types
        if(getCustomReturnValueHandlers() != null) {
            handlers.addAll(getCustomReturnValueHandlers());
        }
        
        // Catch-all
        if(!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
            handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
        } else {
            handlers.add(new ServletModelAttributeMethodProcessor(true));
        }
        
        return handlers;
    }
}

HandlerMethodReturnValueHandlerComposite类如下:

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
    
    private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
    
    // ...
    
    public HandlerMethodReturnValueHandlerComposite addHandlers(
    	@Nullable List<? extends HandlerMethodReturnValueHandler> handlers) {
        if(handlers != null) {
            this.returnValueHandlers.addAll(handlers);
        }
        return this;
    }
}

HandlerMethodReturnValueHandler接口:

public interface HandlerMethodReturnValueHandler {
    boolean supportsReturnType(MethodParameter returnType);
    
    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
回顾执行目标方法
public class DispatcherServlet extends FrameworkServlet {
    // ...
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = null;
        // ...
        mv = ha.handle(processedRequest, response, mappedHandler.getHander());
    }
}

RequestMappingHandlerAdapterhandle()方法:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializaingBean {
    // ...
    
    // AbstactHandlerMethodAdapter类的方法,RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return handleInternal(request, response, (HandlerMethod) handler);
    }
    
    @Overrid
    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav;
        // handleInternal的核心
        mav = invokeHandlerMethod(request, response, handlerMethod);	// 解释看下节
        // ...
        return mav;
    }
}

RequestMappingHandlerAdapterinvokeHandlerMethod()方法:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                              HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // ...
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if(this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArguemntResolvers(this.argumentResolvers);
            }
            if(this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            // ...
            
            // 关注点:执行目标方法
            invocableMethod.onvokeAndHandle(webRequest, mavContainer);
            if(asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
            
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }
}

invokeAndHandle()方法如下:

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... provideArgs) throws Exception {
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        // ...
        
        try {
            // returnValue存储起来
            this.returnValueHandlers.handleReturnValue(
            	returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        } catch(Exception ex) {
            // ...
        }
    }
    
    @Nullable	// InvocableHandlerMethod类的,ServletInvocableHandleMethod类继承InvocableHandlerMethod类
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... provideArgs) throws Exception {
        // 获取方法的参数值
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        // ...
        return doInvoke(args);
    }
    
    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
		Method method = getBridgedMethod();	// @RequestMapping的方法
        ReflectionUtils.makeAccessible(method);
        try {
            if(KotlinDetector.isSuspendingFunction(method)) {
                return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
            }
            // 通过反射调用
            return method.invoke(getBean(), args);	// getBean() 指@RequestMapping的方法所在类的对象。
        }
        catch(IllegalArumentException ex) {
            // ...
        }
        catch(InvocationTargetEception ex) {
            // ...
        }
    }
}
如何确定目标方法每一个参数的值

重点分析ServletInvocableHandlerMethodgetMethodArgumentValues方法

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    // ...
    
    @Nullable	// InvocableHandlerMethod类的,ServletInvocableHandlerMethod类继承InvocableHandlerMethod类
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // 获取方法的参数值
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        // ...
        return doInvoke(args);
    }
    
    // 本节重点,获取方法的参数值
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        MethodParameter[] parameters = getMethodParameters();
        if(ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }
        
        Object[] args = new Object[parameters.length];
        for(int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = findProvidedArgument(parameter, providedArgs);
            if(args[i] != null) {
                continue;
            }
            // 查看resolvers是否有支持
            if(!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 支持的话开始解析吧
                arg[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch(Exception ex) {
                // ...
            }
        }
        return args;
    }
}

this.resolvers的类型为HandlerMethodArgumentResolverComposite(在参数解析器章节提及)

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return getArgumentResolver(parameter) != null;
    }
    
    @Override
    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if(resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "].supportsParameter should be called first.");
        }
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
    
    @Nullable
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        if(result == null) {
            // 挨个判断所有参数解析器那个支持解析这个参数
            for(HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
                if(resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, result);	// 找到了,resolver就缓存起来,方便稍后resolveArgument()方法使用
                    break;
                }
            }
        }
        return result;
    }
}
小结

本节描述,一个请求发送到DispatcherServlet后的具体处理流程,也就是SpringMVC的主要原理。

本节内容较多且硬核,对日后编程很有帮助,需耐心对待。

可以运行一个示例,打断点,在Debug模式下,查看程序流程。

8. 【源码分析】- Servlet API参数解析原理

  • WebRequest
  • ServletRequest
  • MultipartRequest
  • HttpSession
  • javax.servlet.http.PushBuilder
  • Principal
  • InputStream
  • Reader
  • HttpMethod
  • Locale
  • TimeZone
  • ZoneId

ServletRequestMethodArgumentResolver用来处理以上的参数

public class ServletRequestMethodArgumentResolver implemnets HandlerMethodArgumentResolver {
    
    @Nullable
    private static Class<?> pushBuilder;
    
    static {
        try {
            pushBuilder = ClassUtils.forName("javax.servlet.http.PushBuilder", ServletRequestMethodArgumentResolver.class.getClassLoader());
        } catch(ClassNotFoundException ex) {
            // Servlet 4.0 PushBuilder not found - not supported for injection
            pushBuilder = null;
        }
    }
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        Class<?> paramType = parameter.getParameterType();
        return (WebRequest.class.isAssignableFrom(paramType) || 
               ServletRequest.class.isAssignableFrom(paramType) ||
               MultipartRequest.class.isAssignableFrom(paramType) ||
               HttpSession.class.isAssignableFrom(paramType) ||
               (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
               (Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
               InputStream.class.isAssignableFrom(paramType) ||
               Reader.class.isAssignableFrom(paramType) ||
               HttpMethod.class == paramType ||
               Locale.class == paramType ||
               TimeZone.class == paramType ||
               ZoneId.class == paramType);
    }
    
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        Class<?> paramType = parameter.getParameterType();
        
        // WebRequest / NativeWebRequest / ServletWebRequest
        if(WebRequest.class.isAssignableFrom(paramType)) {
            if(!paramType.isInstance(webRequest)) {
                throw new IllegalStateException(
                	"Current request is not of type [" + paramType.getName() + "]: " + webRequest
                );
            }
            return webRequest;
        }
        
        // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
        if(ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
            return resolveNativeRequest(webRequest, paramType);
        }
        
        // HttpServletRequest required for all further argument types
        return resolveArugmnet(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
    }
    
    private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
        T nativeRequest = webRequest.getNativeRequest(requiredType);
        if(nativeRequest == null) {
            throw new IllegalStateException(
            	"Current request is not of type [" + requiredType.getName() + "]: " + webRequest
            );
        }
        return nativeRequest;
    }
    
    @Nullable
    private Object resolveAruemnt(Class<?> paramType, HttpServletRequest request) throws IOException {
        if(HttpSession.class.isAssignableFrom(paramType)) {
            HttpSession session = request.getSession();
            if(session != null && !paramType.isInstance(session)) {
                throw new IllegalStateException(
						"Current session is not of type [" + paramType.getName() + "]: " + session);
            }
            return session;
        } else if(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) {
            return PushBuilderDelegate.resolvePushBuilder(request, paramType);
        } else if(InputStream.class.isAssignableFrom(paramType)) {
            InputStream inputStream = request.getInputStream();
            if(inputStream != null && !paramType.isInstance(inputStream)) {
                throw new IllegalStateException(
						"Request input stream is not of type [" + paramType.getName() + "]: " + inputStream);
            }
            return inputStream;
        } else if(Reader.class.isAssignableFrom(paramType)) {
            Reader reader = request.getReader();
            if(reader != null && !paramType.isInstance(reader)) {
                throw new IllegalStateException(
						"Request body reader is not of type [" + paramType.getName() + "]: " + reader);
            }
            return reader;
        } else if(Principal.class.isAssignableFrom(paramType)) {
            Principal userPrincipal = request.getUserPrincipal();
            if(userPrincipal != null && !paramType.isInstance(userPrincipal)) {
                throw new IllegalStateException(
						"Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);
            }
            return userPrincipal;
        } else if(HttpMethod.class == paramType) {
            return HttpMethod.resolve(request.getMethod());
        } else if(Locale.class == paramType) {
            return RequestContextUtils.getLocale(request);
        } else if(TimeZone.class == paramType) {
            TimeZone timeZone = RequestContextUtils.getTimeZone(request);
            return (timeZone != null ? timeZone : TimeZone.getDefault());
        } else if(ZoneId.class == paramType) {
            TimeZone timeZone = RequestContextUtils.getTimeZone(request);
            return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());
        }
        
        // Should never happen...
        throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());
    }
    
    /**
    * Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.
    */
    private static class PushBuilderDelegate {
        
        @Nullable
        public static Object resolvePushBuilder(HttpServletRequest request, Class<?> paramType) {
            PushBuilder pushBuilder = request.newPushBuilder();
            if(pushBuilder != null && !paramType.isInstance(pushBuilder)) {
                throw new IllegalStateException(
                "Current push builder is not of type [" + paramType.getName() + "]: " + pushBuilder);
            }
            return pushBuilder;
        }
    }
}

用例:

@Controller
public class RequestController {
    @GetMapping("/goto")
    public String goToPage(HttpServletRequest request) {
        request.setAttribute("msg", "成功了....");
        request.setAttribute("code", 200);
        return "forward:/success";	// 转发到	/success请求
    }
}

9. 【源码分析】- Model、Map原理

复杂参数:

  • Map
  • Model(map、model里面的数据会被放在request的请求域 request.setAttribute)
  • Errors/BindingResult
  • RedirectAttributes(重定向携带数据)
  • ServletResponse(response)
  • SessionStatus
  • UriComponentsBuilder
  • ServletUriComponentsBuilder

用例:

@GetMapping("/params")
public String testParam(Map<String, Object> map,
                       Model model,
                       HettpServletRequest request,
                       HttpServletResponse response) {
    // 下面三位都是可以给request域中放数据
    map.put("hello", "world666");
    model.addAttribute("world", "hello666");
    request.setAttribute("message", "HelloWorld");
    
    Cookie cookie = new Cookie("c1", "v1");
    response.addCookie(cookie);
    return "forward:/success";
}

@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute(value = "msg", required = false) String msg,
                  @RequestAttribute(value="code", required = false) Integer code,
                  HttpServletRequest request) {
    Object msg1 = request.getAttribute("msg");
    
    Map<String, Object> map = new HashMap<>();
    Object hello = request.getAttribute("hello");	// 得出testParam方法赋予的值 world666
    Object world = request.getAttribute("world");	// 得出testParam方法赋予的值 hello666
    Object message = request.getAttribute("message");	// 得出testParam方法赋予的值 HelloWorld
    
    map.put("reqMethod_msg", msg1);
    map.put("annotation_msg", msg);
    map.put("hello", hello);
    map.put("world", world);
    map.put("message", message);
    
    return map;
}
  • Map<String, Object> map
  • Model model
  • HttpServletRequest request

上面三位都是可以给request域中放数据,用request.getAttribute()获取

接下来我们看看,Map mapModel model用什么参数处理器。

Map<String, Object> map参数用MapMethodProcessor处理:

public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
               parameter.getParameterAnnotations().length === 0);
    }
    
    @Override
    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
        return mavContainer.getModel();
    }
    // ....
}

mavContainer.getModel()如下:

public class ModelAndViewContainer {
    // ...
    private final ModelMap defaultModel = new BindingAwareModelMap();
    
    @Nullable
    private ModelMap redirectModel;
    // ...
    public ModelMap getModel() {
        if(useDefaultModel()) {
            return this.defaultModel;
        } else {
            if(this.redirectModel == null) {
                this.redirectModel = new ModelMap();
            }
            return this.redirectModel;
        }
    }
    
    private boolean useDefaultModel() {
        return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
    }
    // ...
}

Model modelModelMethodProcessor处理:

public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return Model.class.isAssignableFrom(parameter.getParameterType());
    }
    
    @Override
    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderyFactory binderFactory) throws Exception {
        Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
        return mavContainer.getModel();
    }
    // ...
}

return mavContainer.getModel();这跟MapMethodProcessor的一致

图片

Model也是另一种意义的Map

接下来看看Map<String, Object> mapModel model值是如何做到用request.getAttribute()获取的。

众所周知,所有的数据都放在ModelAndView包含要去的页面地址View,还包含Model数据。

先看ModelAndView接下来是如何处理的?

public class DispatcherServlet extends FrameworkServlet {
    
    // ...
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // ...
        try {
            ModelAndView mv = null;
            // ...
            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // ...
        }
        catch(Exception ex) {
            dispatchException = ex;
        }
        catch(Throwable err) {
            // As of 4.3 we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 处理分发结果
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    // ...
}

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
    // ...
    // Did the handler return a view to render?
    if(mv != null && !mv.wasCleared()) {
        render(mv, request, response);
        // ...
    }
    // ...
}

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // ...
    View view;
    String viewName = mv.getViewName();
    if(viewName != null) {
        // We need to resolve the view name.
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if(view == null) {
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'");
        }
    } else {
        // No need to lookup: the ModelAndView object contains the actual View object.
        view = mv.getView();
        if(view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
        }
    }
    view.render(mv.getModelInternal(), request, response);
    // ...
}

在Debug模式下,view属为InternalResourceView类。

public class InternalResourceView extends AbstractUrlBasedView {
    @Override // 该方法在AbstractView, AbstractUrlBasedView 继承了AbstractView
    public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // ...
        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
        prepareResponse(request, response);
        
        // 看下一个方法实现
        renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
    }
    
    @Override
    protected void renderMergedOutputModel(
    	Map<String, Object> model,HttpServletRequest request, HttpServletResponse response
    ) throws Exception {
        // Expose the model object as request attributes.
        // 暴露模型作为请求域属性
        exposeModelAsRequestAttributes(model, request);	// <--- 重点
        
        // Expose helpers as request attributes, if any.
        exposeHelpers(request);
        
        // Determine the path for the request dispatcher.
        String dispatcherPath = prepareForRendering(request, response);
        
        // Obtain a RequestDispatcher for the target resource (typically a JSP).
        RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
        // ...
    }
    
    // 该方法在AbstractView,AbstractUrlBasedView继承了AbstactView
    protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
        model.forEach((name, value) -> {
            if(value != null) {
                request.setAttribute(name, value);
            } else {
                request.removeAttribute(name);
            }
        })
    }
}

exposeModelAsRequestAttributes方法看出,Map<String, Object> mapModel model这两种类型数据可以给request域中放数据,用request.getAttribute()获取。

9. 【源码分析】- 自定义参数绑定原理

@RestController
public class ParameterTestController {
    /**
    * 数据绑定:页面提交的请求数据(GET、POST)都可以和对象属性进行绑定
    * @param person
    * @return
    */
    @PostMapping("/saveuser")
    public Person saveuser(Person person) {
        return person;
    }
}
/**
 *     姓名: <input name="userName"/> <br/>
 *     年龄: <input name="age"/> <br/>
 *     生日: <input name="birth"/> <br/>
 *     宠物姓名:<input name="pet.name"/><br/>
 *     宠物年龄:<input name="pet.age"/>
 */
@Data
public class Person {
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
}

@Data
public class Pet {
    private String name;
    private String age;
}

封装过程用到ServletModelAttributeMethodProcessor

public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
    
    @Override	// 本方法在ModelAttributeMethodProcessor类
    public boolean supportsParameter(MethodParameter parameter) {
        return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
               (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
    }
    
    @Override
    @Nullable	// 本方法在ModelAttributeMethodProcessor类
    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        // ...
        String name = ModelFactory.getNameForParameter(parameter);
        ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
        if(ann != null) {
            mavContainer.setBinding(name, ann.binding());
        }
        
        Object attribute = null;
        BindingResult bindingResult = null;
        
        if(mavContainer.containsAttribute(name)) {
            attribute = mavContainer.getModel().get(name);
        } else {
            // Create attribute instance
            try {
                attribute = createAttribute(name, parameter, binderFactory, webRequest);
            }
            catch(BindException ex) {
                // ...
            }
        }
        
        if(bindingResult == null) {
            // Bean property binding and validation;
            // skipped in case of binding failure on construction
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if(binder.getTarget() != null) {
                if(!mavContainer.isBindingDisabled(name)) {
                    // web数据绑定器,将请求参数的值绑定到指定的JavaBean里面**
                    bindRequestParameters(binder, webRequest);
                }
                validateIfApplicable(binder, parameter);
                if(binder.getBindResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new BindException(binder.getBindingResult());
                }
            }
            // Value type adaptation, also covering java.util.Optional
            if(!parameter.getParameterType().isInstance(attribute)) {
                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
            }
            bindingResult = binder.getBindingResult();
        }
        
        // Add resolved attribute and BindingResult at the end of the model
        Map<String, Object> bindingResultModel = bindingResult.getModel();
        mavContainer.removeAttributes(bindingResultModel);
		mavContainer.addAllAttributes(bindingResultModel);

		return attribute;
    }
}

WebDataBinder利用它里面的Converters将请求数据转成指定的数据类型。再次封装到JavaBean中

在过程当中,用到GenericConversionService:在设置每一个值的时候,找它里面的所有converter哪个可以将这个数据类型(request带来参数的字符串)转换到指定的类型

10. 【源码分析】- 自定义Converter原理

未来我们可以给WebDataBinder里面放自己的Converter;

下面演示将字符串"阿猫,3"转换成Pet对象。

// 1. WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverter(new Converter<String, Pet>() {
                @Override
                public Pet conver(String source) {
                    // 阿猫,3
                    if(!StringUtils.isEmpty(source)) {
                        Pet pet = new Pet();
                        String[] split = source.split(",");
                        pet.setName(split[0]);
                        pet.setAge(Integer.parseInt(split[1]));
                        return pet;
                    }
                    return null;
                }
            })
        }
    }
}