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

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

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

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

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

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

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

HandlerMethodArgumentResolver接口說(shuō)明

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為需要,并會(huì)去調(diào)用下面的方法resolveArgument。
    boolean supportsParameter(MethodParameter parameter);
    //真正用于處理參數(shù)分解的方法,返回的Object就是controller方法上的形參對(duì)象。
    Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

示例

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

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

first_name = Bill

last_name = Gates

初學(xué)者一般喜歡類(lèi)似下面的代碼

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();
    }
}

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

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

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

我們需要定義如下的一個(gè)參數(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中判斷是否需要啟用分解功能,這里判斷形參類(lèi)型是否為Person類(lèi),也就是說(shuō)當(dāng)形參遇到Person類(lèi)時(shí)始終會(huì)執(zhí)行該分解流程resolveArgument,也可以基于paramter上是否有我們指定的自定義注解判斷是否需要流程分解。在resolveArgument中處理person的初始化工作。

注冊(cè)自定義分解器

傳統(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

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

1. 前言

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

這里只貼出Controller層的代碼,Service層也不是本次我們的關(guān)注點(diǎn)。

// ----------------------------------------- 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));
 }
}
// ----------------------------------------- 前端訪(fǎng)問(wèn)路徑
// {{rootPath}}/a/a.do

2. 問(wèn)題

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

收集前端傳遞過(guò)來(lái)的參數(shù)。

將第一步收集來(lái)的參數(shù)傳遞給相應(yīng)的Service層的某個(gè)方法執(zhí)行。

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

所以我們?cè)谂懦羯儆械奶厥馇闆r之后,就會(huì)發(fā)現(xiàn)在一般情況下這個(gè)所謂的Controller層的存在感實(shí)在有點(diǎn)稀薄。因此本文嘗試去除掉這部分枯燥的重復(fù)性代碼。

3. 解決方案

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

// 這里之所以是 /lq , 而不是 /* ; 是因?yàn)?AntPathMatcher.combine 方法中進(jìn)行合并時(shí)的處理, 導(dǎo)致 前一個(gè) /* 丟失
/**
 * <p> 直接以前端傳遞來(lái)的Serivce名+方法名去調(diào)用Service層的同名方法; Controller層不再需要寫(xiě)任何代碼
 * <p> 例子
 * <pre>
 *   前端: /lq/thirdService/queryTaskList.do
 *   Service層相應(yīng)的方法簽名:  Object queryTaskList(Map<String, Object> parameterMap)
 *   相應(yīng)的Service注冊(cè)到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) {
  // 收集前臺(tái)傳遞來(lái)的參數(shù), 并作預(yù)處理
  final Map<String, String> parameterMap = HtmlUtils.getParameterMap(request);
  final Map<String, Object> paramsCopy = preDealOutParam(parameterMap);
  // 獲取本次的調(diào)度服務(wù)名和相應(yīng)的方法名
  //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中注冊(cè)的Service Bean
  final Object serviceBean = SpringBeanFactory.getBean(serviceName);
  Object rv;
  try {
   // 調(diào)用Service層的方法
   rv = ReflectUtil.invoke(serviceBean, serivceMethodName, paramsCopy);
   // 若用戶(hù)返回一個(gè)主動(dòng)構(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和相應(yīng)的方法名
  * @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());
 }
 
 // 將傳遞來(lái)的JSON字符串轉(zhuǎn)換為相應(yīng)的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層的代碼就算是完成了。之后的開(kāi)發(fā)工作中,在絕大多數(shù)情況下,我們將不再需要編寫(xiě)任何Controller層的代碼。只要遵循如下的約定,前端將會(huì)直接調(diào)取到Service層的相應(yīng)方法,并獲取到約定格式的響應(yīng)值。

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

5. 特殊需求

對(duì)于有額外需要的特殊Controller,可以完全按照之前的Controller層寫(xiě)法。沒(méi)有任何額外需要注意的地方。

6. 完善

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

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

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

相關(guān)文章

  • 史上最全Java8日期時(shí)間工具類(lèi)(分享)

    史上最全Java8日期時(shí)間工具類(lèi)(分享)

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

    Spring?Boot?中starter的原理詳析

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

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

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

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

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

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

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

    Java實(shí)現(xiàn)定時(shí)任務(wù)最簡(jiǎn)單的3種方法

    幾乎在所有的項(xiàng)目中,定時(shí)任務(wù)的使用都是不可或缺的,如果使用不當(dāng)甚至?xí)斐少Y損,下面這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)定時(shí)任務(wù)最簡(jiǎn)單的3種方法,本文通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • Mybatis中resultMap標(biāo)簽和sql標(biāo)簽的設(shè)置方式

    Mybatis中resultMap標(biāo)簽和sql標(biāo)簽的設(shè)置方式

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

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

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

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

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

    springboot中的dockerfile使用

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

最新評(píng)論