preface
You’ve already seen the initStrategies() method in DispatchServlet that initializes the HandlerMapping component, so let’s start there!! RequestMappingHandlerMapping, for example.
Step 1: Spring creates the Bean instance of HandlerMapping
For a brief overview, during Spring’s Bean instance creation process, if the InitializingBean interface is implemented before the final Bean, the afterPropertiesSet() method is called.
/ / RequestMappingHandlerMapping overrides the superclass afterPropertiesSet method. Before completing the Bean,
// afterPropertiesSet() is called
@Override
public void afterPropertiesSet(a) {
// Call the afterPropertiesSet() method of the parent class
① super.afterPropertiesSet();
}
//
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
public void afterPropertiesSet(a) {(2) initHandlerMethods (); }protected void initHandlerMethods(a) {
for (String beanName : getCandidateBeanNames()) {
if(! beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {// Handle the bean in the container, how to handle ????(3) processCandidateBean (beanName); } } handlerMethodsInitialized(getHandlerMethods()); }// Handle the specific logic of the bean in the container
protected void processCandidateBean(String beanName) { Class<? > beanType =null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
// isHandler() : checks whether the bean has @controller or @requestMapping annotations
if(beanType ! =null && isHandler(beanType)) {
// Monitor the annotated bean(4) detectHandlerMethods (beanName); }}//
protected void detectHandlerMethods(Object handler) { Class<? > handlerType = (handlerinstanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if(handlerType ! =null) { Class<? > userType = ClassUtils.getUserClass(handlerType);/** Get all the annotations in the current Controller@RequestMappingThe Method table Map < Method, RequestMappingInfo >. Eg: the key = com. Shang. Controller. HelloController. Hello () value = {RequestMappingInfo} * /
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try{5.return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
}
});
//
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// Add to MappingRegistry6 registerHandlerMethod (handler, invocableMethod, mapping); }); }}}Copy the code
The purpose of the first step is to get the Controller class annotated with @Controller or @requestMapping and then parse all the methods in the Controller to extract the @requestMapping annotated methods. Get a Map < method, requestMappingInfo > collection; Finally, add the Map collection traversal toMappingRegistry, the MappingRegistry is the final answer we need.
Step 2: Get all Bean instances that implement the HandlerMapping interface
private List<HandlerMapping> handlerMappings; // Store all HandlerMapping
private void initHandlerMappings(ApplicationContext context) {
DetectAllHandlerMappings The default value is true, which can be set manually by programmers
if (this.detectAllHandlerMappings) {
// Find all implementation classes that implement the HandlerMapping interface in all containers (including parent containers).
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true.false);
if(! matchingBeans.isEmpty()) {// Assign sort
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings); }}else {
try {
// Get the beanName="handlerMapping" Bean instance from the container and assign it to handlerMappings
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm); }}/ / if the developer has no configuration HandlerMapping, then from the DispatcherServlet. The properties file
// Load the default HandlerMapping.
if (this.handlerMappings == null) {
// There are many useful utility classes in this method, you can take a look
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); }}Copy the code
End goal: Get the implementation class of HandlerMapping in the container (including all parent containers) and add it toList<HandlerMapping>
For use.
Step 3: Use the Bean instance of HandlerMapping
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;
}
// AbstractHandlerMapping.java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// Get the most appropriate HandlerMethod according to request.
Object handler = getHandlerInternal(request);
// Obtain all matched interceptors according to the request and form a HandlerExecutionChain with the Handler Method
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
return executionChain;
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// New HandlerExecutionChain object, usually a native handler
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if(mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); }}else{ chain.addInterceptor(interceptor); }}return chain;
}
Copy the code
One purpose: to get the most appropriate HandlerMethod.
@RequestMapping(value = "/hello", method = {RequestMethod.POST})
public String hello3(a) {
return "hello";
}
@RequestMapping(value = "/hello", method = {RequestMethod.GET})
public String hello4(a) {
return "hello";
}
Copy the code
The size of ArrayList is 2 in pathLookup and nameLookup. What happens when the browser requests /hello? The best fit is determined by RequestMappingInfo. For example, compare value first and then method… Of course, the RequestMappingInfo parameters are more than just these two.
Purpose two: Get the HandlerExecutionChain
Encapsulate the Handler Method and HandlerInterceptor as HandlerExecutionChain for use by DispatchServlet.