根据源码十一中,可以了解到,DispatcherServlet先对请求进行检查,如果是上传请求则封装,然后根据HandlerMapper的getHandler方法找到对应的Handler。
先看DispatcherServlet中的初始化HandlerMapper,默认是加载三个HandlerMapper的实现类
//初始化HandlerMapping
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//判断是否默认添加所有的HandlerMapping类,初始值是默认全部添加的
//有BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping,RequestMappingHandlerMapping
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 保持HandlerMappings是有序的
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
//如果不是默认添加所有的,那么就去context中找一个HandlerMapping的bean
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
//如果上面两步没有找到可以使用的handlerMapping,那么就采用默认的handlerMapping
//默认的HandlerMapping都定义在了DispatcherServlet.properties中,大致定义了如下两个
//org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
以BeanNameUrlHandlerMapping为例,先看BeanNameUrlHandlerMapping是如何使用的?
先创建一个HelloWorldBeanNameHandler,实现AbstractController的handleRequestInternal方法。
public class HelloWorldBeanNameHandler extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
System.out.println("bean HelloWorldBeanNameHandler world");
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
return mv;
}
}
然后在springmvc配置文件中,配置这个类,类名的id为请求的路径,一个请求路径对应一个类
<bean id="/helloWorldBeanNameHandler" class="springmvc.helloworld.HelloWorldBeanNameHandler"></bean>
先来看看springmvc是如何加载这个bean,并让它作为请求处理类。
BeanNameUrlHandlerMapper的结构示意图
HandlerMapper只是一个接口,定义了查找Handler方法,所以从AbstractHandlerMapper开始,AbstractHandlerMapper继承WebApplicationObjectSupport方法,所以会先执行initApplicationContext方法,AbstractHandlerMapper只加载了全部的拦截器,AbstractHandlerMapper并没有获取请求的bean,也没有查找,而是由子类去实现,但是它实现了查找那些handler的方法,并且保存注册的请求路径和handler的映射关系。我们先看注册,后看查找。
protected void initApplicationContext() throws BeansException {
//模板方法,但目前没使用
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
//将所有的MapperInterceptors类型的Bean添加到mappedInterceptors属性中
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
protected void initInterceptors() {
//初始化Interceptor,将全部的拦截器加入adaptedInterceptors中
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
子类AbstractUrlHandlerMapping与Handler的注册无关,只是实现了查找的方法,继续看AbstractDetectingUrlHandlerMapping的initApplicationContext的方法,调用父类的initApplicationContext的方法,并且查找了ioc容器中,所有的bean,然后筛选出bean的名词和别名是/开头的
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
protected void detectHandlers() throws BeansException {
//ioc容器
ApplicationContext applicationContext = obtainApplicationContext();
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + applicationContext);
}
//获取ioc容器中所有的bean的名字
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
//遍历bean的名字,将名字匹配的放入父类中的
for (String beanName : beanNames) {
//获取路径集合,模板方法,由子类实现
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
//根据路径和bean的名字进行注册,即url --> bean作为map的映射,调用父类的方法
//因为一个类可以映射多个路径
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
主角BeanNameUrlHandlerMaper强势登场,因为父类AbstractUrlHandlerMapping采用了determineUrlsForHandler这个模板方法,由子类去实现,然后筛选出所有的以/开头的bean,然后调用父类AbstractUrlHandlerMapping的registerHandler方法,将路径和bean放入AbstractUrlHandlerMapping父类的HashMap中。
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
//所有的bean都要是/开头,才是springmvc的handler,不然就是普通的一个对象
if (beanName.startsWith("/")) {
urls.add(beanName);
}
//获取类的别名的是/开头的
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
AbstractUrlHandlerMapping中的registerHandler注册方法,分别处理/,/*以及普通的请求路径
//遍历url,将bean进行注册
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
//判断当前的handler是否已经存在,目的是为了创建单例的handler,所以controller中的类都是单例的
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
//判断此路径的mapper是否已经存在,存在则判断是否是同一个对象,是则报错,一个路径对应多个bean实例
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
//路径假设是/,则用rootHandler
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
//假设路径是/*,则用父类中默认的defaultHandler,
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
//假设路径是/hello这种,则将路径和handler传入handlerMap中
//路径为key,handler为value
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
到这里BeanNameUrlHandlerMapping的Handler的注册就完成了,总结下BeanNameUrlHandlerMapping其实就是把配置的bean中的名字或者别名以/开头的,将这些以及它的bean保存在一个HashMap中,这里采用了模板方法,让父类调用子类的方法,添加更加详细的内容,也结合了继承,继承父类的方法的同时,并且给父类增加额外的功能,给父类属性增加内容。