深入Spring:自定義Controller

前言

上一篇文章介紹了Spring的事務(wù)管理,接下來開始介紹Spring的Mvc模塊。首先介紹一下SpringMvc的基礎(chǔ)模塊,自定義Controller,RequestMapping注解,來實(shí)現(xiàn)自定義加載。

自定義Controller

Spring開啟Mvc的主要是通過EnableWebMvc注解,觀察源碼就會發(fā)現(xiàn),這個注解注入了DelegatingWebMvcConfiguration這個類,它繼承了WebMvcConfigurationSupport,注入了必要的Bean。
Spring嵌入web應(yīng)用容器的入口類是DispatcherServlet,這個類會讀取WebApplicationContext中的必要的bean的信息,來提供mvc的服務(wù)。這篇文章先介紹下Controller RequestMapping的注入和使用。
完整的代碼在Github上,這里介紹幾個主要的類。

  1. 先定義自己的注解,MyController加上了Component注解,這樣可以被Spring識別加載。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方法來加載自定義的注解,并根據(jù)MyRequestMapping的屬性來生成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;
        }
    }

這個類繼承了HandlerMapping接口,觀察DispatcherServlet的源碼就會發(fā)現(xiàn),HandlerMapping接受httpRequest并查找到對應(yīng)的method。
這個類保存了RequestMapping的注解的方法,保存在MappingRegistry的mappingLookupurlLookup中(這里是Spring4的實(shí)現(xiàn)方式,Spring3會不一樣),
其中urlLookup是用于直接查找的directPathMatches,如果沒有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請求的處理類,接收WebApplicationContextServletConfig進(jìn)行必要參數(shù)的初始化,
    service方法,是處理請求的入口,接受request和response參數(shù)。簡便起見,這里不啟動web容器,而是用MockRequest和MockResponse來模擬處理請求。
    @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é)語

SpringMvc集成了Spring web flow的各個功能,這里先介紹下Spring的Controller和RequestMapping的使用,接下來會介紹包括HandlerAdapter和MassageConverter等更多功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,533評論 19 139
  • 前言 上一篇文章介紹了HandlerAdapter和HttpMessageConverter,這里介紹Spring...
    wcong閱讀 7,561評論 0 4
  • 前言 上一篇文章介紹了SpringMvc的RequestMappingHandlerMapping,自定義了Con...
    wcong閱讀 14,957評論 0 9
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,637評論 18 399
  • 我不擅長于文字之類的東西,但我更不擅長表達(dá)。我沒有可以訴說這些東西的朋友,因?yàn)樗麄儠X得莫名其妙,所以我還是選擇了...
    袁先生總是不開心閱讀 356評論 0 0

友情鏈接更多精彩內(nèi)容