Springmvc源码分析、底层原理

1.Springmvc是如何找到Controller的?

首先在请求过来时,会先进入DispatcherServlet进行请求分发,执行DispatcherServlet类中的doDispatch()方法。
-------------- spring MVC找Controller流程 ----------------:
1.扫描整个项目(spring已经做了)定义一个map集合
2.拿到所有加了@Controller注解的类
3.遍历类中所有的方法对象(把方法作为对象)
4.判断方法是否加了@RequestMapping注解
5.把@RequestMapping注解的value(/getEmp/byId),作为map的key给put进去,把方法对象作为value放进map的value中
6.根据用户发送的请求,拿到uri
url:http://localhost:8081/index/do
uri:/index/do
7.使用请求的uri作为map的key,去map里边,查看是否由对应的返回值

2.---------------源码分析---------------

2.1请求过来时,会先到达DispatcherServlet类中,进行请求分发,看一下DispatcherServlet源码
主要功能:检查请求是否含有文件,调用getHandler()方法,使用handlerMappings映射器来处理业务,并没有直接去找controller,扩展性很强

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.
				//*******调用gethandler方法,框架就是框架,getHandler并没由直接去找controller,
				//*******而使用handlerMappings映射器来处理业务,可扩展性很强!
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				........


}

2.2 进入getHandler()源码

	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//******springmvc框架中新建了一个类 HandlerMapping ,以后去找controller的任务就交给他,
		//******有什么需要修改的地方只需要动HandlerMapping 就可以了,代码解耦
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				//******请求mapping.getHandler方法,在方法里拿到uri
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

在这里插入图片描述
如上图:spring mvc 提供了两个HandlerMapping 映射器去找Controller,为什么定义两个映射器呢?

首先要知道Controller的定义方式 有 两种类型和三种实现
两种类型: BeanName类型 和 @Controller类型
三种实现: 实现HttpRequestHandler、实现Controller、加@Controller注解

实例:
1.@Controller类型

@Controller
public class MenuController {}

2.BeanName类型

实现controller接口:

//------实现controller接口,并加@Component注解
@Component(/getEmp/byId) //括号内为拦截路径
public class MenuController  implements Controller{}

实现HttpRequestHandler接口:

//------实现HttpRequestHandler接口,并加@Component注解
@Component(/getEmp/byId) //括号内为拦截路径
public class MenuController  implements HttpRequestHandler{}

以上两种方式都可以定义controller,HandlerMapping 映射器去找controller时也要根据不同情况去找!
1.RequestMappingHandlerMapping:找所有以@Controller注解标注的对象
2.BeanNameUrlHandlerMapping:找所有实现Controller、HttpRequestHandler接口的对象,称为BeanName类型
如果项目中控制层都是用@Controller标注的,则BeanNameUrlHandlerMapping映射器为空!!

进入mapping.getHandler(request)源码

在这里插入图片描述
2.3 进入getHandlerInternal源码
根据用户发送的请求,拿到uri

	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	
		//*********getUrlPathHelper()获取uri路径	uri为/login
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		request.setAttribute(LOOKUP_PATH, lookupPath);
		this.mappingRegistry.acquireReadLock();
		try {
			//******接下来执行lookupHandlerMethod()方法
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

根据用户发送的请求,拿到uri ,uri为/login,接下来执行lookupHandlerMethod()方法
在这里插入图片描述
2.4 进入lookupHandlerMethod()方法源码,
getMappingsByUrl()方法,传入了uri:/login
在这里插入图片描述
2.5 进入getMappingsByUrl()方法源码,
urlLookup:为最上边提到的spring扫描的map对象
urlPath:请求的uri
意义:根据uri去找对应的方法

	@Nullable
		public List<T> getMappingsByUrl(String urlPath) {
			//
			return this.urlLookup.get(urlPath);
		}

在这里插入图片描述
这样就找到了Controller

3.HandlerAdapter

上边通过DispatchServlet中的doDispatch()方法中的getHandler()方法找到了Controller,接下来要调用getHandlerAdapter方法获取对应的HandlerAdapter
在这里插入图片描述
进入getHandlerAdapter()源码
在这里插入图片描述
适配器找到之后,使用适配器调用对应的方法
在这里插入图片描述
通过debug,可知进入的第一个handle方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
再进入invokeHandlerMethod中:
在这里插入图片描述
在这里插入图片描述
进入invokeForRequest中
在这里插入图片描述
调用完成结束!
注意:
在获取具体参数中的getMethodArgumentValues方法中,需要判断参数类型,(比如是不是string、integer之类的),spring mvc没有用普通的if判断,而是把封装了许多类,每一个类对应一个参数类型,针对不同的参数类型去不同的类中去处理,与业务代码解耦

3.参数处理器引发的思考

在上边获取具体参数时,其实经过了spring mvc的参数处理器处理后才返回的
我们在实际项目可以使用参数处理器吗?当然可以

自定义参数处理器使用场景
1.原始版:当前端传来一个参数username,在Controller层需要获取这个username的详细信息,需要从redis或者mysql中根据username查询到这个人的详情。如果很多Controller层都需要获取详细信息的话,每一个Controller中都需要写一段查询代码。
2.使用mvc参数处理器
2.1:自定义一个注解@UserParam,加在需要获取详细信息的Controller的参数之前。代表这个参数需要使用参数处理器处理
在这里插入图片描述

2.2:自定义类ArgumentResovel实现HandlerMethodArgumentResolver参数处理器类
2.3:判断你的参数是否需要当前参数处理器来处理(依据:是否含有之定义注解@UserParam)

在这里插入图片描述
2.3:如果supportsParameter返回true, resolveArgument执行处理逻辑
在这里插入图片描述
2.4:把自定义参数处理器配置进去
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值