在上篇文章中,大致解析了Spring如何將請求路徑與處理方法進行映射,但映射相關的初始化對于我們來說還是一團迷霧
本篇文章就來探索下,請求路徑和處理方法的映射,是如何進行初始化的
基于上篇文章:Spring 源碼解析 – SpringWeb請求映射解析
本篇文章本來想早點寫完,但一直卡著,沒有找到想要的切入點,還好在周四左右定位到了相關的關鍵代碼,初步探索到相關初始化的代碼過程
importjava、接下來就展示這段時間的定位和解析過程,下面是自己這段時間探索歷程:
接下來詳細看下:
在類: DispatcherServlet.java 中,我們定位到 mappedHandler 獲取的關鍵代碼
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}
它就是遍歷了: handlerMappings ,于是去跟蹤了 handlerMappings 初始化過程
結果失敗而歸,沒有找到自己想要的東西,并沒有發現自定義類: HelloWorld 的相關東西
import java.lang?這塊有很多的代碼,里面也有很多的東西,但在這里就不展示出來了,感興趣的老哥可自行探索
探索 handlerMappings 無果,于是回到上面那段遍歷處理
經過調試 handlerMappings 基本是固定的,包含下面的類:
而匹配的成功的是: RequestMappingHandlerMapping ,其返回了我們想要的處理方法 HelloWorld
調試中很疑惑為啥初期要初始化這幾個了,并且再套了一層請求匹配,目前掌握的知識還不足于破解,只能后面再探索了
java bean轉map?于是開始梳理 RequestMappingHandlerMapping 的請求匹配,在下面的一段關鍵代碼匹配成功了:
# AbstractHandlerMethodMapping.java@Nullableprotected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<>();// 在這里拿到了匹配結果List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);if (directPathMatches != null) {addMatchingMappings(directPathMatches, matches, request);}if (matches.isEmpty()) {addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);}.......}
在上面的代碼中就匹配成功了,其中匹配的方法很簡單粗暴:
# AbstractHandlerMethodMapping.java -- MappingRegistry@Nullablepublic List<T> getMappingsByDirectPath(String urlPath) {return this.pathLookup.get(urlPath);}
于是關鍵點到了 this.mappingRegistry 的初始化,找到初始化的代碼,打上斷點
期間以為是在類:AbstractHandlerMethodMapping 中進行的初始的,在下面的函數打上了斷點:
# AbstractHandlerMethodMapping.javapublic void setPatternParser(PathPatternParser patternParser) {Assert.state(this.mappingRegistry.getRegistrations().isEmpty(),"PathPatternParser must be set before the initialization of " +"request mappings through InitializingBean#afterPropertiesSet.");super.setPatternParser(patternParser);}public void registerMapping(T mapping, Object handler, Method method) {if (logger.isTraceEnabled()) {logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());}this.mappingRegistry.register(mapping, handler, method);}
但一直進不去,于是直接在其定義的內部類中: MappingRegistry 中進行尋找,并成功定位到想要的關鍵代碼
map轉bean對象。閱讀類: MappingRegistry 的相關代碼,發現下面的方法和可以,我們直接打上斷點,重啟程序:
發現了前面的: this.pathLookup 的相關添加操作
public void register(T mapping, Object handler, Method method) {this.readWriteLock.writeLock().lock();try {HandlerMethod handlerMethod = createHandlerMethod(handler, method);validateMethodMapping(handlerMethod, mapping);Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);for (String path : directPaths) {// 這段代碼是關鍵this.pathLookup.add(path, mapping);}String name = null;if (getNamingStrategy() != null) {name = getNamingStrategy().getName(handlerMethod, mapping);addMappingName(name, handlerMethod);}CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);if (corsConfig != null) {corsConfig.validateAllowCredentials();this.corsLookup.put(handlerMethod, corsConfig);}this.registry.put(mapping,new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));}finally {this.readWriteLock.writeLock().unlock();}}
應用重啟后,果然順利來到我們打上的斷點處,通過分析調用棧,我們確實找到了請求映射的關鍵代碼
我們將調用棧從下網上分析查看:
開始就是熟悉Spring啟動相關,這些代碼相信大家嘗試閱讀源碼的時候讀過很多遍了
Springboot?跟蹤發現在: DefaultListableBeanFactory.class 的 preInstantiateSingletons 方法中個,一大段嵌套循環,心想這段代碼目前能優化嗎?
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);}public static void main(String[] args) throws Exception {run(new Class[0], args);}public ConfigurableApplicationContext run(String... args) {......try {......this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 從下面這個進入this.refreshContext(context);this.afterRefresh(context, applicationArguments);......} catch (Throwable var10) {......this.handleRunFailure(context, var10, listeners);throw new IllegalStateException(var10);}......}public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {.......try {this.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);beanPostProcess.end();this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();// 從這里進入this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var10) {} finally {......}......}}
繼續跟蹤下面的,看到了屬性的CreateBean和afterPropertiesSet
# AbstractAutowireCapableBeanFactory.classprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {......try {// 這里初始化了 RequestMappingHandlerMappingbeanInstance = this.doCreateBean(beanName, mbdToUse, args);if (this.logger.isTraceEnabled()) {this.logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;} catch (ImplicitlyAppearedSingletonException | BeanCreationException var7) {throw var7;} catch (Throwable var8) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", var8);}}# AbstractAutowireCapableBeanFactory.classprotected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {boolean isInitializingBean = bean instanceof InitializingBean;if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {if (this.logger.isTraceEnabled()) {this.logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");}if (System.getSecurityManager() != null) {try {AccessController.doPrivileged(() -> {((InitializingBean)bean).afterPropertiesSet();return null;}, this.getAccessControlContext());} catch (PrivilegedActionException var6) {throw var6.getException();}} else {// 這里進入請求映射的相關操作((InitializingBean)bean).afterPropertiesSet();}}......}
繼續跟蹤下去,看看了循環遍歷Controllers相關的代碼(還有很多細節沒搞清,后面再繼續了,先梳理主線)
# AbstractHandlerMethodMapping.java@Overridepublic void afterPropertiesSet() {// 初始化請求映射initHandlerMethods();}protected void initHandlerMethods() {// 遍歷所有的自定義的Controllers,后面自己又定義了一個Controllersfor (String beanName : getCandidateBeanNames()) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {// 在這里看到了我們定義的HelloWorldprocessCandidateBean(beanName);}}handlerMethodsInitialized(getHandlerMethods());}protected String[] getCandidateBeanNames() {return (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :obtainApplicationContext().getBeanNamesForType(Object.class));}
繼續跟蹤下去,看到了下面的獲取類中具體請求路徑相關的代碼,并且到了具體的初始化請求映射的代碼
# AbstractHandlerMethodMapping.javaprotected 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);}}if (beanType != null && isHandler(beanType)) {// 得到Controller Bean后的入口detectHandlerMethods(beanName);}}protected void detectHandlerMethods(Object handler) {Class<?> handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass());if (handlerType != null) {// 處理得到所有的Controllers methodClass<?> userType = ClassUtils.getUserClass(handlerType);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);}});if (logger.isTraceEnabled()) {logger.trace(formatMappings(userType, methods));}else if (mappingsLogger.isDebugEnabled()) {mappingsLogger.debug(formatMappings(userType, methods));}methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);// 注冊registerHandlerMethod(handler, invocableMethod, mapping);});}}public void register(T mapping, Object handler, Method method) {this.readWriteLock.writeLock().lock();try {HandlerMethod handlerMethod = createHandlerMethod(handler, method);validateMethodMapping(handlerMethod, mapping);Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);for (String path : directPaths) {// 映射添加this.pathLookup.add(path, mapping);}}finally {this.readWriteLock.writeLock().unlock();}}
經過一段時間的探索的整理,我們終于得到了大致的請求路徑映射初始化的代碼
java map reduce、經過調試,我們還發現雖然 RequestMappingHandlerMapping 是一開始就初始化了,但加載到 handlerMappings 是第一次請求的時候才加載進去的
本篇雖然得到了大致的請求路徑初始化的代碼,但其中有很多細節是值得探索的,比如Bean中Method的處理
之前自己寫過一些DI和Web相關的Demo,停在了Servlet,卡在了請求映射初始化和匹配,這個給了我一些思路,后面詳細看看這塊代碼,完善下之前的Demo
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态