Introduction to the
In the previous article, I outlined how Spring maps request paths to processing methods, but map-related initialization is still a mystery to us
This article will explore how the mapping of request paths and processing methods is initialized
An overview of
Based on the previous article: Spring source Parsing – SpringWeb request mapping parsing
This article was intended to be finished earlier, but it has been stuck and failed to find the desired entry point. Fortunately, I located the relevant key code around Thursday and initially explored the relevant initialization code process
Next, I will show the positioning and parsing process during this period. The following is my exploration process during this period:
- Trying to locate the initialization of handlerMappings, but not locating the request URL and handler related initializations
- Go back and look at handlerMappings and see what it has. There is no custom HelloWorld in this Map
- Aware of key RequestMappingHandlerMapping, tracking send only in the type matching success
- Review last request mapping parsing, the RequestMappingHandlerMapping meticulous initialization code
- Key code for successfully finding the relevant path and handling method initialization
Let’s take a closer look:
The source code parsing
Initial exploration initialization: misguided
In class: dispatcherServlet.java, we locate the key code that mappedHandler gets
@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) {
returnhandler; }}}return null;
}
Copy the code
It is traversal: handlerMappings, thus tracing the handlerMappings initialization process
I failed to find what I was looking for and found nothing related to the custom class HelloWorld
This piece has a lot of code, there are a lot of things in it, but I won’t show it here, interested elder brother can explore
Review request map lookup matches: a rude awakening
Exploring handlerMappings is fruitless, so go back to the above iterating process
HandlerMappings is basically fixed after debugging and contains the following classes:
- this.handlerMappings
- RequestMappingHandlerMapping
- BeanNameUrlHandlerMapping
- RouterFunctionMapping
- SimpleUrlHandlerMapping
- WelcomePageHandlerMapping
And matching of success is: RequestMappingHandlerMapping, its return to the HelloWorld we want processing method
Debugging is very confused why the initial initialization of these several, and then set a layer of request matching, the current knowledge is not enough to crack, can only be explored later
Then began to comb the RequestMappingHandlerMapping request match, a key code match below:
# AbstractHandlerMethodMapping.java
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// Get the match result here
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if(directPathMatches ! =null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request); }... }Copy the code
This is done in the code above, where the method is simple and crude:
# AbstractHandlerMethodMapping.java -- MappingRegistry
@Nullable
public List<T> getMappingsByDirectPath(String urlPath) {
return this.pathLookup.get(urlPath);
}
Copy the code
The key point then is to initialize this.mappingRegistry, find the initialized code, and set the breakpoint
Thought it was in the class: during AbstractHandlerMethodMapping in initial, put a breakpoint in the following functions:
# AbstractHandlerMethodMapping.java
public 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);
}
Copy the code
But they couldn’t get in, so they looked directly in the inner class they defined: MappingRegistry, and successfully located the key code they wanted
Request mapping key code location: a silver lining
MappingRegistry: MappingRegistry: MappingRegistry: MappingRegistry
Found the previous: this.pathLookup related add operation
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 code is the key
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,
newMappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig ! =null));
}
finally {
this.readWriteLock.writeLock().unlock(); }}Copy the code
After the application was restarted, we did get to the breakpoint we had set, and by analyzing the call stack, we did find the key code for the request mapping
We’ll look at the call stack from the bottom line:
Application startup correlation
Start by familiarizing yourself with Spring startup related code, which I believe you’ve read many times when trying to read the source code
Tracking found in: DefaultListableBeanFactory preInstantiateSingletons method in a class, a large section of the nested loop, and wish this code can now optimization?
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);
// Enter from the bottom
this.refreshContext(context);
this.afterRefresh(context, applicationArguments); . }catch (Throwable var10) {
......
this.handleRunFailure(context, var10, listeners);
throw newIllegalStateException(var10); }... }public void refresh(a) 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();
// Enter from here
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
} finally{... }... }}Copy the code
RequestMappingHandlerMapping related initialization
Continuing below, you see the CreateBean and afterPropertiesSet properties
# AbstractAutowireCapableBeanFactory.class
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {...try {
/ / initialize the RequestMappingHandlerMapping here
beanInstance = 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.class
protected 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) {
throwvar6.getException(); }}else {
// Enter the operations related to the request mapping((InitializingBean)bean).afterPropertiesSet(); }}... }Copy the code
Request mapping initialization
Follow along and see the Controllers looped through the code.
# AbstractHandlerMethodMapping.java
@Override
public void afterPropertiesSet(a) {
// Initializes the request mapping
initHandlerMethods();
}
protected void initHandlerMethods(a) {
// Go through all the custom Controllers and define a new one
for (String beanName : getCandidateBeanNames()) {
if(! beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {// Here you see the HelloWorld we defined
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
protected String[] getCandidateBeanNames() {
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}
Copy the code
As you follow along, you see the code for the specific request path in the fetch class below, and the code for initializing the specific request map
# AbstractHandlerMethodMapping.java
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); }}if(beanType ! =null && isHandler(beanType)) {
// Get the Controller Bean entrydetectHandlerMethods(beanName); }}protected void detectHandlerMethods(Object handler) { Class<? > handlerType = (handlerinstanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if(handlerType ! =null) {
// Handle all 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);
/ / registerregisterHandlerMethod(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) {
// Add mapping
this.pathLookup.add(path, mapping); }}finally {
this.readWriteLock.writeLock().unlock(); }}Copy the code
conclusion
After a period of exploration, we finally get the rough request path map initialization code
- 1. The application starts up, the initialization: RequestMappingHandlerMapping
- 2. Request path in the RequestMappingHandlerMapping initialization
After debugging, we also found that although the RequestMappingHandlerMapping is initialized from the start, but loaded into handlerMappings is the first time I request to load in
This article gives you some rough code for requesting path initialization, but there are many details worth exploring, such as the handling of methods in beans
I have written some DI and Web related demos before, which stopped at servlets and got stuck in request mapping initialization and matching. This gave me some ideas, and I will look at this code in detail later to improve the previous Demo
Refer to the link
- Spring source code parsing series