要了解DispatcherServlet,就必須先了解幾個(gè)特殊Bean,否則在理解源碼的過(guò)程中會(huì)很吃力。
[圖片上傳失敗...(image-11609-1581567742949)]
上圖出自Spring的官方文檔。從圖中可以看出HandlerMapping占的篇幅很大慧域,也是整個(gè)DispatcherServlet映射尋址的關(guān)鍵。我們今天就來(lái)分析在初始化過(guò)程中是如何初始化HandlerMapping的浪读。
在正式開(kāi)始前昔榴,我們先來(lái)了解一個(gè)SpringMVC包中的配置文件
這個(gè)配置文件是Spring初始化時(shí)的默認(rèn)配置敦冬,如果用戶(hù)沒(méi)有進(jìn)行自定義配置键思,那么會(huì)根據(jù)上面這個(gè)配置中的地址去獲取默認(rèn)處理類(lèi)。
上篇文章中我們已經(jīng)介紹了DispatcherServlet#onRefresh時(shí)每個(gè)方法的作用纲缓,現(xiàn)在我們直入主題痘拆,來(lái)分析分析initHandlerMappings這個(gè)方法都做了什么仰禽。
/**
* 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){
// 獲取默認(rèn)HandlerMapping策略
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 獲取需要的策略名稱(chēng)
String key = strategyInterface.getName();
// 從默認(rèn)策略中得到HandlerMapping的策略,在上面的圖中可以看到HandlerMapping有兩個(gè)默認(rèn)策略纺蛆,BeanNameUrlHandlerMapping和RequestMappingHandlerMapping吐葵。
// 此處的defaultStrategies在上篇文章中提到過(guò),在靜態(tài)代碼塊中做了初始化操作桥氏。
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
// 依次處理每個(gè)策略
for (String className : classNames) {
try {
// 通過(guò)反射將配置的類(lèi)加載到j(luò)vm中
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 初始化策略對(duì)象温峭,此時(shí)會(huì)通過(guò)beanFactory來(lái)createBean,這些方法在前面分析Spring容器啟動(dòng)時(shí)已經(jīng)講過(guò)了字支。重點(diǎn)在初始化結(jié)束后調(diào)用initializeBean執(zhí)行Aware回調(diào)
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<>();
}
}
我們已經(jīng)知道HandlerMapping默認(rèn)配置了兩個(gè)實(shí)現(xiàn)(實(shí)際還有其他實(shí)現(xiàn))凤藏,對(duì)于BeanNameUrlHandlerMapping來(lái)說(shuō)相對(duì)簡(jiǎn)單,IOC容器生成對(duì)象之后基本也就結(jié)束了初始化操作堕伪,我們來(lái)重點(diǎn)關(guān)注一下RequestMappingHandlerMapping揖庄。首先我們來(lái)看看RequestMappingHandlerMapping的類(lèi)關(guān)系圖
通過(guò)關(guān)系圖我們可以看到,RequestMappingHandlerMapping的父類(lèi)AbstractHandlerMethodMapping實(shí)現(xiàn)了InitializingBean接口欠雌,這意味著IOC容器在創(chuàng)建結(jié)束后會(huì)執(zhí)行initializeBean方法蹄梢,調(diào)用afterPropertiesSet方法。同時(shí)RequestMappingHandlerMapping重寫(xiě)了afterPropertiesSet富俄,我們來(lái)看看這個(gè)方法里面發(fā)生了什么
@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());
// 前面只是創(chuàng)建了一個(gè)配置對(duì)象禁炒,初始化了一些配置項(xiàng)
// 調(diào)用父類(lèi)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會(huì)獨(dú)去所有被容器管理的對(duì)象
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方法會(huì)判斷當(dāng)前類(lèi)是否存在@Controller或@RequestMapping注解而咆,從而過(guò)濾非Controller對(duì)象
if (beanType != null && isHandler(beanType)) {
// 從此處開(kāi)始真正處理每個(gè)Controller
detectHandlerMethods(beanName);
}
}
請(qǐng)注意,此時(shí)才是HandlerMapping初始化的核心部分齐苛。在AbstractHandlerMethodMapping#detectHandlerMethods方法中會(huì)對(duì)每個(gè)Controller中的所有方法進(jìn)行判斷翘盖,得到帶有@RequestMapping注解的方法并將其封裝成RequestMappingInfo桂塞。最后會(huì)將Method封裝成HandlerMethod凹蜂,同時(shí)依次在mappingLookup、urlLookup阁危、nameLookup玛痊、corsLookup和registry中注冊(cè)。下面我們來(lái)分析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并依次注冊(cè)
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;
}
最終會(huì)調(diào)用到RequestMappingHandlerMapping#getMappingForMethod方法趴乡,在這個(gè)方法中會(huì)判斷是否存在@RequestMapping注解对省,不存在會(huì)跳過(guò)。同時(shí)會(huì)將方法上的@RequestMapping和類(lèi)上的@RequestMapping進(jìn)行合并(如果存在)晾捏,合并操作會(huì)將類(lèi)上的url與方法中配置的url進(jìn)行拼接蒿涎。最終返回合并后的RequestMappingInfo。
當(dāng)類(lèi)中所有的方法全部處理完之后會(huì)遍歷得到的Map惦辛,逐一將Method封裝成HandlerMethod并調(diào)用registry依次注冊(cè)到mappingLookup劳秋、urlLookup、nameLookup胖齐、corsLookup和registry中去玻淑,關(guān)鍵代碼如下
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為值注冊(cè)在mappingLookup中
this.mappingLookup.put(mapping, handlerMethod);
// 從mapping中獲取配置的patterns解析配置的Url呀伙,并以u(píng)rl為key补履,mapping為value注冊(cè)到urlLookup中
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
// 取類(lèi)名中的大寫(xiě)字母用#和方法名連接在一起作為key,handlerMethod集合作為value注冊(cè)到nameLookup中
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
// 初始化跨域訪(fǎng)問(wèn)配置剿另,判斷類(lèi)或者方法上是否存在CrossOrigin注解干像,如果沒(méi)有則忽略
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 注冊(cè)到registry中
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
至此整個(gè)HandlerMapping就算初始化結(jié)束了,下篇文章我們繼續(xù)分析初始化HandlerAdapter的過(guò)程驰弄,這兩個(gè)過(guò)程在后續(xù)的Spring MVC接收請(qǐng)求麻汰,封裝請(qǐng)求參數(shù),調(diào)用Controller中的方法中起到了至關(guān)重要的作用戚篙。我們會(huì)著重分析這兩塊的初始化過(guò)程五鲫,之后再來(lái)分析接收到請(qǐng)求后如何封裝參數(shù),分發(fā)請(qǐng)求岔擂。