Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

首先是跳转,应该说设置状态,其中跳转是其中一个状态,

@RequestMapping(value = "request1", method = RequestMethod.GET)
    public void request1(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(HttpServletResponse.SC_OK);
    }

200 状态就很简单,如果想做跳转可以使用 302 并设置 location

@RequestMapping(value = "request2", method = RequestMethod.GET)
public void request2(HttpServletRequest request, HttpServletResponse response) {
    response.setHeader("Location", "https://baidu.com");
    response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
}

这里如果设置了 302,但没设置跳转 location,就是 302 found

int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;

而如果是 301 跳转,代表原地址不可用了,永久跳转

@RequestMapping(value = "request3", method = RequestMethod.GET)
    public void request3(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Location", "https://baidu.com");
        response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
    }

另一个问题是设置 cookie

@RequestMapping(value = "request4", method = RequestMethod.GET)
    public void request4(HttpServletRequest request, HttpServletResponse response) {
        Cookie cookie = new Cookie("a", "b");
        response.addCookie(cookie);
        response.setStatus(HttpServletResponse.SC_OK);
    }

这样的设置就能成功设定 cookie,而随着目前的浏览器 cookie 策略,如果要跳转后设置的话,估计是会越来越难,包括

response.addHeader("Set-Cookie", "a=b; domain=baidu.com; SameSite=none;Secure");

这样的方式也没法实现。

Tomcat 会把请求委托到org.springframework.web.servlet.DispatcherServlet#doService

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

        // 省略前面的代码
		try {
			doDispatch(request, response);
		}

然后就是调用org.springframework.web.servlet.DispatcherServlet#doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

	try {
		ModelAndView mv = null;
		Exception dispatchException = null;

		try {
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

               // 去获取处理器
			// Determine handler for the current request.
			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 = HttpMethod.GET.matches(method);
			if (isGet || HttpMethod.HEAD.matches(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}

			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// Actually invoke the handler.
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

看下这里的逻辑

@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;
}

可以看到有这些 HandlerMapping

而这里面就是前面提到过的org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
从这就能找到具体的 Handler
com.nicksxs.spbdemo.controller.DemoController#test()
这就是我简单的示例代码

@RequestMapping(value = "/test", method = RequestMethod.GET)
@ResponseBody
public DemoResponse test() {
    String item = "{\"id\": 1, \"name\": \"nick\"}";
    ParserConfig parserConfig = ParserConfig.getGlobalInstance();
    parserConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
    DemoResponse response = JSON.parseObject(item, DemoResponse.class, parserConfig);
    return response;
}

再获取适配器,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

正好这个适配器是调用的父类的 supports 方法

@Override
public final boolean supports(Object handler) {
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

而我这个com.nicksxs.spbdemo.controller.DemoController#test()就是个包装好的 HandlerMethod
然后就是调用 hahandle 方法,也是通过模板方法,实际调用的是
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	return handleInternal(request, response, (HandlerMethod) handler);
}

然后调用 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
            // 是否要锁定 session,否则走到这
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

继续调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            // 包装调用方法,
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			// 省略一部分非本次关注核心代码
            // 然后是调用到这
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

稍微在看一眼
第一步是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#createInvocableHandlerMethod

protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
	return new ServletInvocableHandlerMethod(handlerMethod);
}

第二步是org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#ServletInvocableHandlerMethod(org.springframework.web.method.HandlerMethod)

   public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
	super(handlerMethod);
}

第三部是org.springframework.web.method.support.InvocableHandlerMethod#InvocableHandlerMethod(org.springframework.web.method.HandlerMethod)

   public InvocableHandlerMethod(HandlerMethod handlerMethod) {
	super(handlerMethod);
}

第四步是org.springframework.web.method.HandlerMethod#HandlerMethod(org.springframework.web.method.HandlerMethod)

protected HandlerMethod(HandlerMethod handlerMethod) {
	Assert.notNull(handlerMethod, "HandlerMethod is required");
	this.bean = handlerMethod.bean;
	this.beanFactory = handlerMethod.beanFactory;
	this.beanType = handlerMethod.beanType;
	this.method = handlerMethod.method;
	this.bridgedMethod = handlerMethod.bridgedMethod;
	this.parameters = handlerMethod.parameters;
	this.responseStatus = handlerMethod.responseStatus;
	this.responseStatusReason = handlerMethod.responseStatusReason;
	this.description = handlerMethod.description;
	this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
}

这是个继承关系,一直调用到最顶层的父类的构造方法,其实就是拷贝,然后继续调用org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

        // 调用请求
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		// 稍微忽略下后面的代码
	}

继续调用 org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	return doInvoke(args);
}

来到了最核心处 org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

@Nullable
protected Object doInvoke(Object... args) throws Exception {
	Method method = getBridgedMethod();
	ReflectionUtils.makeAccessible(method);
	try {
		if (KotlinDetector.isSuspendingFunction(method)) {
			return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
		}
           // 会走到这里,获取到 bean,而这个 bean 就是前面构造方法里赋值的,最开始被放在 handler 里面,然后调用方法
		return method.invoke(getBean(), args);
	}

springboot 的一个方便之处就是集成了 web server 进去,接着上一篇继续来看下这个 web server 的启动过程
基于 springboot 的 2.2.9.RELEASE 版本
整个 springboot 体系主体就是看 org.springframework.context.support.AbstractApplicationContext#refresh 刷新方法,
而启动 web server 的方法就是在其中的 OnRefresh

try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
                // ------------------> ‼️就是这里
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

跟进去其实本身是啥都不做的,由具体的实现类子类去做各自的处理
org.springframework.context.support.AbstractApplicationContext#onRefresh

/**
 * Template method which can be overridden to add context-specific refresh work.
 * Called on initialization of special beans, before instantiation of singletons.
 * <p>This implementation is empty.
 * @throws BeansException in case of errors
 * @see #refresh()
 */
protected void onRefresh() throws BeansException {
	// For subclasses: do nothing by default.
}

看具体的实现类里就有我们的主角
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh

protected void onRefresh() {
    super.onRefresh();

    try {
        // -------------> 主要就是这里,创建 webserver
        this.createWebServer();
    } catch (Throwable var2) {
        throw new ApplicationContextException("Unable to start web server", var2);
    }
}

具体的就是一些前置处理
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = this.getServletContext();
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = this.getWebServerFactory();
        this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
    } else if (servletContext != null) {
        try {
            this.getSelfInitializer().onStartup(servletContext);
        } catch (ServletException var4) {
            throw new ApplicationContextException("Cannot initialize servlet context", var4);
        }
    }

    this.initPropertySources();
}

初始的 webServer 和 servletContext 都是 null,需要进行初始化
先是获取 WebServer 工厂,
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getWebServerFactory

protected ServletWebServerFactory getWebServerFactory() {
    String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    if (beanNames.length == 0) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
    } else if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    } else {
        return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }
}

获取了类型是 ServletWebServerFactory 的 bean,然后再回来就是调用的接口方法
org.springframework.boot.web.servlet.server.ServletWebServerFactory#getWebServer
根据具体的 factory 来生成对应的譬如 tomcat 的 factory,
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer

@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		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);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}

最后一行就是创建 TomcatWebServer,
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getTomcatWebServer

/**
 * Factory method called to create the {@link TomcatWebServer}. Subclasses can
 * override this method to return a different {@link TomcatWebServer} or apply
 * additional processing to the Tomcat server.
 * @param tomcat the Tomcat server.
 * @return a new {@link TomcatWebServer} instance
 */
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
	return new TomcatWebServer(tomcat, getPort() >= 0);
}

这里面就开始 new 了一个 TomcatWebServer,
org.springframework.boot.web.embedded.tomcat.TomcatWebServer#TomcatWebServer(org.apache.catalina.startup.Tomcat, boolean)

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
	Assert.notNull(tomcat, "Tomcat Server must not be null");
	this.tomcat = tomcat;
	this.autoStart = autoStart;
	initialize();
}

再调用里面的初始化方法,org.springframework.boot.web.embedded.tomcat.TomcatWebServer#initialize

private void initialize() throws WebServerException {
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();

				Context context = findContext();
				context.addLifecycleListener((event) -> {
					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
						// Remove service connectors so that protocol binding doesn't
						// happen when the service is started.
						removeServiceConnectors();
					}
				});

				// Start the server to trigger initialization listeners
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try {
					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
				}
				catch (NamingException ex) {
					// Naming is not enabled. Continue
				}

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}
```  
这里就是继续调用 `org.apache.catalina.startup.Tomcat#start`
```java
    public void start() throws LifecycleException {
        this.getServer();
        this.server.start();
    }
```      
获取server, `org.apache.catalina.startup.Tomcat#getServer`
```java
public Server getServer() {
        if (this.server != null) {
            return this.server;
        } else {
            System.setProperty("catalina.useNaming", "false");
            this.server = new StandardServer();
            this.initBaseDir();
            ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(this.basedir), (String)null));
            this.server.setPort(-1);
            Service service = new StandardService();
            service.setName("Tomcat");
            this.server.addService(service);
            return this.server;
        }
    }

然后就是启动 server,后面可以继续看这个启动 TomcatServer 内部的逻辑

前面讲了怎么获取 mapping url,继续说下这些mappings 是怎么注册进去的,
来看下这个 RequestMappingHandlerMapping 的继承关系

可以看到这个类实现了 org.springframework.beans.factory.InitializingBean 这个接口,然后这个 InitializingBean 提供了 org.springframework.beans.factory.InitializingBean#afterPropertiesSet 接口,可以在 bean 初始化后做一些属性设置等,
这里是调用了类本身的 afterPropertiesSet 方法和父类的

public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(useSuffixPatternMatch());
		this.config.setTrailingSlashMatch(useTrailingSlashMatch());
		this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
		this.config.setContentNegotiationManager(getContentNegotiationManager());

		super.afterPropertiesSet();
	}

父类是 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
具体代码很简略,就是初始化 HandlerMethod

@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

也就是调用了 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods

protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

然后就是调用 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean

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)) {
		detectHandlerMethods(beanName);
	}
}

先是获取的 beanType,在判断 beanType 是不是 Handler,通过方法 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler

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

就很简单,判断是不是有 Controller 注解或者 RequestMapping 注解
然后就是判断 HandlerMethod 了,调用了 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods

protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> 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));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

前面先通过 getMappingForMethod 找出有 Mapping 的方法,

@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		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;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	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);
}

然后再是对 Method 循环调用 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod
可以看到就是上一篇的 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#mappingRegistry 去存储映射信息

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

最后是真的注册逻辑

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 {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				validateMethodMapping(handlerMethod, mapping);
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					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();
			}
		}

底层的存储就是上一篇说的 mappingLookup 来存储信息

最近有个小需求,要把我们一个 springboot 应用的 request mapping 给导出来,这么说已经是转化过了的,应该是要整理这个应用所有的接口路径,比如我有一个 api.baidu1.com 作为接口域名,然后这个域名下有很多个接口提供服务,这些接口都是写在一个 springboot 应用里,如果本身有网关管理这些接口转发的其实可以通过网关的数据输出,这里就介绍下通过代码来获取

RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
    Set<String> urlSet = entry.getKey().getPatternsCondition().getPatterns();
    for (String url : urlSet) {
        System.out.println(url);
    }
}

第一行

逐行来解析下,第一行就是从上下文中获取 RequestMappingHandlerMapping 这个 bean,

第二行

然后调用了 getHandlerMethods,
这里面具体执行了

public Map<T, HandlerMethod> getHandlerMethods() {
		this.mappingRegistry.acquireReadLock();
		try {
			return Collections.unmodifiableMap(this.mappingRegistry.getMappings());
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

前后加了锁,重要的就是从 mappingRegistry 中获取 mappings, 这里获取的就是
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappings 具体的代码

public Map<T, HandlerMethod> getMappings() {
	return this.mappingLookup;
}

而这个 mappingLookup 再来看下

class MappingRegistry {

	private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

	private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

可以看到就是在 MappingRegistry 中的一个变量,至于这个变量是怎么来的,简单的考虑下 springboot 处理请求的流程,就是从 Mapping 去找到对应的 Handler,所以就需要提前将这个对应关系存到这个变量里,
具体可以看这 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.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 {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				validateMethodMapping(handlerMethod, mapping);
				this.mappingLookup.put(mapping, handlerMethod); 

就是在这里会把 mapping 和 handleMethod 对应关系存进去

第三行

这里拿的是上面的 map 里的 key,也就是 RequestMappingInfo 也就是 org.springframework.web.servlet.mvc.method.RequestMappingInfo
而真的 url 就存在 org.springframework.web.servlet.mvc.condition.PatternsRequestCondition
最终这里面的patterns就是我们要的路径

public class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {

	private final static Set<String> EMPTY_PATH_PATTERN = Collections.singleton("");


	private final Set<String> patterns;

写到这下一篇是不是可以介绍下 mapping 的具体注册逻辑

0%