一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringMVC對自定義controller入參預處理方式

SpringMVC對自定義controller入參預處理方式

2021-12-31 00:45xdoyf Java教程

這篇文章主要介紹了SpringMVC對自定義controller入參預處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Spring Mvc對自定義controller入參預處理

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

這篇文章就是講解處理這方面內容的

我們可以模仿springmvc的源碼,實現一些我們自己的實現類,而方便我們的代碼開發。

HandlerMethodArgumentResolver接口說明

?
1
2
3
4
5
6
7
8
9
10
11
12
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 {
    //用于判定是否需要處理該參數分解,返回true為需要,并會去調用下面的方法resolveArgument。
    boolean supportsParameter(MethodParameter parameter);
    //真正用于處理參數分解的方法,返回的Object就是controller方法上的形參對象。
    Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

示例

本示例顯示如何 優雅地將傳入的信息轉化成自定義的實體傳入controller方法。

post 數據:

first_name = Bill

last_name = Gates

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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。代碼顯得累贅不優雅。在controller里我只想使用person而不想組裝person,想要類似下面的代碼:

?
1
2
3
4
5
@RequestMapping(method = RequestMethod.POST)
public String addPerson(Person person) {
  log.info(person.toString());
  return person.toString();
}

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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類時始終會執行該分解流程resolveArgument,也可以基于paramter上是否有我們指定的自定義注解判斷是否需要流程分解。在resolveArgument中處理person的初始化工作。

注冊自定義分解器

傳統XML配置:

?
1
2
3
4
5
<mvc:annotation-driven>
      <mvc:argument-resolvers>
        <bean class="com.demo.mvc.component.PersonArgumentResolver"/>
      </mvc:argument-resolvers>
</mvc:annotation-driven>

?
1
2
3
4
5
<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代碼配置:

?
1
2
3
4
5
6
public class WebConfig extends WebMvcConfigurerAdapter{
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new CustomeArgumentResolver());
    }
}

SpringMVC技巧之通用Controller

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

1. 前言

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ----------------------------------------- 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開發經驗的,應該對這樣的代碼非常熟悉,熟悉到惡心。我們稍微注意下就會發現:上面的Controller代碼中,大致做了如下事情:

收集前端傳遞過來的參數。

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

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

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

3. 解決方案

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// 這里之所以是 /lq , 而不是 /* ; 是因為 AntPathMatcher.combine 方法中進行合并時的處理, 導致 前一個 /* 丟失
/**
 * <p> 直接以前端傳遞來的Serivce名+方法名去調用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) {
  // 收集前臺傳遞來的參數, 并作預處理
  final Map<String, String> parameterMap = HtmlUtils.getParameterMap(request);
  final Map<String, Object> paramsCopy = preDealOutParam(parameterMap);
  // 獲取本次的調度服務名和相應的方法名
  //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 {
   // 調用Service層的方法
   rv = ReflectUtil.invoke(serviceBean, serivceMethodName, paramsCopy);
   // 若用戶返回一個主動構建的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字符串轉換為相應的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;
 }
 // 構建成功執行后的返回值
 private Object returnVal(Object data) {
  return MapUtil.newMapBuilder().put("data", data).put("status", 200).put("msg", "success").build();
 }
 // 構建執行失敗后的返回值
 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層的代碼就算是完成了。之后的開發工作中,在絕大多數情況下,我們將不再需要編寫任何Controller層的代碼。只要遵循如下的約定,前端將會直接調取到Service層的相應方法,并獲取到約定格式的響應值。

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

5. 特殊需求

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

6. 完善

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

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

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://www.jianshu.com/p/ac976b9fd8d7

延伸 · 閱讀

精彩推薦
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
主站蜘蛛池模板: 日本一区二区三区在线 视频 | 日本暖暖视频在线观看 | 呜呜别塞了啊抽插 | 扒开双腿猛进入爽爽视频ai | 国产亚洲精品一区久久 | 私人影院在线播放 | 国产精品久久国产精品99 | 国产日本免费 | 99久久国产综合精麻豆 | 国产rpg迷雾之风冷狐破解 | 国产成人综合一区人人 | 91嫩草国产在线观看免费 | 色综合久久九月婷婷色综合 | 欧美人在线一区二区三区 | 天堂在线中文无弹窗全文阅读 | 女性全身裸露无遮挡 | 91色视 | 天天综合五月天 | 色婷婷综合久久久中文字幕 | 二次元美女内裤凹陷太深 | 九九99在线视频 | 好姑娘在线视频观看免费 | 欧美一区二区三区gg高清影视 | 欧美一级久久久久久久大片 | 轻轻操在线视频 | 91噜噜噜在线观看 | av中文字幕在线 | 91sao在线看片水片 | 四虎精品在线观看 | 日韩在线天堂免费观看 | 国产成人在线影院 | 69老司机亚洲精品一区 | 免费看一区二区三区 | 2014天堂| 国产一级大片免费看 | fuqer日本 | 黑人与欧洲女子性大战 | 荷兰精品女人性hd | 四虎1515h永久 | 欧美日韩精品一区二区三区视频在线 | 911精品国产亚洲日本美国韩国 |