Spring定義Controller接口的這些方式你肯定不知道
環(huán)境:Springboot2.4.12
概述
當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)后Spring是如何進(jìn)行處理的?下面簡(jiǎn)單的羅列下請(qǐng)個(gè)的過(guò)程中核心組件
SpringMVC處理的流程:
- DispatcherServlet 所有請(qǐng)求的入口
- HandlerMapping 將請(qǐng)求地址與處理程序關(guān)聯(lián)
- HandlerAdapter 真正處理程序,如執(zhí)行上一步中對(duì)應(yīng)的處理程
- HandlerMethodArgumentResolver 對(duì)參數(shù)進(jìn)行解析,這里面還涉及到很多其它東西
- HanlderMethodReturnValueHandler 對(duì)返回值進(jìn)行輸出處理
- ViewResolver 結(jié)果HandlerAdapter返回的有ModelAndView則會(huì)應(yīng)用視圖解析器
常規(guī)Controller定義
@RestController
@RequestMapping("/users")
public class UsersController {
@GetMapping("/save")
public Object save(Users users) {
return users ;
}
}
上面這個(gè)Controller接口是我們最常的定義方法,對(duì)于絕大多數(shù)人來(lái)說(shuō)或許也就知道這樣去定義Controller接口,而這種定義Controller方式基本上已經(jīng)可以滿足我們?nèi)粘5乃胁僮髁恕=酉聛?lái)看看其它的定義Controller方法
HttpRequestHandler
@Component("/others/chrh")
public class ControllerHttpRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf8");
PrintWriter out = response.getWriter() ;
out.print("<h1>你好,HttpRequestHandler</h1>") ;
out.close() ;
}
}
定義一個(gè)類實(shí)現(xiàn)HttpRequestHandler接口即可,注意這里注解@Component("/others/chrh")使用的以‘/’ 開(kāi)頭,為什么這么定義?在概述中說(shuō)到HandlerMapping是用來(lái)將請(qǐng)求地址與處理程序關(guān)聯(lián)起來(lái),在常規(guī)中都是使用@RequestMapping定義接口請(qǐng)求地址,那在這里我們是不能用該注解的,但是又要讓容器知道我們這個(gè)接口就必須使用'/'開(kāi)頭,這樣就會(huì)有一個(gè) BeanNameUrlHandlerMapping的HandlerMapping將我們這個(gè)Bean進(jìn)行收集保存起來(lái),以 /others/chrh為key,Bean對(duì)象為value保存到Map中。
測(cè)試
圖片
Controller接口
定義一個(gè)Bean實(shí)現(xiàn)該即可
@Component("/others/custom")
public class CustomController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/html;charset=utf8");
PrintWriter out = response.getWriter() ;
out.print("<h1>Controller接口</h1>") ;
out.close() ;
return null ;
}
}
Bean的名稱還是以'/' 開(kāi)頭,那么還是由BeanNameUrlHandlerMapping與之關(guān)聯(lián)
測(cè)試
圖片
繼承HttpServlet
這里的判斷依據(jù)就是你的這個(gè)Bean是否是Servlet接口類型(有沒(méi)有實(shí)現(xiàn)Servlet接口)。
@Component("/others/servlet")
public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf8");
PrintWriter out = response.getWriter() ;
out.print("<h1>你好 HttpServlet</h1>") ;
out.close() ;
}
}
這樣看起來(lái)就是個(gè)標(biāo)準(zhǔn)的Servlet程序。
如果你只是這樣,那這可運(yùn)行不起來(lái),你還需要注冊(cè)一個(gè)
SimpleServletHandlerAdapter。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public SimpleServletHandlerAdapter simpleServletHandlerAdapter() {
return new SimpleServletHandlerAdapter() ;
}
}
測(cè)試
圖片
在這個(gè)程序中我們還可以讓這個(gè)程序成為一個(gè)受應(yīng)用服務(wù)管理的Servlet程序。可以將注解改成@WebServlet("/others/servlet")。只是換成這個(gè)注解還并不能生效,還需要在啟動(dòng)類(任何配置類上)添加@ServletComponentScan注解。
以上就是在SpringMVC中支持的幾種接口定義處理方式。
下一篇文章會(huì)對(duì)上面幾種實(shí)現(xiàn)的方式進(jìn)行源碼分析,一個(gè)請(qǐng)求是如何知道使用哪個(gè)HandlerMapping的,找到了HandlerMapping又是怎么確定由哪個(gè)HandlerAdapter處理的(在上面的例子中,每一種實(shí)現(xiàn)他們的實(shí)現(xiàn)方法都不一樣,所以一定的需要不同的HandlerAdapter進(jìn)行處理);通過(guò)源碼的分析,讓你知其然,知其所以然。關(guān)注我讓你對(duì)Spring源碼不再畏懼。