Spring MVC source code analysis

think123 2020-11-11 15:51:57
spring mvc source code analysis


Powerful DispatcherServlet

Remember when web.xml Configured in DispatcherServlet Do you ? In fact, that is SpringMVC The entrance to the frame , This is also struts2 and springmvc One of the differences ,struts2 It's through filter Of , and springmvc It's through servlet Of . look down servlet Structure diagram
 chart
 Class diagram

From the picture above, it is obvious that DispatcherServlet and Servlet as well as Spring The relationship between . And our focus today is on DispatchServlet Speaking of .

I use SpringBoot Set up a very simple background project , Used to analyze . The code is as follows

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private Integer age;
private String address;
public User() {
}
}
/**
* @author generalthink
*/
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public User getUser(HttpServletRequest request,@PathVariable Integer id) {
// Create a user, Don't go to the database just for analysis springmvc Source code
User user = User.builder()
.id(id)
.age(ThreadLocalRandom.current().nextInt(30))
.name("zzz" + id)
.address(" Chengdu ").build();
return user;
}
@RequestMapping(value = "/condition",method = RequestMethod.GET)
public User getByNameOrAge(@RequestParam String name,@RequestParam Integer age) {
User user = User.builder().name(name).age(age).address(" Chengdu ").id(2).build();
return user;
}
@PostMapping
public Integer saveUser(@RequestBody User user) {
Integer id = user.getName().hashCode() - user.getAge().hashCode();
return id > 0 ? id : -id;
}
}

In order to facilitate debugging, we focus more on SpringMVC Source code , So the data here are all fake . And the focus here is on using annotations Controller(org.springframework.stereotype.Controller), instead of Controller Interface (org.springframework.web.servlet.mvc.Controller), The difference between the two is mainly concerned with the annotation only , One needs to implement the interface , But they all perform the basic function of processing requests . We all know that visiting servlet The default is to visit service Methodical , So we hit the breakpoint at HttpServlet Of service In the method , At this point, you can view the entire call stack as follows
 The call stack
From here we also know how to ask from servlet here we are DispatcherServlet Of , So let's see DispatcherServlet Of doDiapatch The method logic of , Here's the core logic , Remove some other non core logic

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// Notice what's put back here is HandlerExecutionChain object
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
ModelAndView mv = null;
Exception dispatchException = null;
// Check for file uploads
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Based on the current request obtain handler,handler Contains the request url, And the final positioning of controller as well as controller The method in
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// adopt handler Get the corresponding adapter , Mainly complete the parameter analysis
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// call Controller The method in
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

You can see that the core logic is actually very simple , First check if it's multipart request, If it is, then for the current request Do some packaging ( Extract files, etc ), Then get the corresponding handler( Saved the request url Corresponding controller as well as method And a series of Interceptor), And then through handler Get the corresponding handlerAdapter( Parameter assembly ), It is used to call the final method

analysis multipart

So how to resolve that the current request is a file upload request ? Here you go straight to checkMultipart Method to see how to parse :

// I simplified the code , Only the core logic is extracted
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
return this.multipartResolver.resolveMultipart(request);
}
return request;
}

It can be seen from here that through multipartResolver Determine whether the current request is a file upload request , If so, return MultipartHttpServletRequest( Inherited from HttpServletRequest). If not, go back to the original request object .
So here comes the question multipartResolver When was it initialized ?

We are idea You can directly locate the breakpoint to multipartResolver Attribute , When you request access, you will find that the breakpoint directly enters initMultipartResolver In the method , Then trace the entire call stack , You can see that the call relationship is as follows :
 initialization multipartResovler
The graph shows that it is initializing servlet The time is right multipartResolver Initialized .

private void initMultipartResolver(ApplicationContext context) {
// from Spring In order to get id by multipartResolver Class
this.multipartResolver = context.getBean("multipartResolver", MultipartResolver.class);
}

MultipartResolver Interface with CommonsMultipartResolve as well as StandardServletMultipartResolver2 Kind of implementation ,CommonsMultipartResolver Interfaces depend on commons-upload Component implemented , and StandardServletMultipartResolver It depends on Servlet Of part(servlet3 Just exist ) Realized . Both methods determine whether it is a file upload request isMultipart Whether the request method is post as well as content-type Does the head contain multipart/ To judge .

DispatchServlet What is initialized

protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); // initialization multipartResolver
initLocaleResolver(context);// initialization localeResolver
initThemeResolver(context);// initialization themResolver
initHandlerMappings(context);// initialization handerMappings
initHandlerAdapters(context);// initialization handlerAdapters
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);// Initialize attempt parser
initFlashMapManager(context);
}

These initialization contents will be used one by one later , Here's an impression .

Obtain on request mapperHandler

Or into getHander To see what's done ?

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

according to HandlerMapping To see the corresponding handler, So go into initHandlerMappings Method to see how to initialize handlerMappings
 initialization handlerMappings

Which gets the default handlerMappings Yes spring-webmvc Of org.springframework.web.servlet Medium DispatcherServlet.properties Search for , The content of the file is as follows
DispatcherServlet.properties
because detechAllhanderMappings The default is true, So you get all the HanderMapping Implementation class of , Let's take a look at its class diagram structure
HandlerMapping Class diagram
this.handlerMappings Value
These are a few HandlerMapping Its function is as follows :
SimpleUrlHandlerMapping : Allow to specify explicitly URL Patterns and Handler The mapping relation of , One was maintained internally urlMap To make sure url and handler The relationship between
BeanNameUrlHandlerMapping: Appoint URL and bean The mapping of names , Not commonly used , Our focus is also mainly on RequestMappingHandlerMapping in

It's basically clear here HandlerMapping The role of : help DispatcherServlet Conduct Web Requested URL Matching to concrete classes , It's called HandlerMapping Because in SpringMVC China is not limited to
Must be annotated Controller We can also inherit Controller Interface , You can also use third-party interfaces , such as Struts2 Medium Action
RequestMappingHandlerMapping
Then take a look getHandler The implementation of the :

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

Back to handler yes HandlerExecutionChain, It contains the real handler And the blocker , It can be done before execution , After execution , Execution completes these three phases to process business logic .
RequestMappingHandlerMapping Of getHandler The call logic of is as follows :
 Call logic

Will traverse all Controller Of url Check whether there are qualified match(head,url,produce,consume,method To meet the requirements ), use antMatcher In the way of url matching , If it matches, it returns the corresponding handler, Otherwise return to null, If the mapping finds duplicate mappings (url The mapping is the same , The request method is the same , Parameters are the same , The request header is the same ,consume identical ,produce identical , The custom parameters are the same ), An exception will be thrown .

and SimpleUrlHandlerMapping The call logic of is as follows :
SimpleUrlHandlerMapping Call logic
It maintains url To handler Mapping , Through the first url To urlMap Find the corresponding handler, If not, try pattenMatch, If you succeed, you will return the corresponding handler, Return if not matched null.

We'll find out how to deal with it HandlerMapping Here we use the template method , Business logic is defined in the abstract class , The specific implementation only needs to realize its own business logic . At the same time, it also conforms to the principle of opening and closing , It's all interface oriented programming , I can't help but admire the logic involved here .

By the time we get here, we'll find out what we defined earlier Controller It's obviously in line with RequestMappingHandlerMapping Of strategy , So back HandlerExecutionChain It already contains the full path of the method to be accessed .

About HandlerAdapter

HandlerMapping Will pass HandlerExecutionChain Return to one Object Type of Handler object , be used for Web Request processing , there Handler There's no limit to what type , Generally speaking, any type of Handler Can be in
SpringMVC Use in , As long as it's used to process Web The processing object of the request is OK .

But for the DispatcherServlet There is a problem , It can't tell what kind of Handler, You can't tell if it's calling Handler Which method of processing the request , To call various types of Handler,
DispatcherServlet Will be different Handler The call responsibility for the HandlerAdapter Role .

Have a look first HandlerAdpter Definition of interface

public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}

Main concern supports and handle Method . Let's take a look at DispatcherServlet in handlerAdapters The initialization process , and handlerMappings The initialization process is similar to
 initialization HandlerAdapters
And then we have a look at HandlerAdapter Class relation of
HandlerAdapter Class diagram
alike , Still look for the right strategy for Adapter, Our main concern is RequestMappingHandlerAdapter( The others are rarely used ), So this is mainly about it . View it support Implementation code :
supports Method
Above about handler It's actually stated in the statement Object handler It's actually HandlerMethod, So here's the corresponding HandlerAdapter Namely RequestMappingHandlerAdapter.

After finding the corresponding adapter , Now you can call the real logic . Before that, users can do something with interceptors , Like logging , Print execution time, etc , So if you want to add a statement before the executed method , We just need to configure our own blocker .
 Execute interceptor method
Next we focus on handle Method , See what it's going to do ?, Have a look first handle Method execution flow , alike adapter The template method is also used , First define the process in the parent class , Subclasses only need to implement logic , So the first call here is AbstracthandlerMethodAdapter Of invokeHadlerMethod Method , Among them the HandlerMethod It was packaged .
invokeHandle
invokeAndHandle
We got to the first step , have a look invokeForRequest What is the main part of the method
invokeForRequest

Find that the calling logic of this method is actually very simple , It's parsing parameters , And then call the method . Let's take a look at how to parse the parameters ?
 Argument parsing
You can see almost all the logic in the core argumentResovlers In the middle , So supportive arguementResolver What are they? ? Where is it initialized ?

First of all, you need to locate where this attribute comes from ,RequestMappingHandlerAdapter Realized InitializingBean, It will be executed during initialization afterPropertiesSet Method , In the middle of it, to arguementResolvers as well as returnValueHandlers It's initialized .
Different resovler The supported parameter parsing is different , For example, there is support HttpServletRequest Injected , Have a support HttpServletREsponse There is also support body Body injection and so on .
arguementResovler initialization

returnValueHandlers initialization
After parameter analysis, we get the data needed by reflection ,class,method And parameters , Finally through java Reflection of api Call .
 Reflection calls real methods

thus ,springmvc The whole process of calling is basically clear .
But here the problem is not over , Because we don't know how to parse the parameters . such as get How to submit data ?post How to submit data ? How to convert to object ? This writing problem still exists , Let's continue to study .
Here I use postman Tools to initiate requests , First visit Get http://localhost:8080/user/condition?name=zhangsan&age=25, Locate the resolveArgument Method
 How to get specific arguementResolver

And then it was executed revolver.resolveArgument Method , The same template method is used here , In the abstract class AbstractNamedValueMethodArgumentResolver Define the process in , Each subclass only needs to implement its own logic .RequestParamMethodArgumentResolver The parameter of is through request.getParameter To get it . After getting the parameters, the reflection call is executed , At this time, we implemented what we wrote UserController The corresponding method of , Got it User object , The next step is to process the return value , adopt returnValueHandlers To deal with
 Process return value

handler The data will be processed according to the type returned , For example, it's through here response Output data to the requester , The output data is also through messageConverter To achieve
 Processing data output
Finally get ModalAndView object , But here because there is no modalAndView So back null. Last in DispatcherServlet Of processDispatchResult The call logic of the method is as follows
 The final treatment

How to resolve such requests ?

@PostMapping
public Integer saveUser(@RequestBody User user) {
Integer id = user.getName().hashCode() - user.getAge().hashCode();
return id > 0 ? id : -id;
}

Again, we focus on parsing parameters , In the last get In the example of the request, I said that I would visit AbstractNamedValueMethodArgumentResolver, But it's being dealt with @RequestBody It uses RequestResponseBodyMethodProcessor, It reproduces resolveArgument Method . So it doesn't execute the logic of the parent class .

 Argument parsing

Finally, it will be located here jakson Of objectMapper in , stay spring boot in , By default Jackson To achieve java Object to json Serialization and deserialization of formats . Of course, it can be configured messageConvert Of , Just implement Spring Of HttpMessageConverter that will do .

This is the end of source code analysis , Of course, there are still some things not mentioned , such as View The rendering of , The general view is diverse , also html,xml,jsp wait , therefore springmvc It also provides an interface for users to choose the template they need , Just implement ViewResolver Interface can . Also about Theme,MessageResource,Exception And so on , It's too long to talk about , I believe that it will be very simple to master the core process and see other processing , So there is no analysis of other details here .

A picture is worth a thousand words

SpringMVC flow chart

版权声明
本文为[think123]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云