IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> HandlerAdapter -> 正文阅读

[Java知识库]HandlerAdapter

一、概述

1.问题

之前我们讲到了通过请求找到了对应的映射器,也就是找到了对应的方法,那么如何调用这个方法呢,因为不同的接收请求方法有不同的方式,最简单的就是方法名就不一样,那么如何调用,这里采用了设计模式中的适配器模式,通过适配,进行判断,然后通过不同的方式调用方法,这就是HandlerAdpater 的主要功能

2.请求流程

之前介绍了HandlerMapping,我们通过请求找到了对应的handler,那么我们继续来看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);

				// Determine handler for the current request.
                 //将我们的请求的地址和方法进行映射起来,也是我们这篇博客的重点
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
                      // 如果没有找到 会调用这个方法
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
                 // 通过handler找到对应的适配器 
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                
                 // Process last-modified header, if supported by the handler.
				String method = request.getMethod();
                 // 判断这个方法是get还是其它的方法
				boolean isGet = HttpMethod.GET.matches(method);
                 // 若是get或者head执行下面的操作
				if (isGet || HttpMethod.HEAD.matches(method)) {
                      // 默认的情况下都是返回 -1 
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                      // 一般情况不会进入
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
                 // 处理拦截器的方法,调用拦截器的preHandle方法, 如果被拦截就直接返回
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                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);
                 

				//省略一些代码
	}

我们先看没有找到对应的Handler的对象,如果没有找到Handler对象的话,就执行noHandlerFound(processedRequest, response);的方法,具体的代码如下:

	protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
         // 日志打印
		if (pageNotFoundLogger.isWarnEnabled()) {
			pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
		}
         // 是否出现了异常
		if (this.throwExceptionIfNoHandlerFound) {
			throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
					new ServletServerHttpRequest(request).getHeaders());
		}
		else {
             // 没有出现异常 直接返回404
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
		}
	}

上面的代码其实很简单,判断是否需要打印日志,如果需要,就直接打印日志,如果在查找Handler的时候出错了,就直接抛出异常,如果没有出现异常,就直接将状态码设置成404,然后返回。

然后就是根据对应的Handler获取对应的HandlerAdapter。这个时候会调用getHandlerAdapter(mappedHandler.getHandler());方法,具体的代码如下:

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

上面运行到了 HandlerAdapter,下面会先介绍一下HandlerAdapter 这个接口和其常用子类

二、HandlerAdapter

1.HandlerAdapter接口

该接口中有3个方法

public interface HandlerAdapter {
	// 判断该适配器是否支持传入的handler
	boolean supports(Object handler);
	// 使用给定的handler来处理当前的request请求,调用对应的逻辑方法
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	// 返回handler最后修改的时间 该方法已经标记为废弃
	@Deprecated
	long getLastModified(HttpServletRequest request, Object handler);
}

2.子类

HttpRequestHandlerAdapter

SimpleControllerHandlerAdapter

RequestMappingHandlerAdapter

RequestMappingHandlerAdapter

3.初始化

HandlerAdapter 和 之前介绍的 HandlerMapping 初始化思路是一样,调用链:

org.springframework.web.servlet.HttpServletBean#init()
--> org.springframework.web.servlet.FrameworkServlet#initServletBean()
--> org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext()
--> org.springframework.web.servlet.DispatcherServlet#onRefresh()
--> org.springframework.web.servlet.DispatcherServlet#initStrategies()
--> org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters()

通过上面的调用链,我们会调用初始化处理适配器方法 initHandlerAdapters

	private void initHandlerAdapters(ApplicationContext context) {
		this.handlerAdapters = null;

		if (this.detectAllHandlerAdapters) {
			// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
             // 从spring的容器中取,如果这儿加了@EnableWebMvc注解,这儿取出来的就是三个
			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);
			}
		}
		else {
			try {
				HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
				this.handlerAdapters = Collections.singletonList(ha);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerAdapter later.
			}
		}

		// Ensure we have at least some HandlerAdapters, by registering
		// default HandlerAdapters if no other adapters are found.
         // 如果上面没有从Spring中获取不到适配器,那么就从配置文件中去默认的
		if (this.handlerAdapters == null) {
			this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

上面的代码就是如果加了@EnableWebMvc注解的话,就直接能从spring容器中取出对应的三个HandlerMapping,如果没有加,则调用 getDefaultStrategies() 方法,

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		if (defaultStrategies == null) {
			try {
				// Load default strategy implementations from properties file.
				// This is currently strictly internal and not meant to be customized
				// by application developers.
                  // 从默认的配置中 ‘DispatcherServlet.properties’ 取值 
				ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
                  // 加载属性
				defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
			}
			catch (IOException ex) {
				throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
			}
		}
         // 遍历默认的映射器注入
		String key = strategyInterface.getName();
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<>(classNames.length);
			for (String className : classNames) {
				try {
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					Object strategy = createDefaultStrategy(context, clazz);
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (LinkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return Collections.emptyList();
		}
	}

上面的代码说如果spring容器中取不到,那么会从默认的配置文件中取,这个配置在通过IDEA中查看jar包 spring-webmvc-5.3.9.jar,打开可以看到其中的一段配置

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter

三、适配调用

上面我们通过doDispatch()方法调用到了 HandlerAdapter,上面我们介绍了这个接口及其子类,那么接下来我们按照类图来介绍一下接口

1.HttpRequestHandlerAdapter

我们先看一下这个类的结构,看看有没有利用spring的一些扩展点,来完成一些数据的初始化的工作,具体如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOTL2A80-1634225752237)(HandlerAdapter.assets/image-20211013205417528.png)]

可以发现这个类没有实现spring的扩展点,就是实现了HandlerAdapter这个接口

public class HttpRequestHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
         // 判断这个handler是否实现了HttpRequestHandler接口
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 这里只是把handler强转一下,然后调用对应的handleRequest()方法
		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}
	// getLastModified() 方法已经标记废弃,这里省略
}

我们可以从上面的内容中看到,这个类很简单,调用supports方法来进行适配,这个方法也只是进行了强转判断,并且在适配调用逻辑方法的适合,也很好理解,

测试代码

@Component("/name")
public class BeanNameController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("实现Controller接口方式");
        return null;
    }
}

getHandlerAdapter()这个方法里面,会遍历 handlerAdapter集合,来查找这个handlerAdapter是否匹配这个Handler,找到1个就直接返回,所以这个匹配是存在顺序的,第一步就是BeanNameUrlHandlerMapping,也就是Bean的name是以“/”开头的都是会匹配到,并且这个类是实现了HttpRequestHandler接口的类,才会返回这种适配器

2.SimpleControllerHandlerAdapter

有了上面的介绍,我们看这个就很好理解了,和上面的类似

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8JJCjrB-1634225752238)(HandlerAdapter.assets/image-20211013210811874.png)]

对应的类内容:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
         // 强转判断
		return (handler instanceof Controller);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 强转调用方法
		return ((Controller) handler).handleRequest(request, response);
	}

	// getLastModified() 方法已经标记废弃,这里省略
}

测试代码

@Component("/name")
public class BeanNameController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("实现Controller接口方式");
        return null;
    }
}

3.RequestMappingHandlerAdapter

首先看一下这个类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzQtuNzz-1634225752240)(HandlerAdapter.assets/image-20211013211502256.png)]

这儿实现了ApplicationContextAwareBeanFactoryAwareInitializingBean接口,这3个接口实现的方法都是在容器启动时运行的,其中ApplicationContextAware中的接口的方法不用看的,因为是父类实现了而且也没有调用这个类中的方法,接下来看下BeanFactoryAware接口中的setBeanFactory的方法,具体的代码如下:

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (beanFactory instanceof ConfigurableBeanFactory) {
			this.beanFactory = (ConfigurableBeanFactory) beanFactory;
		}
	}

可以发现这个类就是Spring的bean的工厂设置给属性beanFactory,接下来我们继续看实现InitializingBean中的afterPropertiesSet方法,具体的代码如下:

public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
  		//初始化@ControllerAdvice注解需要的东西,后面写篇博客介绍下这个注解
		initControllerAdviceCache();

  		//获取调用的方法的参数
		if (this.argumentResolvers == null) {
        	//Spring提供的一系列的HandlerMethodArgumentResolver,同时这儿也会调用用户自己添加的HandlerMethodArgumentResolver
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
  		//获取桥接方法的参数
		if (this.initBinderArgumentResolvers == null) {
      		//Spring提供的一系列的HandlerMethodArgumentResolver,同时这儿也会调用用户自己添加的HandlerMethodArgumentResolver
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
  		//获取方法的返回的参数
		if (this.returnValueHandlers == null) {
      		//Spring提供的一系列的HandlerMethodReturnValueHandler,同时这儿也会调用用户自己添加的HandlerMethodReturnValueHandler
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

上面就是完成了一系列的初始化的工作,同时添加了处理方法的参数的处理器,以及一些处理方法返回值的处理器。这些在调用调用指定的方法和处理返回值的时候有用,后面笔者会讲到。看完了这些扩展点,我们需要看下这个类中的supports的方法,具体的代码如下

public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

判断类型是否是HandlerMethod,而第二个方法默认是返回true,所以这种的匹配的规则就是,加了@RequestMapping或者@Controller的类,同时这个类中的所有加@RequestMapping的方法都是会被解析成HandlerMethod类型的handler我们再来看最后一个HandlerAdapter类

4.HandlerFunctionAdapter

类图

可以发现这个类实现Ordered接口,没有实现其他的特殊的接口,可以得出没有利用Spring的扩展点,接下来我们就看这个类的supports的方法,具体的代码如下:

@Override
public boolean supports(Object handler) {
	return handler instanceof HandlerFunction;
}

只要handler的类型是HandlerFunction就返回true,所以在什么时候会返回true呢?由上一篇博客,我们可以得出,如果查找出来的Handler是RouterFunctionMapping,这个方法就会返回true。至此SpringMVC的三大HandlerMapping,四大HandlerAdapter,笔者到此就讲完了。

er类

4.HandlerFunctionAdapter

类图

可以发现这个类实现Ordered接口,没有实现其他的特殊的接口,可以得出没有利用Spring的扩展点,接下来我们就看这个类的supports的方法,具体的代码如下:

@Override
public boolean supports(Object handler) {
	return handler instanceof HandlerFunction;
}

只要handler的类型是HandlerFunction就返回true,所以在什么时候会返回true呢?由上一篇博客,我们可以得出,如果查找出来的Handler是RouterFunctionMapping,这个方法就会返回true。至此SpringMVC的三大HandlerMapping,四大HandlerAdapter,笔者到此就讲完了。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-15 11:39:07  更:2021-10-15 11:40:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:40:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码