SpringMVC核心组件HandlerMapping你清楚了吗?
环境:Springboot2.4.11概述
当一个请求过来后Spring是如何进行处理的?下面简单的罗列下请个的过程中核心组件
SpringMVC处理的流程:DispatcherServlet 所有请求的入口HandlerMapping 将请求地址与处理程序关联HandlerAdapter 真正的处理程序,如执行上一步中对应的处理程序HandlerMethodArgumentResolver 对参数进行解析,这里面还涉及到很多其它东西HanlderMethodReturnValueHandler 对返回值进行输出处理ViewResolver 当上一步返回结果为ModelAndView时会应用视图解析器
一个请求的处理过程获取HandlerMapping
该步从容器中获取所有的HandlerMapping对象。public class DispatcherServlet extends FrameworkServlet { private List handlerMappings; private void initHandlerMappings(ApplicationContext context) { // 在ApplicationContext中查找所有HandlerMappings,包括祖先上下文。 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerMappings); } } }查找HandlerMapping
该步从获取的HandlerMapping中查找适合当前请求的HandlerMapping。public class DispatcherServlet extends FrameworkServlet { private List handlerMappings; protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerExecutionChain mappedHandler = null; // 查找能够处理当前请求HandlerMapping对象,主要就是根据请求的URI mappedHandler = getHandler(processedRequest); } protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // HandlerMapping 实现了Ordered接口,是由顺序的,那在这里,谁先匹配谁就处理 for (HandlerMapping mapping : this.handlerMappings) { // 在这个过程中会通过查找到的HandlerMapping对象,然后获取合适的处理程序(可能是个Bean对象或是HandlerMethod对象等) HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; } }
系统默认有如下5个HandlerMappingRequestMappingHandlerMappingBeanNameUrlHandlerMappingRouterFunctionMappingSimpleUrlHandlerMappingWelcomePageHandlerMapping
一般默认都是RequestMappingHandlerMapping匹配
接下来看看是如何进行匹配的
调用父类AbstractHandlerMapping#getHandler方法,父类中的这个方法中定义了特定的逻辑,而针对每种不同的HandlerMapping实现是需要具体的子类来实现AbstractHandlerMapping#getHandlerInternal方法public abstract class AbstractHandlerMapping { public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); // ... HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); // ... return executionChain; } } public abstract class AbstractHandlerMethodMapping { protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 获取请求地址 String lookupPath = initLookupPath(request); try { // 根据请求地址查询对应的HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } // ... } protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List matches = new ArrayList<>(); // 在已注册的Mapping中根据请求的url进行查找 // 这样查找的this.pathLookup.get(urlPath); List directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath); if (!matches.isEmpty()) { Match bestMatch = matches.get(0); // ... handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.getHandlerMethod(); } // ... } }
到这里就是查找处理请求的HandlerMethod对象。接下来看看系统是如何进行初始化所有的HandlerMethod初始化HandlerMethodpublic class RequestMappingHandlerMapping { public void afterPropertiesSet() { // ... super.afterPropertiesSet(); } } public abstract class AbstractHandlerMethodMapping { public void afterPropertiesSet() { initHandlerMethods(); } protected void initHandlerMethods() { // getCandidateBeanNames获取容器中的所有Bean for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); } protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { // 根据BeanName获取对应的Class beanType = obtainApplicationContext().getType(beanName); } // ... // isHandler方法判断当前的类是否符合条件,该方法在RequestMappingHandlerMapping中实现 // isHandler方法用处就是判断当前的Class@Controller或者@RequestMapping注解 // 这样就将所有的@Controller与RequestMappingHandlerMapping关联一起了。 if (beanType != null && isHandler(beanType)) { // 查找所有的HandlerMethod detectHandlerMethods(beanName); } } protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); // 查找Class中的所有方法 Map methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup) method -> { try { // 将每一个符合条件的方法(方法上有@RequestMapping注解的) // 封装到RequestMappingInfo对象中 return getMappingForMethod(method, userType); } // ... }); methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); // 将找到的所有Method进行注册添加到Map中 registerHandlerMethod(handler, invocableMethod, mapping); }); } } protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); } class MappingRegistry { // T : RequestMappingInfo, handler: 字符串(usersController)Bean名称,method:请求方法对象 public void register(T mapping, Object handler, Method method) { // 创建HandlerMethod对象 HandlerMethod handlerMethod = createHandlerMethod(handler, method); // ... for (String path : directPaths) { // 缓存上,在请求到来的时候 会从这个pathLookup集合中查找 this.pathLookup.add(path, mapping); } } } }
完毕!!!
求个关注+转发
公众:Springboot实战案例锦集
spring data jpa 高级应用
Spring MVC 异常处理方式
Spring事务实现原理源码分析
Spring 自定义Advisor以编程的方式实现AOP
SpringBoot项目中应用Spring Batch批处理框架,处理大数据新方案
Spring IOC容器对Bean实例化的过程详解源码分析
Spring Boot Security防重登录及在线总数
Spring Security记住我功能实现及源码分析
Spring Security 自定义登录成功后的逻辑