springmvc learning an initialization source code

In the past two days, I have studied the source code of springmvc, mainly learning the process of starting and calling. It is mainly divided into the following two parts to record notes:
1. Starting process
2. Calling process
springmvc source code, let’s briefly talk about it.

1. Initialize the handlerMapping object and initialize the handlerAdapter object; when initializing the handlerMapping object, all beans will be parsed, and the controller and the corresponding URL will be stored in the corresponding map collection.
2. When called, it will be called to org.springframework .web.servlet.DispatcherServlet#doDispatch method
3. GetHandler() gets the handlerMapping that processes the current request, which is to find the corresponding method in the map according to the URL in the request. 4. GetAdapter
() gets a suitable handlerAdapter object. So it is appropriate because different controller methods have different processing logic.
5. Execution target method.
There is a detail point here: one is to complete the method call by reflection; the other is to implement the method in the class by calling the interface To complete the call
6, make a judgment: do you need to jump to the view, or write the data directly to the browser through the stream; that is, the difference between @ResponseBody and ModelAndView

springmvc application

The way to implement the controller

There are three ways to declare a controller

  1. Add the @Controller annotation to the class, and specify the url through the @RequestMapping annotation in the method
  2. To implement the Controller interface, in this way, you need to add @Component("/mapping address") to the class name
  3. Implement the HttpRequestHandler interface, add @Component("/mapping address") to the class

The latter two principles are basically the same. Spring has two default handlerMappings: RequestMappingHandlerMapping and BeanNameUrlHandlerMapping; for @Controller annotated controllers, the former is handled by the former, and the controller interface or httpRequestHandler interface is implemented by the latter. The
handlerAdapter is similar

The latter two can be considered as one type, and both use beanName as the requested url; when the method is actually called, the two types of methods are different:
@Controller This method is through reflection To complete the call, the
latter two are to complete the method call by calling the method in the interface implementation class

Insert picture description here
The above picture is a screenshot taken casually on the Internet, which is roughly the process of springmvc

Start the process

For the start-up process, our blog, just need to pay attention

RequestMappingHandlerMapping
BeanNameUrlHandlerMapping

The initialization of these two beans is enough, because the initialization of these two beans is the focus of our blog: how url and method correspond

RequestMappingHandlerMapping

Insert picture description here
We can see that this class indirectly implements the InitializingBean interface, so when the class is initialized, it will be called

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
	org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
		org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods

This is the call chain of the initialization method. Only the call chain is posted here. The code in the middle is not important, it is just a few lines of code; the key code is the last method

/**
 * 在初始化时,会调到这里,然后会获取到beanDefinitionMap中的bean,判断当前bean是否是@Controller或者@RequestMapping修饰的类
 * 如果是,就调用detectHandlerMethods方法,解析方法的@RequestMapping注解对应的path,然后存入到map集合中
 */
protected void initHandlerMethods() {
    
    
	if (logger.isDebugEnabled()) {
    
    
		logger.debug("Looking for request mappings in application context: " + getApplicationContext());
	}
	/**
	 * 这里涉及到父子容器
	 * spring容器和springmvc容器
	 */
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));

	/**
	 * 我们姑且可以理解为:这里获取到spring容器中所有的对象
	 */
	for (String beanName : beanNames) {
    
    
		/**
		 * 如果是以"scopedTarget."开头,就跳过,不做处理
		 */
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
    
    
			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.isDebugEnabled()) {
    
    
					logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
				}
			}
			/**
			 * 判断当前bean是否是@Controller或者@RequestMapping修饰的bean
			 * 如果当前类是这两个注解修饰的,就在下面的方法中,会解析@RequestMapping对应的URL
			 */
			if (beanType != null && isHandler(beanType)) {
    
    
				detectHandlerMethods(beanName);
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

@Override
protected boolean isHandler(Class<?> beanType) {
    
    
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

This method is to process all beans modified by @Controller or @RequestMapping, and then obtain the method corresponding to the bean for analysis

/**
* @param handler
 * 在这里其实是根据bean,获取到bean中所有添加了@RequestMapping注解的method,然后把method和url进行映射,并把映射关系存到map中
 */
protected void detectHandlerMethods(Object handler) {
    
    
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
    
    
		//userType是当前的类名
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		/**
		 * 根据类名获取到所有的方法,这里的key是method,value是@RequestMapping对应的path
		 * key: public void com.springmvc.TestController.test()
		 * value: {[/test]}
		 */
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
    
    
					try {
    
    
						/**
						 * 根据method,获取到当前method上添加的@RequestMapping注解的path属性信息
						 */
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
    
    
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isDebugEnabled()) {
    
    
			logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
		}
		/**
		 * 遍历依次解析bean所有的method以及对应的url
		 */
		methods.forEach((method, mapping) -> {
    
    
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

This is the method of parsing the url corresponding to the method and the code stored in the map;
among them, getMappingForMethod(method, userType); is the code for parsing the @RequestMapping information of the method according to the method, which is not posted here, the logic inside is relatively simple
registerHandlerMethod (handler, invocableMethod, mapping); The method will traverse the method in turn, and then store the method and url into the map collection

BeanNameUrlHandlerMapping

For this class, this is done through the bean's post processor to complete the mapping of url and method. As
Insert picture description here
you can see, this class indirectly implements the ApplicationContextAware interface, so in

org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization

When the BeanNameUrlHandlerMapping bean is initialized, when the postProcessBeforeInitialization method of the post processor is called, it will be called to org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#determineUrlsForHandler

Just look at the debug here, and the jump logic in the middle will not be explained too much.

/**
	 * BeanNameUrlHandlerMapping该bean在初始化的时候,会调用到该方法,和该接口实现了ApplicationContextAware接口有关系
	 * @param beanName the name of the candidate bean
	 * @return
	 */
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
    
    
		List<String> urls = new ArrayList<>();
		/**
		 * 只处理以/开头的beanName
		 * 这个bean处理的是实现了Controller接口或者HttpRequestHandler接口的controller,这两种方式
		 * 都是需要在bean上添加@Component注解,并制定beanName,beanName就是url,所以,beanName要以/开头
		 */
		if (beanName.startsWith("/")) {
    
    
			urls.add(beanName);
		}
		//处理别名
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
    
    
			if (alias.startsWith("/")) {
    
    
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}

This is the core code, and the others will not be posted. It is roughly the same logic. Store the beanName and method in the parsed urls into a map collection.

transfer

When the call controller, entrance directly from org.springframework.web.servlet.DispatcherServlet # doDispatch we
began to read

/**
* 找到对应的handlerMapping,并将interceptor封装到HandlerExecutionChain
 * 如果handlerMapping为null,就表示没有找到对应的映射器,返回404 notFound
 */
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
    
    
	noHandlerFound(processedRequest, response);
	return;
}

// Determine handler adapter for the current request.
//获取到处理请求的处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
    
    
	long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
	if (logger.isDebugEnabled()) {
    
    
		logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
	}
	// 这里应该也是和缓存有关系
	if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    
    
		return;
	}
}

//在调用目标方法之前调用拦截器,拦截器预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    
    
	return;
}

// Actually invoke the handler.对modelAndView的处理
/**
 * 实际的处理器处理请求,返回结果视图对象
 * 如果是@Controller注解的这种方式,是通过反射实现的
 * 如果是实现了Controller接口或者实现了HttpRequestHandler接口这种方式,是通过调用实现类的方法来完成的
 */
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

I only posted a part of the code.
Here is the calling process, let ’s put it in the next blog; there is too much content and it’s easy to mess up

Guess you like

Origin blog.csdn.net/CPLASF_/article/details/108962732