spring boot DispatcherServlet(一)初始化和注册

本文详细探讨了Spring Boot中DispatcherServlet的注册过程,从getSelfInitializer、prepareWebApplicationContext、registerApplicationScope到DispatcherServletRegistrationBean。接着,文章深入到DispatcherServlet的初始化,包括Servlet、GenericServlet、HttpServlet、FrameworkServlet的init方法,以及DispatcherServlet的HandlerMapping和MappingRegistry的注册过程,特别是对@Controller和@RequestMapping注解的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DispatcherServlet

DispatcherServlet的注册

在容器上下文ServletWebServerApplicationContext执行完容器初始化之后,会调用createWebServer来创建web server,内置的tomcat容器就是在这里进创建的

private void createWebServer() {
	WebServer webServer = this.webServer;
	// 获取ServletContext
	ServletContext servletContext = getServletContext();
	if (webServer == null && servletContext == null) {
		ServletWebServerFactory factory = getWebServerFactory();
		// 获取webserver
		this.webServer = factory.getWebServer(getSelfInitializer());
		getBeanFactory().registerSingleton("webServerGracefulShutdown",
				new WebServerGracefulShutdownLifecycle(this.webServer));
		getBeanFactory().registerSingleton("webServerStartStop",
				new WebServerStartStopLifecycle(this, this.webServer));
	}
	else if (servletContext != null) {
		try {
			getSelfInitializer().onStartup(servletContext);
		}
		catch (ServletException ex) {
			throw new ApplicationContextException("Cannot initialize servlet context", ex);
		}
	}
	initPropertySources();
}

在调用getWebServer的时候会调用getSelfInitializer来传入ServletContextInitializer
接下来看下getWebServer

public WebServer getWebServer(ServletContextInitializer... initializers) {
	if (this.disableMBeanRegistry) {
		Registry.disableRegistry();
	}
	// 下面是对Tomcat的一些设置
	Tomcat tomcat = new Tomcat();
	File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
	tomcat.setBaseDir(baseDir.getAbsolutePath());
	Connector connector = new Connector(this.protocol);
	connector.setThrowOnFailure(true);
	tomcat.getService().addConnector(connector);
	customizeConnector(connector);
	tomcat.setConnector(connector);
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
	for (Connector additionalConnector : this.additionalTomcatConnectors) {
		tomcat.getService().addConnector(additionalConnector);
	}
	// 准备context
	prepareContext(tomcat.getHost(), initializers);
	return getTomcatWebServer(tomcat);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
	File documentRoot = getValidDocumentRoot();
	// 继承了tomcat中的StandardContext,一个context代表一个应用
	// 下面是对应用的一些设置
	TomcatEmbeddedContext context = new TomcatEmbeddedContext();
	if (documentRoot != null) {
		context.setResources(new LoaderHidingResourceRoot(context));
	}
	context.setName(getContextPath());
	context.setDisplayName(getDisplayName());
	context.setPath(getContextPath());
	File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
	context.setDocBase(docBase.getAbsolutePath());
	context.addLifecycleListener(new FixContextListener());
	context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
			: ClassUtils.getDefaultClassLoader());
	resetDefaultLocaleMapping(context);
	addLocaleMappings(context);
	try {
		context.setCreateUploadTargets(true);
	}
	catch (NoSuchMethodError ex) {
		// Tomcat is < 8.5.39. Continue.
	}
	configureTldPatterns(context);
	WebappLoader loader = new WebappLoader();
	loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
	loader.setDelegate(true);
	context.setLoader(loader);
	if (isRegisterDefaultServlet()) {
		addDefaultServlet(context);
	}
	if (shouldRegisterJspServlet()) {
		addJspServlet(context);
		addJasperInitializer(context);
	}
	context.addLifecycleListener(new StaticResourceConfigurer(context));
	ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
	host.addChild(context);
	// 配置上下文
	configureContext(context, initializersToUse);
	postProcessContext(context);
}

在接下来调用configureContext的时候,会将所有ServletContextInitializer传给TomcatStarter,TomcatStarter又会传给TomcatEmbeddedContext,TomcatEmbeddedContext继承了StandardContext

protected void configureContext(Context context, ServletContextInitializer[] initializers) {
	TomcatStarter starter = new TomcatStarter(initializers);
	if (context instanceof TomcatEmbeddedContext) {
		TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
		embeddedContext.setStarter(starter);
		embeddedContext.setFailCtxIfServletStartFails(true);
	}
	context.addServletContainerInitializer(starter, NO_CLASSES);
	for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
		context.addLifecycleListener(lifecycleListener);
	}
}

在调用context的startInternal时,会调用这些ServletContextInitializer
TomcatStarter继承了ServletContainerInitializer
在StandardContext的startInternal方法中会调用ServletContainerInitializer的onStartUp方法

for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
   initializers.entrySet()) {
    try {
        entry.getKey().onStartup(entry.getValue(),
                getServletContext());
    } catch (ServletException e) {
        log.error(sm.getString("standardContext.sciFail"), e);
        ok = false;
        break;
    }
}

返回来看下TomcatStarter的onStartUp,可以看到会调用赋值给它的ServletContextInitializer 的onStartUp方法,之前的selfInitialize就是在这里调用的

public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
	try {
		for (ServletContextInitializer initializer : this.initializers) {
			initializer.onStartup(servletContext);
		}
	}
	catch (Exception ex) {
		this.startUpException = ex;
		// Prevent Tomcat from logging and re-throwing when we know we can
		// deal with it in the main thread, but log for information here.
		if (logger.isErrorEnabled()) {
			logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
					+ ex.getMessage());
		}
	}
}
getSelfInitializer

下面看下getSelfInitializer具体做了什么
ServletContextInitializer是一个函数接口,因此这里的主要执行逻辑在selfInitialize中

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
	return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
	// 主要是建立serlvetContext和applicationcontext之间的联系
	prepareWebApplicationContext(servletContext);
	// 注册一个新的bean scope
	registerApplicationScope(servletContext);
	// 这里会将servletContext注册到容器中
	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
	// 从容器中获取ServletContextInitializer类型的bean,并且调用onStartup方法
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}
prepareWebApplicationContext
protected void prepareWebApplicationContext(ServletContext servletContext) {
	Object rootContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
	if (rootContext != null) {
		if (rootContext == this) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - "
							+ "check whether you have multiple ServletContextInitializers!");
		}
		return;
	}
	servletContext.log("Initializing Spring embedded WebApplicationContext");
	try {
		// 将当前的ServletWebServerApplicationContext放入servletContext中
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
		if (logger.isDebugEnabled()) {
			logger.debug("Published root WebApplicationContext as ServletContext attribute with name ["
					+ WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
		}
		// 将servletcontext设置到当前的applicationContext中
		setServletContext(servletContext);
		if (logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - getStartupDate();
			logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
		}
	}
	catch (RuntimeException | Error ex) {
		logger.error("Context initialization failed", ex);
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
		throw ex;
	}
}
registerApplicationScope
private void registerApplicationScope(ServletContext servletContext) {
	// 注册一个新的bean scope
	ServletContextScope appScope = new ServletContextScope(servletContext);
	getBeanFactory().registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
	// Register as ServletContext attribute, for ContextCleanupListener to detect it.
	servletContext.setAttribute(ServletContextScope.class.getName(), appScope);
}
DispatcherServletRegistrationBean

从之前的有关spring boot web自动配置的文章中,我们知道,在自动注册的过程中会向容器中注入一个DispatcherServletRegistrationBean类型的bean,这个bean同样继承了ServletContextInitializer,因此在启动的过程中同样会调用

在这里插入图片描述
在RegistrationBean的onStartup方法中会调用register方法,register方法由子类实现

@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
	String description = getDescription();
	if (!isEnabled()) {
		logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
		return;
	}
	register(description, servletContext);
}

接下来看下DynamicRegistrationBean的register方法

protected final void register(String description, ServletContext servletContext) {
	D registration = addRegistration(description, servletContext);
	if (registration == null) {
		logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
		return;
	}
	configure(registration);
}

接下来看下ServletRegistrationBean的addRegistration方法

protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
	String name = getServletName();
	// 这里会将DispatcherServlet加入到servletContext中
	return servletContext.addServlet(name, this.servlet);
}

DispatcherServlet的初始化

在这里插入图片描述

Servlet

Servlet接口主要有如下几个方法
在这里插入图片描述

GenericServlet

这里看下init方法,这里首先对ServletConfig进行了赋值,然后调用了子类实现的init方法

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}
HttpServlet
public final void init() throws ServletException {

	// Set bean properties from init parameters.
	// 将servletConfig中的参数设置到PropertyValues中
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			// 将servletConfig中的参数设置到servlet中
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			}
			throw ex;
		}
	}

	// Let subclasses do whatever initialization they like.
	// 由子类来完成自定义的初始化操作
	initServletBean();
}
FrameworkServlet
protected final void initServletBean() throws ServletException {
	getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
	if (logger.isInfoEnabled()) {
		logger.info("Initializing Servlet '" + getServletName() + "'");
	}
	long startTime = System.currentTimeMillis();

	try {
		// 从servletContext中取出webApplicationContext
		this.webApplicationContext = initWebApplicationContext();
		initFrameworkServlet();
	}
	catch (ServletException | RuntimeException ex) {
		logger.error("Context initialization failed", ex);
		throw ex;
	}

	if (logger.isDebugEnabled()) {
		String value = this.enableLoggingRequestDetails ?
				"shown which may lead to unsafe logging of potentially sensitive data" :
				"masked to prevent unsafe logging of potentially sensitive data";
		logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
				"': request parameters and headers will be " + value);
	}

	if (logger.isInfoEnabled()) {
		logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
	}
}
initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
	// 从servletContext中获取root applicationContext
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;

	// 判断当前是否已经有servlet webapplicationcoantext
	// 走到这里的时候webApplicationContext不为空,因为FrameworkServet实现了ApplicationContextAware,所以是通过setApplicationContext方法设置的
	// 另外需要注意,spring mvc 中的applicationContext是有分层结构的,root application作为servlet application context的父容器
	// 但是在spring boot中只有一个application context
	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				// The context has not yet been refreshed -> provide services such as
				// setting the parent context, setting the application context id, etc
				if (cwac.getParent() == null) {
					// The context instance was injected without an explicit parent -> set
					// the root application context (if any; may be null) as the parent
					cwac.setParent(rootContext);
				}
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	// 当前没有创建servlet webApplicationContext
	if (wac == null) {
		// No context instance was injected at construction time -> see if one
		// has been registered in the servlet context. If one exists, it is assumed
		// that the parent context (if any) has already been set and that the
		// user has performed any initialization such as setting the context id
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		// No context instance is defined for this servlet -> create a local one
		wac = createWebApplicationContext(rootContext);
	}

	if (!this.refreshEventReceived) {
		// Either the context is not a ConfigurableApplicationContext with refresh
		// support or the context injected at construction time had already been
		// refreshed -> trigger initial onRefresh manually here.
		synchronized (this.onRefreshMonitor) {
			// 调用子类实现的onRefresh
			onRefresh(wac);
		}
	}

	if (this.publishContext) {
		// Publish the context as a servlet context attribute.
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}

	return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		// The application context id is still set to its original default value
		// -> assign a more useful id based on available information
		if (this.contextId != null) {
			wac.setId(this.contextId);
		}
		else {
			// Generate default id...
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
		}
	}

	// 将servletcontext servletconfig等设置到root applicationContext上
	wac.setServletContext(getServletContext());
	wac.setServletConfig(getServletConfig());
	wac.setNamespace(getNamespace());
	wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

	// The wac environment's #initPropertySources will be called in any case when the context
	// is refreshed; do it eagerly here to ensure servlet property sources are in place for
	// use in any post-processing or initialization that occurs below prior to #refresh
	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
	}

	postProcessWebApplicationContext(wac);
	applyInitializers(wac);
	// 重新刷新容器
	wac.refresh();
}
DispatcherServlet
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);
	initLocaleResolver(context);
	initThemeResolver(context);
	initHandlerMappings(context);
	initHandlerAdapters(context);
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}

这里简单地看下initMultipartResolver

private void initMultipartResolver(ApplicationContext context) {
	try {
		// 从root application context中获取MultipartResolver
		// 然后设置到到dispatcher servlet上
		this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
		if (logger.isTraceEnabled()) {
			logger.trace("Detected " + this.multipartResolver);
		}
		else if (logger.isDebugEnabled()) {
			logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
		}
	}
	catch (NoSuchBeanDefinitionException ex) {
		// Default is no multipart resolver.
		this.multipartResolver = null;
		if (logger.isTraceEnabled()) {
			logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
		}
	}
}

其他的几个init逻辑类似

HandlerMapping

重点需要看下initHandlerMappings

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		// 从容器中获取类型为HandlerMapping的bean
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}

	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	if (this.handlerMappings == null) {
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}

默认情况下,一共有5个默认的HanlerMapping类型的bean
在这里插入图片描述
其中最重要的就是requestMappingHandlerMapping,这个handlerMapping主要负责处理@Controller注解和@RequestMapping注解
下面重点看下RequestMappingHandlerMapping
在这里插入图片描述
首先看下AbstractHandlerMapping

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}
}

可以看到AbstractHandlerMapping实现了InitializingBean这个接口,因此需要关注afterPropertiesSet方法

protected void initHandlerMethods() {
	// 这里会将容器中所有的bean都取出来
	for (String beanName : getCandidateBeanNames()) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			// 这里会判断是否是需要处理的bean,如果是的话会对这个bean进行处理
			processCandidateBean(beanName);
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
	Class<?> beanType = null;
	try {
		// 根据bean的名称来获取当前bean的类型
		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实现,根据bean的类型来判断是否是自己需要处理的bean
	if (beanType != null && isHandler(beanType)) {
		detectHandlerMethods(beanName);
	}
}

这里可以看下RequestMappingHandlerMapping的isHandler实现,比较简单,就是判断这个类是否使用了Controller注解或者是RequestMapping注解

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

接着看下detectHandlerMethods

protected void detectHandlerMethods(Object handler) {
	// 获取这个handler的类型
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		// 遍历当前bean的所有方法,对每个方法调用getMappingForMethod方法,调用getMappingForMethod来生成对应的RequestMappingInfo
		// 如果遍历的方法返回的RequestMappingInfo不为空,那么那会将Method -> RequestMappingInfo添加到methods这个map中
		// 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);
					}
				});
		if (logger.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
		// 这里会将每个带有@RequestMapping注解的方法和其映射信息注册到mappingRegistry
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

下面首先看下getMappingForMethod

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	// 读取方法上的@RequestMapping注解,主要是获取映射路径
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		// 解析当前方法所在的类上的@RequestMapping注解来生成RequestMappingInfo
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		// 这里会将方法的映射信息和类的映射信息结合起来
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
		}
	}
	// 返回映射信息
	return info;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	// 获取element上的的RequestMapping注解,这里的element有可能是controller或者是其中的method
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

至此就完成了对@Controller和@RequestMapping注解的解析,并且将映射信息注册到了mappingRegistry
接下来看下是如何注册到mappingRegistry中的

MappingRegistry

这里看下MappingRegistry的register方法

public void register(T mapping, Object handler, Method method) {
	// Assert that the handler method is not a suspending one.
	if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
		Class<?>[] parameterTypes = method.getParameterTypes();
		if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
			throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
		}
	}
	// 上写锁
	this.readWriteLock.writeLock().lock();
	try {
		// 根据controller和对应的方法创建HandlerMethod
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		validateMethodMapping(handlerMethod, mapping);
		// 将映射关系添加到mappingLookup中
		this.mappingLookup.put(mapping, handlerMethod);
		
		// 返回映射信息中的直接url,比如/test/hello就是directUrl
		// 而具有? * {}等字符的就不是directurl
		List<String> directUrls = getDirectUrls(mapping);
		// 将这些directurl和mapping的关系添加到urlLookup
		for (String url : directUrls) {
			this.urlLookup.add(url, mapping);
		}

		String name = null;
		if (getNamingStrategy() != null) {
			// 这里会获取handler的类型名称,比如TestController
			// 然后提取名称中的大写字符,这里是TC
			// 然后拼接上方法的名称,比如TC#helloWorld
			name = getNamingStrategy().getName(handlerMethod, mapping);
			// 然后将这个name -> handlerMethod的映射添加到namelookup这个map中
			addMappingName(name, handlerMethod);
		}

		// 跨域相关,之后会有单独的文章介绍
		CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
		if (corsConfig != null) {
			this.corsLookup.put(handlerMethod, corsConfig);
		}

		this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值