每日一例 | 手寫RequestParameter注解,實現(xiàn)有參方法調(diào)用

前言
昨天實現(xiàn)了requestMapping和controller注解,解決了請求地址與接口方法之間的映射問題,可以根據(jù)前端請求地址調(diào)用對應(yīng)的接口方法。但是還不能解決有參方法的調(diào)用,今天我們就來啃下這個硬碴,解決這個最后一公里。
開肝
定義RequestParameter注解
springboot的注解是RequestParam,我們今天實現(xiàn)的需求就是參考RequestParam,但是我沒有時間去研究springboot的源碼(至少今天沒有時間),就先按照自己的想法來實現(xiàn)了。和前面注解不一樣的一點是,這個注解的target指定的是ElementType.PARAMETER,因為這個注解是加在方法的參數(shù)上的。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestParameter {
String value();
}
加到方法的參數(shù)上是這樣的:
@RequestMapping("/sayHello")
public String test(@RequestParameter("name") String name) {
return "hello," + name;
}
是不是看著還挺像像樣的。
業(yè)務(wù)端使用
目前業(yè)務(wù)處理還是在doDispatcher方法,所以今天依然是動這個方法。
先說下思路,思路也很簡單,就是服務(wù)器收到前端請求后,根據(jù)requestMapping(請求地址)拿到對應(yīng)的方法,從方法中拿到參數(shù)注解RequestParameter(這個注解的作用就是標(biāo)記參數(shù)名,讓我們可以根據(jù)參數(shù)名拿到參數(shù)),然后根據(jù)注解的value拿到參數(shù)名,然后根據(jù)參數(shù)名從requestAttributeMap拿到請求參數(shù)的值,組裝方法入?yún)⒘斜怼?/p>
/**
* 請求分發(fā)處理
* @throws Exception
*/
public void doDispatcher() throws Exception{
RequestHear requestHear = request.getRequestHear();
Map<String, Object> requestAttributeMap = request.getRequestAttributeMap();
logger.info("請求頭信息:{}", requestHear);
logger.info("請求信息:{}", requestAttributeMap);
if (Objects.isNull(requestHear) || Objects.isNull(requestAttributeMap)) {
return;
}
String requestMapping = requestHear.getRequestMapping();
if (requestMappingMap.containsKey(requestMapping)) {
Method method = requestMappingMap.get(requestMapping);
logger.debug("method:{}", method);
Class<?> declaringClass = method.getDeclaringClass();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Object[] parameters = new Object[parameterAnnotations.length];
for (int i = 0; i < parameterAnnotations.length; i++) {
RequestParameter annotation = (RequestParameter)parameterAnnotations[i][0];
parameters[i] = requestAttributeMap.get(annotation.value());
}
Object o = declaringClass.newInstance();
Object invoke = method.invoke(o, parameters);
logger.info("invoke:{}", invoke);
response.write(String.format("hello syskeCat, dateTime:%d\n result = %s", System.currentTimeMillis(), invoke));
} else {
response.write(404, String.format("resources not found :%d", System.currentTimeMillis()));
}
socket.close();
}
我單獨把修改部分拿出來,簡單解釋下。
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Object[] parameters = new Object[parameterAnnotations.length];
for (int i = 0; i < parameterAnnotations.length; i++) {
RequestParameter annotation = (RequestParameter)parameterAnnotations[i][0];
parameters[i] = requestAttributeMap.get(annotation.value());
}
Object o = declaringClass.newInstance();
Object invoke = method.invoke(o, parameters);
method.getParameterAnnotations的作用是獲取方法的所有參數(shù)注解,返回結(jié)果是一個二維數(shù)組,因為一個方法可以有多個參數(shù),每個參數(shù)都可以有多個注解,所以肯定是一個二維數(shù)組。parameterAnnotations[0][0]就是第一個參數(shù)的第一個注解,parameterAnnotations[1][0]就是第二個參數(shù)的第一個注解,其他以此類推。這張圖更清楚地說明了這一點:

因為獲取到的注解是Annotation,并非是我們的定義的注解,所以需要進行強制轉(zhuǎn)換成我們的自定義注解RequestParameter。
然后通過注解的value()方法從注解中拿到參數(shù)名,根據(jù)參數(shù)名從參數(shù)map中拿到請求的值,組裝成參數(shù)集合,然后反射調(diào)用。
請求參數(shù)處理這邊我們也做了一些調(diào)整,主要是為了獲取請求參數(shù)參數(shù):
Map<String, Object> attributeMap = Maps.newHashMap();
if (requestMapping.contains("?")) {
int endIndex = requestMapping.lastIndexOf('?');
String requestParameterStr = requestMapping.substring(endIndex + 1);
requestMapping = requestMapping.substring(0, endIndex);
String[] split = requestParameterStr.split("&");
for (String s : split) {
String[] split1 = s.split("=");
attributeMap.put(StringUtil.trim(split1[0]), StringUtil.trim(split1[1]));
}
}
主要就是在增加了請求參數(shù)的處理,把sayHello?name=yunzhongzhi&age=12處理成requestMapping和請求參數(shù)map。這塊后期還需要優(yōu)化,要把get請求和post請求分開,分別處理,所以目前我們的服務(wù)器只支持get請求。
測試
完成以上調(diào)整,我們的方法就已經(jīng)支持有參調(diào)用了,我們來測試下吧!



效果還不錯,目標(biāo)完美達成。
總結(jié)
感覺有思路的話,寫東西還是比較快的,這些需求都是今天早上實現(xiàn)的,中間被Annotation卡住了,一直無法獲取到參數(shù)名,后來發(fā)現(xiàn)只要強轉(zhuǎn)一下就行了。千里之行,始于足下,感興趣的小伙伴肝起來。
下面是項目的開源倉庫,有興趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推薦你自己動個手,自己寫一下,真的感覺不錯:
https://github.com/Syske/syske-boot

