前言
上一篇文章介紹了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上,這里介紹幾個主要的類。
- 先定義自己的注解,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 {};
}
- 定義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;
}
}
- 加載自定義的注解,這里繼承自RequestMappingHandlerMapping重載了isHandler和getMappingForMethod方法來加載自定義的注解,并根據(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的mappingLookup和urlLookup中(這里是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);
}
.....
}
- 注入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;
}
}
-
DispatcherServlet是web請求的處理類,接收WebApplicationContext和ServletConfig進(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等更多功能。