深入Spring:自定義ViewResolver

前言

上一篇文章介紹了SpringMvc的ControllerAdviceExceptionHandler提岔,這里在介紹一下ViewResolver的使用,并介紹一下HandlerMethodReturnValueHandlerViewResolver的關(guān)系笋敞。

ViewResolver和HandlerMethodReturnValueHandler

自定義ResponseBody這篇文章介紹過ResponseBody的編碼規(guī)則碱蒙,ViewResolverResponseBody是明顯互斥的。
這兩個(gè)不同類型的返回值就是通過不同的HandlerMethodReturnValueHandler來是實(shí)現(xiàn)的夯巷。
先看RequestResponseBodyMethodProcessor振亮,這里設(shè)置了setRequestHandledtrue,然后通過HttpMessageConverters編碼對(duì)應(yīng)的model鞭莽。

    public void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        mavContainer.setRequestHandled(true);
        // Try even with null return value. ResponseBodyAdvice could get involved.
        writeWithMessageConverters(returnValue, returnType, webRequest);
    }

再看ViewNameMethodReturnValueHandler坊秸,這里沒有設(shè)置setRequestHandled,而是取出CharSequence類型的返回值澎怒,并賦值viewName褒搔。

    public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        if (returnValue instanceof CharSequence) {
            String viewName = returnValue.toString();
            mavContainer.setViewName(viewName);
            if (isRedirectViewName(viewName)) {
                mavContainer.setRedirectModelScenario(true);
            }
        }
        else if (returnValue != null){
            // should not happen
            throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
        }
    }

再看RequestMappingHandlerAdapter中使用方法,假如mavContainer.isRequestHandled是true直接返回null喷面,后面就不會(huì)調(diào)用ViewResolver了星瘾。假如是false,會(huì)取出mavContainer.getViewName惧辈,返回ModelAndView琳状,后面會(huì)根據(jù)ViewName進(jìn)行模板的映射。

    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
            ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
        modelFactory.updateModel(webRequest, mavContainer);
        if (mavContainer.isRequestHandled()) {
            return null;
        }
        ModelMap model = mavContainer.getModel();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
        return mav;
    }

自定義ViewResolver

相比ExceptionHandler盒齿,自定義ViewResolver就比較簡單了念逞,只要注入一個(gè)ViewResolver的實(shí)現(xiàn)類就可以了。
不過為了介紹ViewResolver的原理边翁,這里自定義了一個(gè)HandlerMethodReturnValueHandler來取代ViewNameMethodReturnValueHandler;
完整的代碼還是在Github上了翎承。

  1. 定義Controller。ViewName自定義類來包裝viewName符匾。
    @Controller
    public static class ControllerClass {
        @RequestMapping
        public ViewName index(ModelMap modelMap) {
            modelMap.put("message", "hello world");
            ViewName viewName = new ViewName();
            viewName.setName("index");
            return viewName;
        }
        @RequestMapping("html")
        public ViewName htmlIndex(ModelMap modelMap) {
            modelMap.put("message", "hello world");
            ViewName viewName = new ViewName();
            viewName.setName("html");
            return viewName;
        }
    }
    public static class ViewName {
        private String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
  1. 注入自定義的ViewResolversHandlerMethodReturnValueHandler
    @Configuration
    public static class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
        public void configureViewResolvers(ViewResolverRegistry registry) {
            MyViewResolver myViewResolver = new MyViewResolver();
            registry.viewResolver(myViewResolver);
        }
        public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
            returnValueHandlers.add(new MyHandlerMethodReturnValueHandler());
        }
    }
  1. 自定義HandlerMethodReturnValueHandler叨咖,指定只支持ViewName這個(gè)類,處理時(shí)啊胶,取出來ViewNamename甸各,設(shè)置到ModelAndViewContainer中。
    public static class MyHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
        public boolean supportsReturnType(MethodParameter returnType) {
            return returnType.getParameterType().isAssignableFrom(ViewName.class);
        }
        public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
            ViewName viewName = (ViewName) returnValue;
            mavContainer.setViewName(viewName.getName());
        }
    }
  1. 自定義ViewResolver焰坪,定義了MyViewMyHtmlView一個(gè)處理index返回純文字趣倾,另一個(gè)處理html返回html格式。
    public static class MyViewResolver implements ViewResolver {
        private View htmlView = new MyHtmlView();
        private View view = new MyView();
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            if (viewName.equals("index")) {
                return view;
            } else if (viewName.equals("html")) {
                return htmlView;
            }
            return null;
        }
    }
    public static class MyView implements View {
        public String getContentType() {
            return "text/html";
        }
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            StringBuilder stringBuilder = new StringBuilder();
            for (Map.Entry<String, ?> entry : model.entrySet()) {
                stringBuilder.append(entry.getKey()).append(":").append(entry.getValue());
            }
            response.getWriter().write(stringBuilder.toString());
        }
    }
    public static class MyHtmlView implements View {
        public String getContentType() {
            return "text/html";
        }
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            String head = "<html><head><title>Hello World</title></head><body><ul>";
            String tail = "</ul></body></html>";
            StringBuilder sb = new StringBuilder();
            sb.append(head);
            for (Map.Entry<String, ?> entry : model.entrySet()) {
                sb.append("<li>").append(entry.getKey()).append(":").append(entry.getValue()).append("</li>");
            }
            sb.append(tail);
            response.getWriter().write(sb.toString());
        }
    }
  1. 程序入口琳彩,這里同時(shí)請(qǐng)求了//html分別用到了MyViewMyHtmlView誊酌。
    @Configuration
    public class CustomizeViewResolverTest {
        public static void main(String[] args) throws ServletException, IOException {
            MockServletContext mockServletContext = new MockServletContext();
            MockServletConfig mockServletConfig = new MockServletConfig(mockServletContext);
            AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
            annotationConfigWebApplicationContext.setServletConfig(mockServletConfig);
            annotationConfigWebApplicationContext.register(CustomizeViewResolverTest.class);
            DispatcherServlet dispatcherServlet = new DispatcherServlet(annotationConfigWebApplicationContext);
            dispatcherServlet.init(mockServletConfig);
            MockHttpServletResponse response = new MockHttpServletResponse();
            MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
            dispatcherServlet.service(request, response);
            System.out.println(new String(response.getContentAsByteArray()));
            MockHttpServletResponse htmlResponse = new MockHttpServletResponse();
            MockHttpServletRequest htmlRequest = new MockHttpServletRequest("GET", "/html");
            dispatcherServlet.service(htmlRequest, htmlResponse);
            System.out.println(new String(htmlResponse.getContentAsByteArray()));
        }
    }

運(yùn)行程序就會(huì)發(fā)現(xiàn),一個(gè)輸出了文章露乏,另一個(gè)輸出了html格式的文字碧浊。

結(jié)語

ViewResolverResponseBody都是用來處理HttpResponseBody的內(nèi)容的,只不過處理的方式不同瘟仿。
ResponseBody的編碼方式是一樣的箱锐,一般是處理JSON的編碼。ViewResolver還可以一根據(jù)ViewName來路由到不用的View劳较,每個(gè)View都可以自己的編碼規(guī)則驹止。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市观蜗,隨后出現(xiàn)的幾起案子臊恋,更是在濱河造成了極大的恐慌,老刑警劉巖墓捻,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抖仅,死亡現(xiàn)場離奇詭異,居然都是意外死亡砖第,警方通過查閱死者的電腦和手機(jī)撤卢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梧兼,“玉大人放吩,你說我怎么就攤上這事∮鸾埽” “怎么了渡紫?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長考赛。 經(jīng)常有香客問我腻惠,道長,這世上最難降的妖魔是什么欲虚? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任集灌,我火速辦了婚禮,結(jié)果婚禮上复哆,老公的妹妹穿的比我還像新娘欣喧。我一直安慰自己,他們只是感情好梯找,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布唆阿。 她就那樣靜靜地躺著,像睡著了一般锈锤。 火紅的嫁衣襯著肌膚如雪驯鳖。 梳的紋絲不亂的頭發(fā)上闲询,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音浅辙,去河邊找鬼扭弧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛记舆,可吹牛的內(nèi)容都是我干的鸽捻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼泽腮,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼御蒲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起诊赊,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤厚满,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后碧磅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痰滋,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年续崖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敲街。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡严望,死狀恐怖多艇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情像吻,我是刑警寧澤峻黍,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站拨匆,受9級(jí)特大地震影響姆涩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惭每,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一骨饿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧台腥,春花似錦宏赘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至峻汉,卻和暖如春贴汪,著一層夾襖步出監(jiān)牢的瞬間脐往,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工扳埂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留业簿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓聂喇,卻偏偏與公主長得像辖源,于是被迫代替她去往敵國和親蔚携。 傳聞我的和親對(duì)象是個(gè)殘疾皇子希太,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法酝蜒,內(nèi)部類的語法誊辉,繼承相關(guān)的語法,異常的語法亡脑,線程的語...
    子非魚_t_閱讀 31,644評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理堕澄,服務(wù)發(fā)現(xiàn),斷路器霉咨,智...
    卡卡羅2017閱讀 134,672評(píng)論 18 139
  • 前言 上一篇文章介紹了SpringMvc的RequestMappingHandlerMapping蛙紫,自定義了Con...
    wcong閱讀 14,784評(píng)論 0 9
  • 多態(tài) 任何域的訪問操作都將有編譯器解析,如果某個(gè)方法是靜態(tài)的途戒,它的行為就不具有多態(tài)性 java默認(rèn)對(duì)象的銷毀順序與...
    yueyue_projects閱讀 944評(píng)論 0 1
  • 累呀坑傅,很累,忙了一整天了喷斋。 寶寶睡了唁毒,我忍不住要為寶媽吶喊。 早起八點(diǎn)多星爪,挺享受的吧...
    人生留白_81e6閱讀 403評(píng)論 4 5