前言
上一篇文章介绍了Spring的事务管理,接下来开始介绍Spring的Mvc模块。首先介绍一下SpringMvc的基础模块,自定义Controller,RequestMapping注解,来实现自定义加载。
自定义Controller
Spring开启Mvc的主要是通过EnableWebMvc注解,观察源码就会发现,这个注解注入了DelegatingWebMvcConfiguration这个类,它继承了WebMvcConfigurationSupport,注入了必要的Bean。
Spring嵌入web应用容器的入口类是DispatcherServlet,这个类会读取WebApplicationContext中的必要的bean的信息,来提供mvc的服务。这篇文章先介绍下Controller RequestMapping的注入和使用。
完整的代码在Github上,这里介绍几个主要的类。
- 先定义自己的注解,MyController加上了Component注解,这样可以被Spring识别加载。MyRequestMapping则完全复用RequestMapping的属性,因为是附加是属性,所以就不需要加上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 map = new HashMap();
map.put("result", "hello world");
return map;
}
}
- 加载自定义的注解,这里继承自RequestMappingHandlerMapping重载了isHandler和getMappingForMethod方法来加载自定义的注解,并根据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的源码就会发现,HandlerMapping接受httpRequest并查找到对应的method。
这个类保存了RequestMapping的注解的方法,保存在MappingRegistry的mappingLookup和urlLookup中(这里是Spring4的实现方式,Spring3会不一样),
其中urlLookup是用于直接查找的directPathMatches,如果没有directPathMatches,在遍历mappingLookup,查找匹配的处理方法。
private final Map mappingLookup = new LinkedHashMap();
private final MultiValueMap urlLookup = new LinkedMultiValueMap();
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List matches = new ArrayList();
List 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进行必要参数的初始化,
service方法,是处理请求的入口,接受request和response参数。简便起见,这里不启动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()));
}
}
结语
SpringMvc集成了Spring web flow的各个功能,这里先介绍下Spring的Controller和RequestMapping的使用,接下来会介绍包括HandlerAdapter和MassageConverter等更多功能。