欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringMVC對自定義controller入?yún)㈩A處理方式

 更新時間:2021年09月14日 11:03:11   作者:xdoyf  
這篇文章主要介紹了SpringMVC對自定義controller入?yún)㈩A處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Spring Mvc對自定義controller入?yún)㈩A處理

在初學springmvc框架時,我就一直有一個疑問,為什么controller方法上竟然可以放這么多的參數(shù),而且都能得到想要的對象,比如HttpServletRequest或HttpServletResponse,各種注解@RequestParam、@RequestHeader、@RequestBody、@PathVariable、@ModelAttribute等。相信很多初學者都曾經(jīng)感慨過。

這篇文章就是講解處理這方面內(nèi)容的

我們可以模仿springmvc的源碼,實現(xiàn)一些我們自己的實現(xiàn)類,而方便我們的代碼開發(fā)。

HandlerMethodArgumentResolver接口說明

package org.springframework.web.method.support;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
public interface HandlerMethodArgumentResolver {
    //用于判定是否需要處理該參數(shù)分解,返回true為需要,并會去調(diào)用下面的方法resolveArgument。
    boolean supportsParameter(MethodParameter parameter);
    //真正用于處理參數(shù)分解的方法,返回的Object就是controller方法上的形參對象。
    Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

示例

本示例顯示如何 優(yōu)雅地將傳入的信息轉(zhuǎn)化成自定義的實體傳入controller方法。

post 數(shù)據(jù):

first_name = Bill

last_name = Gates

初學者一般喜歡類似下面的代碼

package com.demo.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.demo.domain.Person;
import com.demo.mvc.annotation.MultiPerson;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequestMapping("demo1")
public class HandlerMethodArgumentResolverDemoController {
    @ResponseBody
    @RequestMapping(method = RequestMethod.POST)
    public String addPerson(HttpServletRequest request) {
        String firstName = request.getParameter("first_name");
        String lastName = request.getParameter("last_name");
        Person person = new Person(firstName, lastName);
        log.info(person.toString());
        return person.toString();
    }
}

這樣的代碼強依賴了javax.servlet-api的HttpServletRequest對象,并且把初始化Person對象這“活兒”加塞給了controller。代碼顯得累贅不優(yōu)雅。在controller里我只想使用person而不想組裝person,想要類似下面的代碼:

@RequestMapping(method = RequestMethod.POST)
public String addPerson(Person person) {
  log.info(person.toString());
  return person.toString();
}

直接在形參列表中獲得person。那么這該如實現(xiàn)呢?

我們需要定義如下的一個參數(shù)分解器

package com.demo.mvc.component;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.demo.domain.Person;
public class PersonArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(Person.class);
    }
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String firstName = webRequest.getParameter("first_name");
        String lastName = webRequest.getParameter("last_name");
        return new Person(firstName, lastName);
    }
}

在supportsParameter中判斷是否需要啟用分解功能,這里判斷形參類型是否為Person類,也就是說當形參遇到Person類時始終會執(zhí)行該分解流程resolveArgument,也可以基于paramter上是否有我們指定的自定義注解判斷是否需要流程分解。在resolveArgument中處理person的初始化工作。

注冊自定義分解器

傳統(tǒng)XML配置:

<mvc:annotation-driven>
      <mvc:argument-resolvers>
        <bean class="com.demo.mvc.component.PersonArgumentResolver"/>
      </mvc:argument-resolvers>
</mvc:annotation-driven>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="customArgumentResolvers">
          <bean class="com.demo.mvc.component.PersonArgumentResolver"/>
    </property>
</bean>

spring boot java代碼配置:

public class WebConfig extends WebMvcConfigurerAdapter{
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new CustomeArgumentResolver());
    }
}

SpringMVC技巧之通用Controller

一個通用Controller。大多數(shù)情況下不再需要編寫任何Controller層代碼,將開發(fā)人員的關注點全部集中到Service層。

1. 前言

平時在進行傳統(tǒng)的MVC開發(fā)時,為了完成某個特定的功能,我們通常需要同時編寫Controller,Service,Dao層的代碼。代碼模式大概是這樣的。

這里只貼出Controller層的代碼,Service層也不是本次我們的關注點。

// ----------------------------------------- Controller層
@RestController
@RequestMapping("/a")
public class AController {
 @Resource(name = "aService")
 private AService aService;
 
 @PostMapping(value = "/a")
 public ResponseBean<String> a(HttpServletRequest request, HttpServletResponse response) {
  final String name = WebUtils.findParameterValue(request, "name");
  return ResponseBean.of(aService.invoke(name));
 }
}
// ----------------------------------------- 前端訪問路徑
// {{rootPath}}/a/a.do

2. 問題

只要有過幾個月Java Web開發(fā)經(jīng)驗的,應該對這樣的代碼非常熟悉,熟悉到惡心。我們稍微注意下就會發(fā)現(xiàn):上面的Controller代碼中,大致做了如下事情:

收集前端傳遞過來的參數(shù)。

將第一步收集來的參數(shù)傳遞給相應的Service層的某個方法執(zhí)行。

將Service層執(zhí)行后的結(jié)果使用Controller層特有的ResponseBean進行封裝后返回給前臺。

所以我們在排除掉少有的特殊情況之后,就會發(fā)現(xiàn)在一般情況下這個所謂的Controller層的存在感實在有點稀薄。因此本文嘗試去除掉這部分枯燥的重復性代碼。

3. 解決方案

直接上代碼。talk is cheap, show me the code。

// 這里之所以是 /lq , 而不是 /* ; 是因為 AntPathMatcher.combine 方法中進行合并時的處理, 導致 前一個 /* 丟失
/**
 * <p> 直接以前端傳遞來的Serivce名+方法名去調(diào)用Service層的同名方法; Controller層不再需要寫任何代碼
 * <p> 例子
 * <pre>
 *   前端: /lq/thirdService/queryTaskList.do
 *   Service層相應的方法簽名:  Object queryTaskList(Map<String, Object> parameterMap)
 *   相應的Service注冊到Spring容器中的id : thirdServiceService
 * </pre>
 * @author LQ
 *
 */
@RestController
@RequestMapping("/lq")
public class CommonController {
 private static final Logger LOG = LoggerFactory.getLogger(ThirdServiceController.class);
 @PostMapping(value = "/{serviceName}/{serviceMethodName}")
 public void common(@PathVariable String serviceName, @PathVariable final String serviceMethodName, HttpServletRequest request, HttpServletResponse response) {
  // 收集前臺傳遞來的參數(shù), 并作預處理
  final Map<String, String> parameterMap = HtmlUtils.getParameterMap(request);
  final Map<String, Object> paramsCopy = preDealOutParam(parameterMap);
  // 獲取本次的調(diào)度服務名和相應的方法名
  //final List<String> serviceAndMethod = parseServiceAndMethod(request);
  //final String serviceName = serviceAndMethod.get(0) + "Service";
  //final String serivceMethodName = serviceAndMethod.get(1);
  
  // 直接使用Spring3.x新加入的@PathVariable注解; 代替上面的自定義操作
  serviceName = serviceName + "Service";
  final String fullServiceMethodName = StringUtil.format("{}.{}", serviceName, serivceMethodName);
  // 輸出日志, 方便回溯
  LOG.debug("### current request method is [ {} ] ,  parameters is [ {} ]", fullServiceMethodName, parameterMap);
  // 獲取Spring中注冊的Service Bean
  final Object serviceBean = SpringBeanFactory.getBean(serviceName);
  Object rv;
  try {
   // 調(diào)用Service層的方法
   rv = ReflectUtil.invoke(serviceBean, serivceMethodName, paramsCopy);
   // 若用戶返回一個主動構(gòu)建的FriendlyException
   if (rv instanceof FriendlyException) {
    rv = handlerException(fullServiceMethodName, (FriendlyException) rv);
   } else {
    rv = returnVal(rv);
   }
  } catch (Exception e) {
   rv = handlerException(fullServiceMethodName, e);
  }
  LOG.debug("### current request method [ {} ] has dealed,  rv is [ {} ]", fullServiceMethodName, rv);
  HtmlUtils.writerJson(response, rv);
 }
 /**
  * 解析出Service和相應的方法名
  * @param request
  * @return
  */
 private List<String> parseServiceAndMethod(HttpServletRequest request) {
  // /lq/thirdService/queryTaskList.do 解析出 [ thirdService, queryTaskList ]
  final String serviceAndMethod = StringUtil.subBefore(request.getServletPath(), ".", false);
  List<String> split = StringUtil.split(serviceAndMethod, '/', true, true);
  return split.subList(1, split.size());
 }
 
 // 將傳遞來的JSON字符串轉(zhuǎn)換為相應的Map, List等
 private Map<String, Object> preDealOutParam(final Map<String, String> parameterMap) {
  final Map<String, Object> outParams = new HashMap<String, Object>(parameterMap.size());
  for (Map.Entry<String, String> entry : parameterMap.entrySet()) {
   outParams.put(entry.getKey(), entry.getValue());
  }
  for (Map.Entry<String, Object> entry : outParams.entrySet()) {
   final String value = (String) entry.getValue();
   if (StringUtil.isEmpty(value)) {
    entry.setValue("");
    continue;
   }
   Object parsedObj = JSONUtil.tryParse(value);
   // 不是JSON字符串格式
   if (null == parsedObj) {
    continue;
   }
   entry.setValue(parsedObj);
  }
  return outParams;
 }
 // 構(gòu)建成功執(zhí)行后的返回值
 private Object returnVal(Object data) {
  return MapUtil.newMapBuilder().put("data", data).put("status", 200).put("msg", "success").build();
 }
 // 構(gòu)建執(zhí)行失敗后的返回值
 private Object handlerException(String distributeMethod, Throwable e) {
  final String logInfo = StringUtil.format("[ {} ] fail", distributeMethod);
  LOG.error(logInfo, ExceptionUtil.getRootCause(e));
  return MapUtil.newMapBuilder().put("data", "").put("status", 500)
    .put("msg", ExceptionUtil.getRootCause(e).getMessage()).build();
 }
}

4. 使用

到此為止,Controller層的代碼就算是完成了。之后的開發(fā)工作中,在絕大多數(shù)情況下,我們將不再需要編寫任何Controller層的代碼。只要遵循如下的約定,前端將會直接調(diào)取到Service層的相應方法,并獲取到約定格式的響應值。

  • 前端請求路徑 : {{rootPath}}/lq/serviceName/serviceMethodName.do
  • {{rootPath}} : 訪問地址的根路徑
  • lq :自定義的固定名稱,用于滿足SpringMVC的映射規(guī)則。
  • serviceName : 用于獲取Spring容器中的Service Bean。這里的規(guī)則是 該名稱后附加上Service字符來作為Bean Id來從Spring容器中獲取相應 Service Bean。
  • serviceMethodName : 第三步中找到的Service Bean中的名為serviceMethodName的方法。簽名為Object serviceMethodName(Map<String,Object> param)。

5. 特殊需求

對于有額外需要的特殊Controller,可以完全按照之前的Controller層寫法。沒有任何額外需要注意的地方。

6. 完善

上面的Service層的方法簽名中,其參數(shù)使用的是固定的Map<String,Object> param。對Map和Bean的爭論由來已久,經(jīng)久不衰,這里不攪和這趟渾水。

對于希望使用Bean作為方法參數(shù)的,可以參考SpringMVC中對Controller層方法調(diào)用的實現(xiàn),來達到想要的效果。具體的實現(xiàn)就不在這里獻丑了,有興趣的同學可以參考下源碼ServletInvocableHandlerMethod.invokeAndHandle。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 史上最全Java8日期時間工具類(分享)

    史上最全Java8日期時間工具類(分享)

    這篇文章主要介紹了史上最全Java8日期時間工具類(分享),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • Spring?Boot?中starter的原理詳析

    Spring?Boot?中starter的原理詳析

    這篇文章主要介紹了Spring?Boot?中starter原理詳析,文章圍繞主題展開springboot的插件機制和starter原理相關資料,需要的小伙伴可以參考一下
    2022-06-06
  • Java微信授權(quán)登錄小程序接口流程

    Java微信授權(quán)登錄小程序接口流程

    微信授權(quán)登錄小程序的流程是一個涉及前端和后端交互的過程,主要目的是讓用戶能夠使用微信賬號快速登錄小程序,避免重復輸入用戶名和密碼,下面給大家介紹Java微信授權(quán)登錄小程序接口流程,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • Java圖像之自定義角度旋轉(zhuǎn)(實例)

    Java圖像之自定義角度旋轉(zhuǎn)(實例)

    這篇文章主要介紹了Java圖像之自定義角度旋轉(zhuǎn)(實例),需要的朋友可以參考下
    2017-09-09
  • JAVA實現(xiàn)KMP算法理論和示例代碼

    JAVA實現(xiàn)KMP算法理論和示例代碼

    本文從理論到代碼講解了JAVA對KMP算法的實現(xiàn),大家可以參考一下
    2013-11-11
  • Java實現(xiàn)定時任務最簡單的3種方法

    Java實現(xiàn)定時任務最簡單的3種方法

    幾乎在所有的項目中,定時任務的使用都是不可或缺的,如果使用不當甚至會造成資損,下面這篇文章主要給大家介紹了關于Java實現(xiàn)定時任務最簡單的3種方法,本文通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • Mybatis中resultMap標簽和sql標簽的設置方式

    Mybatis中resultMap標簽和sql標簽的設置方式

    這篇文章主要介紹了Mybatis中resultMap標簽和sql標簽的設置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • JVM:你知道為什么對象一定在堆中分配嗎

    JVM:你知道為什么對象一定在堆中分配嗎

    這篇文章主要介紹了jvm對象的創(chuàng)建和分配的相關資料,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下,希望能夠給你帶來幫助
    2021-08-08
  • java代碼實現(xiàn)雙向鏈表

    java代碼實現(xiàn)雙向鏈表

    這篇文章主要為大家詳細介紹了java代碼實現(xiàn)雙向鏈表,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • springboot中的dockerfile使用

    springboot中的dockerfile使用

    這篇文章主要介紹了springboot中的dockerfile使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評論