当前位置: 首页 > news >正文

网站域名有什么用/网站快速排名推荐

网站域名有什么用,网站快速排名推荐,自己动手做导航网站,做一个网站的策划方案文章目录1. 注解RequestBody 的作用2. Http 请求的处理流程2.1 HandlerAdapter 的继承结构2.2 RequestMappingHandlerAdapter 的处理过程3. SpringMVC 自定义协议的解析3.1 重写转换器方式3.1.1 实现方式3.1.2 使用与测试3.1 重写解析器方式1. 注解RequestBody 的作用 Request…

文章目录

  • 1. 注解@RequestBody 的作用
  • 2. Http 请求的处理流程
      • 2.1 HandlerAdapter 的继承结构
      • 2.2 RequestMappingHandlerAdapter 的处理过程
  • 3. SpringMVC 自定义协议的解析
      • 3.1 重写转换器方式
        • 3.1.1 实现方式
        • 3.1.2 使用与测试
      • 3.1 重写解析器方式

1. 注解@RequestBody 的作用

@RequestBody用在方法参数上面,通常在 Controller 中使用,用于将请求携带的参数绑定到 request body中,然后借助HttpMessageConverter转化为目标 Java Bean。要想了解该注解生效的原理,就需要理清框架对请求的具体处理逻辑。

2. Http 请求的处理流程

在这里插入图片描述

2.1 HandlerAdapter 的继承结构

SpringMVC 中请求处理的主要流程可参考SpringMVC 源码分析(1)-请求处理主流程,本文主要分析请求到来时 SpringMVC 对请求的处理细节,入口在DispatcherServlet#doDispatch()方法中,该方法会先找到合适的 HandlerAdapter 对象来处理当前请求:

// 判断使用哪个adapter,逻辑位于 HandlerAdapter子类的 supports()方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

HandlerAdapter是一个接口,有 4 个直接子类,但是对于@RequestMapping注解的处理方法的调用会在子类 AbstractHandlerMethodAdapter#handle()中调用到间接子类RequestMappingHandlerAdapter#handleInternal()实现
在这里插入图片描述

2.2 RequestMappingHandlerAdapter 的处理过程

  1. 从上文可知代码mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 实际会调用到 RequestMappingHandlerAdapter#handleInternal()。从代码流程很容易看出来,这个方法其实就是完成请求处理并返回 ModeAndView对象,其核心逻辑为 invokeHandlerMethod 方法

    @Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {······// Execute invokeHandlerMethod in synchronized block if required.if (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {//真正调用到 Controller 中的请求处理方法mav = invokeHandlerMethod(request, response, handlerMethod);}······return mav;}
    
  2. RequestMappingHandlerAdapter#invokeHandlerMethod() 中需注意在该方法里会设置请求参数的解析器以及返回参数的解析器

    @Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 设置请求参数解析器,argumentResolvers为参数解析器列表,参考本类中getDefaultArgumentResolvers()方法可知其包含了哪些解析器if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// 设置返回参数解析器if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}······//调用具体请求处理方法invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}
    
  3. ServletInvocableHandlerMethod#invokeAndHandle() 中调用 invokeForRequest() 方法完成请求处理并得到返回值,之后调用上一个步骤中设置的返回参数解析器处理返回值

     public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 处理请求Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);······try {// 返回参数解析器处理返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}}
    
  4. InvocableHandlerMethod#invokeForRequest()方法首先去处理请求中携带的参数,之后将其填入方法执行

     @Nullablepublic Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 获取处理后的请求参数值Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}return doInvoke(args);}
    
  5. InvocableHandlerMethod#getMethodArgumentValues()处理参数,其实质为根据请求的相关条件选择步骤2中设置的解析器解析参数

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {if (ObjectUtils.isEmpty(getMethodParameters())) {return EMPTY_ARGS;}MethodParameter[] parameters = getMethodParameters();Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}// 遍历参数解析器列表,判断是否有满足此次请求要求的解析器if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {// 有则调用其 resolveArgument 方法处理参数args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {// Leave stack trace for later, exception may actually be resolved and handled..if (logger.isDebugEnabled()) {String error = ex.getMessage();if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, error));}}throw ex;}}return args;}
    
  6. 解析器匹配的关键是以上 this.resolvers.supportsParameter(parameter)代码,这段代码内部具体调用不作具体分析,其功能可归纳为根据参数的相关条件匹配到对应的解析器。对于 @RequestBody 修饰的参数,可知其对应的解析器为 RequestResponseBodyMethodProcessor,在该类的 supportsParameter()方法中明确其支持解析 @RequestBody 注解修饰的参数

    @Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(RequestBody.class);}
    
  7. 参数解析器选择完毕,接下来就是解析参数。该过程最终会调用到RequestResponseBodyMethodProcessor#resolveArgument()方法

    @Overridepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();// 转化参数关键方法,使用到转换器Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());String name = Conventions.getVariableNameForParameter(parameter);if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);if (arg != null) {validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());}}if (mavContainer != null) {mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());}}return adaptArgumentIfNecessary(arg, parameter);}
    
  8. 进入readWithMessageConverters()方法,最终来到 AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters()方法,可以看到其使用 HttpMessageConverter解析参数并返回。而此处的HttpMessageConverter 是在RequestMappingHandlerAdapter 中设置解析器的时候添加到每个解析器中的。

    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {......Class<?> contextClass = parameter.getContainingClass();Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);if (targetClass == null) {ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);targetClass = (Class<T>) resolvableType.resolve();}HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);Object body = NO_VALUE;EmptyBodyCheckingHttpInputMessage message;try {message = new EmptyBodyCheckingHttpInputMessage(inputMessage);// 遍历解析器中的 converter,如果  converter.canRead() 为真,表明该converter支持当前请求的数据转化,则使用 converter.read()转化数据for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();GenericHttpMessageConverter<?> genericConverter =(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :(targetClass != null && converter.canRead(targetClass, contentType))) {if (message.hasBody()) {HttpInputMessage msgToUse =getAdvice().beforeBodyRead(message, parameter, targetType, converterType);body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);}else {body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}}catch (IOException ex) {throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);}......return body;}
    

3. SpringMVC 自定义协议的解析

通过以上分析我们知道,SpringMVC 中 @RequestBody 生效的原理依赖两个因素:

  1. RequestResponseBodyMethodProcessor 解析器
  2. 解析器中装载的转换器转换参数

这样我们就有两种方式来完成自定义协议的解析,Github 项目传送门

3.1 重写转换器方式

3.1.1 实现方式

使用Spring 已有的注解,重新写一个HttpMessageConverter 后加入到 Spring 配置中。这种方式生效的原理是 WebMvcConfigurationSupport#requestMappingHandlerAdapter()初始化时会调用其函数设置转换器
adapter.setMessageConverters(getMessageConverters())。代码中的 getMessageConverters 会调用 configureMessageConverters,从而触发自定义配置类的 configureMessageConverters方法,将重写的转换器加载到转换器列表中。这样在初始化解析器的时候,就将转换器添加进去给解析器使用了

  1. ExMessageConverter 转换器

    public class ExMessageConverter extends AbstractGenericHttpMessageConverter<Object> {private static final MediaType EX_STRING = MediaType.valueOf("application/ex-serialize");@Override
    public List<MediaType> getSupportedMediaTypes() {MediaType[] typeArr = {EX_STRING};// 支持的 MediaTypereturn Arrays.asList(typeArr);
    }@Override
    protected void writeInternal(Object o, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {// 该方法在处理返回值时调用outputMessage.getHeaders().setContentType(EX_STRING);System.out.println("third");String json = "hello:" + o.toString();outputMessage.getBody().write(json.getBytes());
    }@Override
    protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {// 该方法在读取请求的参数时调用byte[] bytes = StreamUtils.copyToByteArray(inputMessage.getBody());String json = new String(bytes);return json + "-hello";
    }@Override
    public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {return readInternal(contextClass, inputMessage);}
    }
    
  2. WebConfig 配置

    @Configuration
    public class WebConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {// 设置优先级为 0,优先使用converters.set(0, new ExMessageConverter());}
    }
    

3.1.2 使用与测试

  1. 测试的 Controller 方法:
    使用POST方式请求,ContentType 必须设置为转换器自定义的支持类型,此处配置为 produces = {“application/ex-serialize”}

    @RequestMapping(value = "/get/happy", produces = {"application/ex-serialize"})@ResponseBodypublic UserInfo getConverter (@RequestBody String acct) {System.out.println(acct);UserInfo user = new UserInfo();user.setName("HooKong");user.setAge("90");return user;}
    
  2. 测试结果
    在这里插入图片描述

3.1 重写解析器方式

这种方式可使用自定义的新注解,新注解的使用方式与 @RequestBody完全相同。实现思路为继承 AbstractMessageConverterMethodProcessor,仿照 RequestResponseBodyMethodProcessor 重写其抽象方法,之后将自定义的消息转换器设置到解析器中,通过自定义的配置类将解析器添加到Spring配置中

  1. @ExRequestBody 注解

    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExRequestBody {}
    

    @ExResponseBody 注解

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExResponseBody {
    }
    
  2. ExMethodProcessor 解析器

    public class ExMethodProcessor extends AbstractMessageConverterMethodProcessor {public ExMethodProcessor(List<HttpMessageConverter<?>> converters) {super(converters);}// 关键方法,该解析器触发使用的条件@Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(ExRequestBody.class);}// 关键方法,定义该解析器触发使用的条件@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return returnType.getMethodAnnotation(ExResponseBody.class) != null;}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());String name = Conventions.getVariableNameForParameter(parameter);if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);if (arg != null) {validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());}}if (mavContainer != null) {mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());}}return arg;}@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,NativeWebRequest webRequest) throws Exception {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// Try even with null return value. ResponseBodyAdvice could get involved.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}}
    
  3. WebConfig 配置

    @Configuration
    public class WebConfig implements WebMvcConfigurer {@Autowiredprivate ExMethodProcessor exMethodProcessor;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(exMethodProcessor);}@Overridepublic void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {handlers.add(exMethodProcessor);}// 设置该解析器的转换器@Beanpublic ExMethodProcessor newExResolver() {return new ExMethodProcessor(Arrays.asList(new ExMessageConverter()));}
    }
    
http://www.lbrq.cn/news/1121131.html

相关文章:

  • 苏州网络网站建设/百度企业官网
  • 南宁响应式网站制作/百度网络小说排行榜
  • flash素材网站有哪些/seo问答
  • 专门做水果的网站/班级优化大师是干什么用的
  • 使用wget对网站做镜像/seo培训网
  • 阳信做网站/陕西新站seo
  • 大数据营销笔记本/杭州seo网络推广
  • 做301网站打不开/如何进行网站的推广
  • 网站开发从入门到精通/seo工具软件
  • 阜南做网站/2024年疫情还会封控吗
  • 做自己的网站流量怎么/网站建设流程图
  • 深圳创业补贴2024/福建seo顾问
  • 怎么查询网站是哪家公司做的/网站建设步骤
  • 免费游戏直接能玩/360搜索优化
  • 市县政府网站建设管理工作总结/免费的模板网站
  • wordpress页面浏览量/seo流量
  • 嘉兴市建设教育网站/个人建网站需要多少钱
  • 网站设计在营销中的作用/兰州网络推广技术
  • 中国建设教育协会的网站/网络市场营销
  • soho 网站建设/近期热点新闻事件50个
  • 医程通 网站做的太/手游推广平台哪个好
  • 无锡专业做网站的公司/深圳网
  • 做网站的分辨率/如何进行网站性能优化
  • 玉溪做网站/网络科技公司骗了我36800
  • 惠州企业建站程序/seo课程培训班
  • wordpress编辑插件/seo门户网站
  • 一键优化为什么不能100/流程优化四个方法
  • 什么网站可以做公共基础知识/新闻20字摘抄大全
  • 谷歌seo网站建设/谷歌google搜索引擎入口
  • 郑州做网站公司排/全国疫情最新情况最新消息今天
  • Unity音游开发全指南:模板与免费资源高效构建节奏游戏
  • 厂区车辆导航系统:基于 GPS+AI 动态路径规划的技术实现与实践
  • AI-Compass LLM训练框架生态:整合ms-swift、Unsloth、Megatron-LM等核心框架,涵盖全参数/PEFT训练与分布式优化
  • 简单2步配置CadenceSkill开发编辑器,支持关键字高亮
  • 【网络安全】大型语言模型(LLMs)及其应用的红队演练指南
  • 测试tcpdump,分析tcp协议