深入Spring:自定義Controller

前言

上一篇文章介紹了Spring的事務(wù)管理塘辅,接下來(lái)開(kāi)始介紹Spring的Mvc模塊钝域。首先介紹一下SpringMvc的基礎(chǔ)模塊式廷,自定義Controller休偶,RequestMapping注解,來(lái)實(shí)現(xiàn)自定義加載暗挑。

自定義Controller

Spring開(kāi)啟Mvc的主要是通過(guò)EnableWebMvc注解笋除,觀察源碼就會(huì)發(fā)現(xiàn),這個(gè)注解注入了DelegatingWebMvcConfiguration這個(gè)類(lèi)炸裆,它繼承了WebMvcConfigurationSupport垃它,注入了必要的Bean。
Spring嵌入web應(yīng)用容器的入口類(lèi)是DispatcherServlet,這個(gè)類(lèi)會(huì)讀取WebApplicationContext中的必要的bean的信息国拇,來(lái)提供mvc的服務(wù)洛史。這篇文章先介紹下Controller RequestMapping的注入和使用。
完整的代碼在Github上酱吝,這里介紹幾個(gè)主要的類(lèi)也殖。

  1. 先定義自己的注解,MyController加上了Component注解务热,這樣可以被Spring識(shí)別加載忆嗜。MyRequestMapping則完全復(fù)用RequestMapping的屬性,因?yàn)槭歉郊邮菍傩云槠瘢跃筒恍枰由?em>Component注解了捆毫。
    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface MyController {
        String value() default "";
    }
    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyRequestMapping {
        String name() default "";
        String[] value() default {};
        RequestMethod[] method() default {};
        String[] params() default {};
        String[] headers() default {};
        String[] consumes() default {};
        String[] produces() default {};
    }
  1. 定義controller和RequestMapping。
    @MyController
    public static class IndexController {
        @MyRequestMapping("/")
        @ResponseBody
        public Map index() {
            Map<String, String> map = new HashMap<String, String>();
            map.put("result", "hello world");
            return map;
        }
    }
  1. 加載自定義的注解冲甘,這里繼承自RequestMappingHandlerMapping重載了isHandlergetMappingForMethod方法來(lái)加載自定義的注解绩卤,并根據(jù)MyRequestMapping的屬性來(lái)生成RequestMappingInfo
    public static class MyRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
        @Override
        protected boolean isHandler(Class<?> beanType) {
            return ((AnnotationUtils.findAnnotation(beanType, MyController.class) != null) || (
                    AnnotationUtils.findAnnotation(beanType, MyRequestMapping.class) != null));
        }
        private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
            MyRequestMapping requestMapping = AnnotatedElementUtils
                    .findMergedAnnotation(element, MyRequestMapping.class);
            RequestCondition<?> condition = (element instanceof Class<?> ?
                    getCustomTypeCondition((Class<?>) element) :
                    getCustomMethodCondition((Method) element));
            if (requestMapping == null) {
                return null;
            }
            return RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.value()))
                    .methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers())
                    .consumes(requestMapping.consumes()).produces(requestMapping.produces())
                    .mappingName(requestMapping.name()).customCondition(condition).build();
        }
        @Override
        protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
            RequestMappingInfo info = createRequestMappingInfo(method);
            if (info != null) {
                RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
                if (typeInfo != null) {
                    info = typeInfo.combine(info);
                }
            }
            return info;
        }
    }

這個(gè)類(lèi)繼承了HandlerMapping接口江醇,觀察DispatcherServlet的源碼就會(huì)發(fā)現(xiàn)濒憋,HandlerMapping接受httpRequest并查找到對(duì)應(yīng)的method。
這個(gè)類(lèi)保存了RequestMapping的注解的方法嫁审,保存在MappingRegistry的mappingLookupurlLookup中(這里是Spring4的實(shí)現(xiàn)方式跋炕,Spring3會(huì)不一樣),
其中urlLookup是用于直接查找的directPathMatches律适,如果沒(méi)有directPathMatches辐烂,在遍歷mappingLookup,查找匹配的處理方法捂贿。

    private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
    private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }
        .....
    }
  1. 注入RequestMappingHandlerMapping纠修,這里繼承了WebMvcConfigurationSupport,然后重載了requestMappingHandlerMapping的注入方法厂僧。
    RequestMappingHandlerMapping的配置方法跟WebMvcConfigurationSupport一致扣草。
    @Configuration
    public static class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
        @Bean
        @Override
        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            MyRequestMappingHandlerMapping handlerMapping = new MyRequestMappingHandlerMapping();
            handlerMapping.setOrder(0);
            handlerMapping.setInterceptors(getInterceptors());
            handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
            handlerMapping.setCorsConfigurations(getCorsConfigurations());
            PathMatchConfigurer configurer = getPathMatchConfigurer();
            if (configurer.isUseSuffixPatternMatch() != null) {
                handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
            }
            if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
                handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
            }
            if (configurer.isUseTrailingSlashMatch() != null) {
                handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
            }
            if (configurer.getPathMatcher() != null) {
                handlerMapping.setPathMatcher(configurer.getPathMatcher());
            }
            if (configurer.getUrlPathHelper() != null) {
                handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
            }
            return handlerMapping;
        }
    }
  1. DispatcherServlet是web請(qǐng)求的處理類(lèi),接收WebApplicationContextServletConfig進(jìn)行必要參數(shù)的初始化颜屠,
    service方法辰妙,是處理請(qǐng)求的入口,接受request和response參數(shù)甫窟。簡(jiǎn)便起見(jiàn)密浑,這里不啟動(dòng)web容器,而是用MockRequest和MockResponse來(lái)模擬處理請(qǐng)求粗井。
    @Configuration
    public class CustomizeControllerTest {
        public static void main(String[] args) throws ServletException, IOException {
            // init WebApplicationContext
            AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
            MockServletContext mockServletContext = new MockServletContext();
            MockServletConfig mockServletConfig = new MockServletConfig(mockServletContext);
            annotationConfigWebApplicationContext.setServletConfig(mockServletConfig);
            annotationConfigWebApplicationContext.register(CustomizeControllerTest.class);
            // init and start DispatcherServlet
            DispatcherServlet dispatcherServlet = new DispatcherServlet(annotationConfigWebApplicationContext);
            dispatcherServlet.init(mockServletConfig);
            MockHttpServletResponse response = new MockHttpServletResponse();
            MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
            request.addHeader("Accept","application/json");
            dispatcherServlet.service(request, response);
            System.out.println(new String(response.getContentAsByteArray()));
        }
    }

結(jié)語(yǔ)

SpringMvc集成了Spring web flow的各個(gè)功能尔破,這里先介紹下Spring的Controller和RequestMapping的使用街图,接下來(lái)會(huì)介紹包括HandlerAdapter和MassageConverter等更多功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末懒构,一起剝皮案震驚了整個(gè)濱河市餐济,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胆剧,老刑警劉巖絮姆,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異秩霍,居然都是意外死亡滚朵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)前域,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人韵吨,你說(shuō)我怎么就攤上這事匿垄。” “怎么了归粉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵椿疗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我糠悼,道長(zhǎng)届榄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任倔喂,我火速辦了婚禮铝条,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘席噩。我一直安慰自己班缰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布悼枢。 她就那樣靜靜地躺著埠忘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馒索。 梳的紋絲不亂的頭發(fā)上莹妒,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音绰上,去河邊找鬼旨怠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛渔期,可吹牛的內(nèi)容都是我干的运吓。 我是一名探鬼主播渴邦,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拘哨!你這毒婦竟也來(lái)了谋梭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤倦青,失蹤者是張志新(化名)和其女友劉穎瓮床,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體产镐,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隘庄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了癣亚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丑掺。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖述雾,靈堂內(nèi)的尸體忽然破棺而出街州,到底是詐尸還是另有隱情,我是刑警寧澤玻孟,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布唆缴,位于F島的核電站,受9級(jí)特大地震影響黍翎,放射性物質(zhì)發(fā)生泄漏面徽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一匣掸、第九天 我趴在偏房一處隱蔽的房頂上張望趟紊。 院中可真熱鬧,春花似錦碰酝、人聲如沸织阳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)唧躲。三九已至,卻和暖如春碱璃,著一層夾襖步出監(jiān)牢的瞬間弄痹,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工嵌器, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肛真,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓爽航,卻偏偏與公主長(zhǎng)得像蚓让,于是被迫代替她去往敵國(guó)和親乾忱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理历极,服務(wù)發(fā)現(xiàn)窄瘟,斷路器,智...
    卡卡羅2017閱讀 134,701評(píng)論 18 139
  • 前言 上一篇文章介紹了HandlerAdapter和HttpMessageConverter趟卸,這里介紹Spring...
    wcong閱讀 7,364評(píng)論 0 4
  • 前言 上一篇文章介紹了SpringMvc的RequestMappingHandlerMapping蹄葱,自定義了Con...
    wcong閱讀 14,797評(píng)論 0 9
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法锄列,內(nèi)部類(lèi)的語(yǔ)法图云,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法邻邮,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,663評(píng)論 18 399
  • 我不擅長(zhǎng)于文字之類(lèi)的東西竣况,但我更不擅長(zhǎng)表達(dá)。我沒(méi)有可以訴說(shuō)這些東西的朋友筒严,因?yàn)樗麄儠?huì)覺(jué)得莫名其妙帕翻,所以我還是選擇了...
    袁先生總是不開(kāi)心閱讀 324評(píng)論 0 0