要了解DispatcherServlet,就必须先了解几个特殊Bean,否则在理解源码的过程中会很吃力。
[图片上传失败...(image-11609-1581567742949)]
上图出自Spring的官方文档。从图中可以看出HandlerMapping占的篇幅很大,也是整个DispatcherServlet映射寻址的关键。我们今天就来分析在初始化过程中是如何初始化HandlerMapping的。
在正式开始前,我们先来了解一个SpringMVC包中的配置文件
DispatcherServletProcess.png
这个配置文件是Spring初始化时的默认配置,如果用户没有进行自定义配置,那么会根据上面这个配置中的地址去获取默认处理类。
上篇文章中我们已经介绍了DispatcherServlet#onRefresh时每个方法的作用,现在我们直入主题,来分析分析initHandlerMappings这个方法都做了什么。
/**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null){
// 获取默认HandlerMapping策略
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 获取需要的策略名称
String key = strategyInterface.getName();
// 从默认策略中得到HandlerMapping的策略,在上面的图中可以看到HandlerMapping有两个默认策略,BeanNameUrlHandlerMapping和RequestMappingHandlerMapping。
// 此处的defaultStrategies在上篇文章中提到过,在静态代码块中做了初始化操作。
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 {
// 通过反射将配置的类加载到jvm中
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 初始化策略对象,此时会通过beanFactory来createBean,这些方法在前面分析Spring容器启动时已经讲过了。重点在初始化结束后调用initializeBean执行Aware回调
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}
我们已经知道HandlerMapping默认配置了两个实现(实际还有其他实现),对于BeanNameUrlHandlerMapping来说相对简单,IOC容器生成对象之后基本也就结束了初始化操作,我们来重点关注一下RequestMappingHandlerMapping。首先我们来看看RequestMappingHandlerMapping的类关系图
RequestMappingHandlerMapping.png
通过关系图我们可以看到,RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean接口,这意味着IOC容器在创建结束后会执行initializeBean方法,调用afterPropertiesSet方法。同时RequestMappingHandlerMapping重写了afterPropertiesSet,我们来看看这个方法里面发生了什么
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 前面只是创建了一个配置对象,初始化了一些配置项
// 调用父类afterPropertiesSet方法
super.afterPropertiesSet();
}
/**
* Detects handler methods at initialization.
* @see #initHandlerMethods
*/
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
// 这里的getCandidateBeanNames会独去所有被容器管理的对象
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// isHandler方法会判断当前类是否存在@Controller或@RequestMapping注解,从而过滤非Controller对象
if (beanType != null && isHandler(beanType)) {
// 从此处开始真正处理每个Controller
detectHandlerMethods(beanName);
}
}
请注意,此时才是HandlerMapping初始化的核心部分。在AbstractHandlerMethodMapping#detectHandlerMethods方法中会对每个Controller中的所有方法进行判断,得到带有@RequestMapping注解的方法并将其封装成RequestMappingInfo。最后会将Method封装成HandlerMethod,同时依次在mappingLookup、urlLookup、nameLookup、corsLookup和registry中注册。下面我们来分析detectHandlerMethods方法。
/**
* Look for handler methods in the specified handler bean.
* @param handler either a bean name or an actual handler instance
* @see #getMappingForMethod
*/
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 查找@RequestMapping方法并封装成RequestMappingInfo
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
// 将Method封装成HandlerMethod并依次注册
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
// 判断方法是否是桥接方法(Bridge)、组合方法(Synthetic)或者是Object中的方法,如果不是则封装RequestMappingInfo
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
最终会调用到RequestMappingHandlerMapping#getMappingForMethod方法,在这个方法中会判断是否存在@RequestMapping注解,不存在会跳过。同时会将方法上的@RequestMapping和类上的@RequestMapping进行合并(如果存在),合并操作会将类上的url与方法中配置的url进行拼接。最终返回合并后的RequestMappingInfo。
当类中所有的方法全部处理完之后会遍历得到的Map,逐一将Method封装成HandlerMethod并调用registry依次注册到mappingLookup、urlLookup、nameLookup、corsLookup和registry中去,关键代码如下
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
// 封装HanderMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
// 以RequestMappingHandlerMapping为key,HandlerMethod为值注册在mappingLookup中
this.mappingLookup.put(mapping, handlerMethod);
// 从mapping中获取配置的patterns解析配置的Url,并以url为key,mapping为value注册到urlLookup中
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
// 取类名中的大写字母用#和方法名连接在一起作为key,handlerMethod集合作为value注册到nameLookup中
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
// 初始化跨域访问配置,判断类或者方法上是否存在CrossOrigin注解,如果没有则忽略
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 注册到registry中
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
至此整个HandlerMapping就算初始化结束了,下篇文章我们继续分析初始化HandlerAdapter的过程,这两个过程在后续的Spring MVC接收请求,封装请求参数,调用Controller中的方法中起到了至关重要的作用。我们会着重分析这两块的初始化过程,之后再来分析接收到请求后如何封装参数,分发请求。
网友评论