zoukankan      html  css  js  c++  java
  • SpringMVC及其HandlerMapping、HandlerInterceptor、HandlerAdapter等组件的原理解析

    SpringMVC的原理

    加载的过程

     关于加载,在SpringIOC的加载过程中,并不会直接将HandlerMapping等组件给加载进去,而是在第一次请求的时候,进行初始化

    SpringMVC核心类,DispatcherServlet的类图

    image


    关于DispatcherServlet的初始化过程:由HttpServletBean调用init(),再由FrameworkServlet实现initServletBean(),最终由DispatcherServlet实现onRefresh(),并在其中加载初始化各个组件。

    	protected void initStrategies(ApplicationContext context) {
    		initMultipartResolver(context);//加载上传解析器
    		initLocaleResolver(context);
    		initThemeResolver(context);//
    		initHandlerMappings(context);//加载初始化HandlerMapping
    		initHandlerAdapters(context);//加载初始化HandlerAdapter
    		initHandlerExceptionResolvers(context);//加载初始化HandlerException
    		initRequestToViewNameTranslator(context);
    		initViewResolvers(context);
    		initFlashMapManager(context);
    	}
    

    HandlerMapping初始化过程

     通过SpringBean工厂获取已经加载了的HandlerMapping的实现类对象

    	private void initHandlerMappings(ApplicationContext context) {
    		this.handlerMappings = null;
    
    		if (this.detectAllHandlerMappings) {
    			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
    			//通过SpringIOC获取已经加载了的HandlerMapping的实现类对象
    			Map<String, HandlerMapping> matchingBeans =
    					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
    		//省略。。。
    	}
    

     在初始化的过程中,通过类型type从SpringIOC容器中去获取HandlerMapping的实现类
    image
     SpringBoot默认加载了5个HandlerMapping的实现类:
    RequestMappingHandlerMapping:通过@RequestMapping映射的路径寻找到对应方法的类
    WelcomePageHandlerMapping:配置默认欢迎页时需要配置的类
    BeanNameUrlHandlerMapping:和下面的SimplerUrlHandlerMapping类似,都是通过xml文件的方式配置url,将url与对应的Controller的方法进行绑定
    SimpleUrlHandlerMapping:如上所说


     在SpringMVC初始化之前,SpringIOC加载RequestMappingHandlerMapping的时候,在AbstractAutowireCapableBeanFactory构建好对象,并进行初始化(initializeBean())的时候,将各个@RequestMapping对应的Controller方法映射的String地址给注册到RequestMappingHandlerMapping

    	protected void detectHandlerMethods(Object handler) {
    		//...
    			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
    					(MethodIntrospector.MetadataLookup<T>) method -> {
    						try {
    							return getMappingForMethod(method, userType);
    						}
    						catch (Throwable ex) {
    							throw new IllegalStateException("Invalid mapping on handler class [" +
    									userType.getName() + "]: " + method, ex);
    						}
    					});
    			//...
    			//
    			methods.forEach((method, mapping) -> {
    				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
    				registerHandlerMethod(handler, invocableMethod, mapping);
    			});
    		}
    	}
    
    	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    		//将对应的url映射到对应的Controller方法上
    		this.mappingRegistry.register(mapping, handler, method);
    	}
    

    HandlerInterceptor在加载的时候

    HandlerInterceptor随着RequestMappingHandlerMapping在SpringIOC中创建对象的同时,进行加载与初始化

    	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    			throws BeanCreationException {
    
    		// 实例化对象
    		BeanWrapper instanceWrapper = null;
    
    		if (instanceWrapper == null) {
    			//在实例化对象的包装器的时候,将HandlerInterceptor进行了加载与初始化
    			instanceWrapper = createBeanInstance(beanName, mbd, args);
    		}
    		Object bean = instanceWrapper.getWrappedInstance();
    
    		// 对实例对象初始化
    		Object exposedObject = bean;
    		try {
    			populateBean(beanName, mbd, instanceWrapper);
    			exposedObject = initializeBean(beanName, exposedObject, mbd);
    		}
    
    
    		//
    
    		return exposedObject;
    	}
    

    HandlerAdapter在加载的时候

     和HandlerMapping的流程类似,都是现在SpringIOC中加载、初始化对象,然后在SpringMVC第一次请求来的时候,将HandlerAdapter的实现对象初始化到SpringMVC的上下文中。
    HandlerAdapter的常见实现类:
    image
    RequestMappingHandlerAdapter:通过RequestMapping请求的处理器
    HttpRequesthandlerAdapter:静态资源处理器
    SimpleControllerHandlerAdapterController接口实现类的处理器,处理Controller实现类的请求
     ``

    SpringMVC的工作流程

    	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);
    
    				//1.通过请求的url找到应该使用哪个HandlerMapping,并获取到HandlerMapping的HandlerExecutionChain
    				//HandlerInterceptor就是在这里进行初始化的,每次请求来时,都会在HandlerExecutionChain对象中赋值
    				mappedHandler = getHandler(processedRequest);
    
    				//2.通过HandlerMethod找到其应该用哪个HandlerAdapter
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    				//3.执行HandlerInterptor中的preHandler
    				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    					return;
    				}
    
    				//4.通过HandlerAdapter调用Contoller中的具体方法,并返回ModelAndView
    				//如果不返回ModelAndView,则直接响应用户了
    				//在这里面,有HandlerAdapter决定了如果处理请求体、响应体
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    				if (asyncManager.isConcurrentHandlingStarted()) {
    					return;
    				}
    
    				applyDefaultViewName(processedRequest, mv);
    				//5.执行HandlerInterptor中的postHandler
    				mappedHandler.applyPostHandle(processedRequest, response, mv);
    			}
    			catch (Exception ex) {
    				dispatchException = ex;
    			}
    
    			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    		}
    	}
    
    

    HandlerMapping在工作的时候

    HandlerInterceptor在工作的时候

    HandlerAdapter在工作的时候

  • 相关阅读:
    git 项目代码打包
    jira查看字段
    jmeter压力测试报错:java.net.BindException: Address already in use: connect解决办法
    python 破解验证码
    mysql授权远程登录
    豆瓣api
    利用python开发财务工具
    钉钉发送消息通知
    git使用命令行自动登录
    后宫
  • 原文地址:https://www.cnblogs.com/lcmlyj/p/14674578.html
Copyright © 2011-2022 走看看