HandlerMapping
url映射,也就是访问的url,对应的处理器。
在DispatcherServlet
类中的initHandlerMappings()
初始化HandlerMapping
。
SpringMVC默认有两个HandlerMapping
BeanNameUrlHandlerMapping
:只要beanName或别名是以/
开头就会添加到该映射处理器中。RequestMappingHandlerMapping
:@RequestMapping映射处理,最为常用的
。
先看看代码块,如果需要自定义,尽量将默认的也注入到容器中
/** 检测所有HandlerMapping映射 还是只期望 “handlerMapping”bean?. */
private boolean detectAllHandlerMappings = true;
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
/**
* 默认是 true
* 如果 {@link #detectAllHandlerMappings} 设置为true,则从容器中查找所有 HandlerMapping类型的Bean
*/
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
/* 给当前 handlerMappings 赋值 */
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
/* 如果 {@link #detectAllHandlerMappings} 设置为false 则从容器根据BeanName = handlerMapping 获取 */
HandlerMapping hm = context.getBean("handlerMapping", HandlerMapping.class);
/* 给当前 handlerMappings 赋值 */
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
/**
* 如果没有自定义 则使用默认策略
* {@link BeanNameUrlHandlerMapping}
* {@link RequestMappingHandlerMapping}
*/
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
}
getDefaultStrategies(context, Class)
根据Class类型创建默认的实例,依靠Web容器的createBean
创建实例
defaultStrategies 这里有一个静态变量,保存着SpringMVC的默认配置,查看默认配置文件
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
/**
* 重点关注下这里 SpringMVC 的默认策略
*/
String value = defaultStrategies.getProperty(key);
if (value != null) {
/**
* 以 逗号 分割
*/
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
/**
* 这里是用 Web容器去创建实例
* 默认是多实例的(scope = provided)
*/
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
}
catch (LinkageError err) {
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}
createDefaultStrategy(context, clazz)
调用 Web容器的createBean
创建Bean,直接从这里开始分析。
1:BeanNameUrlHandlerMapping
只要BeanName或Bean的别名是以/
开头,都会添加到该映射器中。
createDefaultStrategy(context, BeanNameUrlHandlerMapping.class)
它在ApplicationContextAware
的回调方法setApplicationContext
中,进行处理 url映射。
public abstract class ApplicationObjectSupport implements ApplicationContextAware {
@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
/**
* 忽略其他代码...
*/
/**
* 执行 ApplicationContext(context) 子类重写该方法了
*/
initApplicationContext(context);
}
/**
* 由子类 super.initApplicationContext(context); 执行到此
*
* 执行 initApplicationContext(); 无参的方法
*/
protected void initApplicationContext(ApplicationContext context) throws BeansException {
initApplicationContext();
}
}
1.0.1:initApplicationContext()
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
@Override
public void initApplicationContext() throws ApplicationContextException {
/**
* 初始化拦截器
*/
super.initApplicationContext();
/**
* 注册容器所有的Bean的 url映射处理
*/
detectHandlers();
}
}
1.0.2:super.initApplicationContext()
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered, BeanNameAware {
@Override
protected void initApplicationContext() throws BeansException {
/**
* 注册格外的拦截器
*/
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
/* 由子类拓展 */
protected void extendInterceptors(List<Object> interceptors) {
}
/* 从容器中获取所有的 MappedInterceptor类型的拦截器 */
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
/* 初始化指定的拦截器 */
protected void initInterceptors() {
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));
}
}
}
}
1.0.3:detectHandlers()
注册容器中所有的 url映射处理器
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
/**
* 注册容器中所有的 url映射
*/
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
/* 从容器中获取所有的 BeanName */
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)) {
// 找到的URL路径:让我们把它看作一个处理程序。
registerHandler(urls, beanName);
}
}
}
}
1.0.4:determineUrlsForHandler(beanName)
该BeanName 是否是 url映射处理
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
/**
* 检查给定bean的名称和别名,以“/”开头。
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
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);
}
}
2:RequestMappingHandlerMapping
@RequestMapping
url映射处理
在InitializingBean
Bean初始化方法afterPropertiesSet
中,添加容器中所有的@RequestMapping
2.0.0:AbstractHandlerMethodMapping.afterPropertiesSet()
Bean的初始化方法
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* 检查所有的 Handler 方法 可以url映射的方法
* @see #initHandlerMethods
*/
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
/**
* 遍历 容器中所有的 BeanName
*/
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
/* 排除 {scopedTarget.} 开头的BeanName */
if (!beanName.startsWith("scopedTarget.")) {
/**
* 处理该Bean是否满足条件
* 重点关注这里
*/
processCandidateBean(beanName);
}
}
/* */
handlerMethodsInitialized(getHandlerMethods());
}
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
// 总计包括检测到的映射+通过注册映射的显式注册
int total = handlerMethods.size();
if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
logger.debug(total + " mappings in " + formatMappingName());
}
}
}
2.0.1:processCandidateBean(beanName)
处理当前BeanName是否满足 url映射,如果满足,则添加到 url映射集合中
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
/* 获取 Bean的类型 */
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
}
/**
* isHandler 检查是否有 @Controller 或 @RequestMapping 注解
*/
if (beanType != null && isHandler(beanType)) {
/* 检查该 Bean */
detectHandlerMethods(beanName);
}
}
protected void detectHandlerMethods(Object handler) {
/* 如果 handler是BeanName 获取 Bean的类型 */
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
/**
* 获取Bean中 所有标注了 @RequestMapping 注解的 方法
*
* T = RequestMappingInfo 包装了 RequestMapping信息
*/
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
}
});
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
/**
* 添加到 url映射的集合中
*/
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
}
总结:
HandlerMapping是处理url映射的,所有的url映射需要在HandlerMapping的映射集合中。如果在HandlerMapping中,找不到url,则404.