前言
上一篇文章介紹了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)也殖。
- 先定義自己的注解,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 {};
}
- 定義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方法來(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的mappingLookup和urlLookup中(這里是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);
}
.....
}
- 注入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請(qǐng)求的處理類(lèi),接收WebApplicationContext和ServletConfig進(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等更多功能。