赞
踩
Spring Web MVC 框架也是一个基于请求驱动的Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射 规则分发给相应的页面控制器(动作/处理器)进行处理。博文介绍Spring mvc的原理与工作流程,帮助大家更好的理解spring mvc的思想与原理。
核心架构的具体流程步骤如下:
上面就是一个传统的完整的 Spring MVC
流程,为什么要说这是传统的流程呢?因为这个流程是用于前后端没有分离的时候,后台直接返回页面给浏览器进行渲染,而现在大部分应用都是前后端分离,后台直接生成一个 Json
字符串就直接返回前端,不需要经过视图解析器进行处理。
Spring MVC主要可以分为两大过程,一是初始化,二就是处理请求。初始化的过程主要就是将我们定义好的 RequestMapping
映射路径和 Controller
中的方法进行一一映射存储,这样当收到请求之后就可以处理请求调用对应的方法,从而响应请求。
初始化:初始化过程的入口方法是 DispatchServlet
的 init()
方法,而实际上 DispatchServlet
中并没有这个方法,所以我们就继续寻找父类,会发现 init
方法在其父类(FrameworkServlet)的父类 HttpServletBean
中。
HttpServletBean:init()方法
在这个方法中,首先会去家在一些 Servlet 相关配置(web.xml),然后会调用 initServletBean()
方法,这个方法是一个空的模板方法,业务逻辑由子类 FrameworkServlet
来实现。
- public final void init() throws ServletException {
- PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
- if (!pvs.isEmpty()) {
- try {
- // 定义资源
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- // 加载servlet相关配置
- ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
- this.initBeanWrapper(bw);
- bw.setPropertyValues(pvs, true);
- } catch (BeansException var4) {
- if (this.logger.isErrorEnabled()) {
- this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
- }
-
- throw var4;
- }
- }
- // 核心方法 由子类FrameworkServlet实现
- this.initServletBean();
- }
FrameworkServlet-initServletBean()
这个方法本身没有什么业务逻辑,主要是初始化 WebApplicationContext
对象,WebApplicationContext
继承自 ApplicationContext
,主要是用来处理 web
应用的上下文。
- /**
- * Overridden method of {@link HttpServletBean}, invoked after any bean properties
- * have been set. Creates this servlet's WebApplicationContext.
- * {@link HttpServletBean} 的重写方法,在设置任何 bean 属性后调用。创建此 servlet 的 WebApplicationContext。
- */
- @Override
- 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 {
- 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");
- }
- }
FrameworkServlet-initWebApplicationContext()
initWebApplicationContext()
方法主要就是为了找到一个上下文,找不到就会创建一个上下文,创建之后,最终会调用方法configureAndRefreshWebApplicationContext(cwac)
方法,而这个方法最终在设置一些基本容器标识信息之后会去调用 refresh()
方法。这个就是初始化IOC容器。
- protected WebApplicationContext initWebApplicationContext() {
- // 从serlvetContent 中获取父类容器 webApplicationContext()
- WebApplicationContext rootContext =
- WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- WebApplicationContext wac = null;
-
- 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);
- }
- // 调用ioc容器的refresh()方法来初始化ioc信息
- configureAndRefreshWebApplicationContext(cwac);
- }
- }
- }
- 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(wac);
- }
- }
-
- if (this.publishContext) {
- // Publish the context as a servlet context attribute.
- String attrName = getServletContextAttributeName();
- getServletContext().setAttribute(attrName, wac);
- }
-
- return wac;
- }
当调用 refresh()
方法初始化 ioc
容器之后,最终会调用方法 onRefresh()
,这个方法也是一个模板钩子方法,由子类实现,也就是回到了我们 Spring MVC
的入口类 DispatcherServlet
。
DispatchServlet-onRefresh()
onRefresh()
方法就是 Spring MVC
初始化的最后一个步骤,在这个步骤当中会初始化 Spring MVC
流程中可能需要使用到的九大组件。
- */
- @Override
- protected void onRefresh(ApplicationContext context) {
- initStrategies(context);
- }
-
- /**
- * 初始化的九大组件
- * Initialize the strategy objects that this servlet uses.
- * <p>May be overridden in subclasses in order to initialize further strategy objects.
- */
- protected void initStrategies(ApplicationContext context) {
- initMultipartResolver(context);//初始化多文件上传组件
- initLocaleResolver(context);// 初始化本地语言环境
- initThemeResolver(context);// 初始化模板处理器
- initHandlerMappings(context);// 初始化 HandlerMappings
- initHandlerAdapters(context);// 初始化HandlerAdapters
- initHandlerExceptionResolvers(context);// 初始化异常拦截器
- initRequestToViewNameTranslator(context);// 初始化视图预处理器
- initViewResolvers(context);// 初始化视图转化器
- initFlashMapManager(context);// 解决用户重的定向是参数丢失问题
- }
Request
对象包装成 MultipartHttpServletRequest
对象来进行处理。LocaleResolver
用于初始化本地语言环境,其从 Request
对象中解析出当前所处的语言环境,如中国大陆则会解析出 zh-CN
等等,模板解析以及国际化的时候都会用到本地语言环境。Spring MVC
中,一套主题对应一个 .properties
文件,可以存放和当前主题相关的所有资源,如图片,css样式等。Handler
),比如我们 Controller
中的方法,这个其实最主要就是用来存储 url
和 调用方法的映射关系,存储好映射关系之后,后续有请求进来,就可以知道调用哪个 Controller
中的哪个方法,以及方法的参数是哪些。Spring MVC
中支持很多种 Handler
,但是最终将请求交给 Servlet
时,只能是 doService(req,resp)
形式,所以 HandlerAdapter
就是用来适配转换格式的。Handler
时产生的异常进行处理,然后会根据异常设置对应的 ModelAndView
,然后交给 Render
渲染成页面。Request
中获取到视图名称。RequestToViewNameTranslator
组件获取到的视图名称,因为视图名称是字符串格式,所以这里会将字符串格式的视图名称转换成为 View
类型视图,最终经过一系列解析和变量替换等操作返回一个页面到前端。FlashMap
,那么 FlashMap
又有什么用呢?要明白这个那就不得不提到重定向了,有时候我们提交一个请求的时候会需要重定向,那么假如参数过多或者说我们不想把参数拼接到 url
上(比如敏感数据之类的),这时候怎么办呢?因为参数不拼接在 url
上重定向是无法携带参数的。FlashMap
就是为了解决这个问题,我们可以在请求发生重定向之前,将参数写入 request
的属性 OUTPUT_FLASH_MAP_ATTRIBUTE
中,这样在重定向之后的 handler
中,Spring
会自动将其设置到 Model
中,这样就可以从 Model
中取到我们传递的参数了。在九大组件初始化完成之后,Spring MVC
的初始化就完成了,接下来就是接收并处理请求了,那么处理请求的入口在哪里呢?处理请求的入口方法就是 DispatcherServlet
中的 doService
方法,而 doService
方法又会调用 doDispatch
方法。
- @Override
- protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
- logRequest(request);
-
- // Keep a snapshot of the request attributes in case of an include,
- // to be able to restore the original attributes after the include.
- Map<String, Object> attributesSnapshot = null;
- if (WebUtils.isIncludeRequest(request)) {
- attributesSnapshot = new HashMap<>();
- Enumeration<?> attrNames = request.getAttributeNames();
- while (attrNames.hasMoreElements()) {
- String attrName = (String) attrNames.nextElement();
- if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
- attributesSnapshot.put(attrName, request.getAttribute(attrName));
- }
- }
- }
-
- // Make framework objects available to handlers and view objects.
- request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
- request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
- request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
- request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
-
- if (this.flashMapManager != null) {
- FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
- if (inputFlashMap != null) {
- request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
- }
- request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
- request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
- }
-
- RequestPath previousRequestPath = null;
- if (this.parseRequestPath) {
- previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
- ServletRequestPathUtils.parseAndCache(request);
- }
-
- try {
- // 核心的方法
- doDispatch(request, response);
- }
- finally {
- if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
- // Restore the original attribute snapshot, in case of an include.
- if (attributesSnapshot != null) {
- restoreAttributesAfterInclude(request, attributesSnapshot);
- }
- }
- if (this.parseRequestPath) {
- ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
- }
- }
- }
- @SuppressWarnings("deprecation")
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- boolean multipartRequestParsed = false;
-
- WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
-
- try {
- ModelAndView mv = null;
- Exception dispatchException = null;
-
- try {
- // 检查是否有文件上传请求
- processedRequest = checkMultipart(request);
- multipartRequestParsed = (processedRequest != request);
-
- // Determine handler for the current request.
- //获取处理器,也就是controller中的方法
- 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());
-
- if (asyncManager.isConcurrentHandlingStarted()) {
- return;
- }
-
- applyDefaultViewName(processedRequest, mv);
- mappedHandler.applyPostHandle(processedRequest, response, mv);
- }
- catch (Exception ex) {
- dispatchException = ex;
- }
- catch (Throwable err) {
- // As of 4.3, we're processing Errors thrown from handler methods as well,
- // making them available for @ExceptionHandler methods and other scenarios.
- dispatchException = new NestedServletException("Handler dispatch failed", err);
- }
- processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
- }
- catch (Exception ex) {
- triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
- }
- catch (Throwable err) {
- triggerAfterCompletion(processedRequest, response, mappedHandler,
- new NestedServletException("Handler processing failed", err));
- }
- finally {
- if (asyncManager.isConcurrentHandlingStarted()) {
- // Instead of postHandle and afterCompletion
- if (mappedHandler != null) {
- mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
- }
- }
- else {
- // Clean up any resources used by a multipart request.
- if (multipartRequestParsed) {
- cleanupMultipart(processedRequest);
- }
- }
- }
- }
这个方法最关键的就是调用了 getHandler
方法,这个方法就是会获取到前面九大组件中的 HandlerMapping
,然后进行反射调用对应的方法完成请求,完成请求之后后续还会经过视图转换之类的一些操作,最终返回 ModelAndView
,不过现在都是前后端分离,基本也不需要用到视图模型,在这里我们就不分析后续过程,主要就是分析 HandlerMapping
的初始化和查询过程。
DispatcherServlet-getHandler()
这个方法里面会遍历 handllerMappings
,这个 handllerMappings
是一个 List
集合,因为 HandlerMapping
有多重实现,也就是 HandlerMapping
不止一个实现,其最常用的两个实现为 RequestMappingHandlerMapping
和 BeanNameUrlHandlerMapping
。
- @Nullable
- protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- if (this.handlerMappings != null) {
- // 遍历的map 中的Handler处理 HandlerMapping 中有多重实现 多重类型
- for (HandlerMapping mapping : this.handlerMappings) {
- HandlerExecutionChain handler = mapping.getHandler(request);
- if (handler != null) {
- return handler;
- }
- }
- }
- return null;
- }
AbstractHandlerMapping-getHandler()
AbstractHandlerMapping
是一个抽象类,其 getHandlerInternal
这个方法也是一个模板方法:
- @Nullable
- public Object getDefaultHandler() {
- return this.defaultHandler;
- }
getHandlerInternal
方法最终其会调用子类实现,而这里的子类实现会有多个,其中最主要的就是 AbstractHandlerMethodMapping
和 AbstractUrlHandlerMapping
两个抽象类,那么最终到底会调用哪个实现类呢?这时候如果拿捏不准我们就可以看一下类图,上面我们提到,HandlerMapper
有两个非常主要的实现类:RequestMappingHandlerMapping
和 BeanNameUrlHandlerMapping
。那么我们就分别来看一下这两个类的类图关系:
可以看到,这两个实现类的抽象父类正好对应了 AbstractHandlerMapping
的两个子类,所以这时候具体看哪个方法,那就看我们想看哪种类型了。
RequestMappingHandlerMapping:主要用来存储 RequestMapping
注解相关的控制器和 url
的映射关系。
BeanNameUrlHandlerMapping:主要用来处理 Bean name
直接以 /
开头的控制器和 url
的映射关系。
其实除了这两种 HandlerMapping
之外,Spring
中还有其他一些 HandllerMapping
,如 SimpleUrlHandlerMapping
等。提到的这几种 HandlerMapping
,对我们来说最常用,最熟悉的那肯定就是 RequestMappingHandlerMapping
,在这里我们就以这个为例来进行分析。
AbstractHandlerMethodMapping-getHandlerInternal()
这个方法本身也没有什么逻辑,其主要的核心查找 Handler
逻辑在 lookupHandlerMethod
方法中,这个方法主要是为了获取一个 HandlerMethod
对象,前面的方法都是 Object
,而到这里变成了 HandlerMethod
类型,这是因为 Handler
有各种类型,目前我们已经基本跟到了具体类型之下,所以类型就变成了具体类型,而如果我们看的的另一条分支线,那么返回的就会是其他对象,正是因为支持多种不同类型的 HandlerMapping
对象,所以最终为了统一执行,才会需要在获得 Hanlder
之后,DispatcherServlet
中会再次通过调用 getHandlerAdapter
方法来进一步封装成 HandlerAdapter
对象,才能进行方法的调用。
- /**
- * Look up a handler method for the given request.
- * 查找给定请求的处理程序方法。
- */
- @Override
- @Nullable
- protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
- String lookupPath = initLookupPath(request);// 查找请求的路径
- this.mappingRegistry.acquireReadLock();
- try {
- // 寻找handler
- HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
- return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
- }
- finally {
- this.mappingRegistry.releaseReadLock();
- }
- }
AbstractHandlerMethodMapping-lookupHandlerMethod()
这个方法主要会从 mappingRegistry
中获取命中的方法,获取之后还会经过一系列的判断比较判断比较,因为有些 url
会对应多个方法,而方法的请求类型不同,比如一个 GET
方法,一个 POST
方法,或者其他一些属性不相同等等,都会导致最终命中到不同的方法,这些逻辑主要都是在addMatchingMappings
方法去进一步实现,并最终将命中的结果加入到 matches
集合内。
- @Nullable
- protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
- List<Match> matches = new ArrayList<>();
- List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
- if (directPathMatches != null) {
- // 找到合适的Mapping 这一步进一步根据属性查找准确的Hadnler
- addMatchingMappings(directPathMatches, matches, request);
- }
- if (matches.isEmpty()) {
- // 如果没有找打 那就遍历mapping中的HandlerMapping
- addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
- }
- if (!matches.isEmpty()) {
- Match bestMatch = matches.get(0);
- if (matches.size() > 1) {
- Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
- matches.sort(comparator);
- bestMatch = matches.get(0);
- //将HandlerMethod进一步进行包装
- if (logger.isTraceEnabled()) {
- logger.trace(matches.size() + " matching mappings: " + matches);
- }
- if (CorsUtils.isPreFlightRequest(request)) {
- for (Match match : matches) {
- if (match.hasCorsConfig()) {
- return PREFLIGHT_AMBIGUOUS_MATCH;
- }
- }
- }
- else {
- // 获取第二个 Handler 进行进一步的比较。
- Match secondBestMatch = matches.get(1);
- if (comparator.compare(bestMatch, secondBestMatch) == 0) {
- Method m1 = bestMatch.getHandlerMethod().getMethod();
- Method m2 = secondBestMatch.getHandlerMethod().getMethod();
- String uri = request.getRequestURI();
- throw new IllegalStateException(
- "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
- }
- }
- }
- request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
- handleMatch(bestMatch.mapping, lookupPath, request);
- return bestMatch.getHandlerMethod();
- }
- else {
- return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
- }
- }
在这个方法中,有一个对象非常关键,那就是 mappingRegistry
,因为最终我们根据url
到这里获取到对应的 HandlerMtthod
,所以这个对象很关键:
- class MappingRegistry {
-
- private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
-
- private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
-
- private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
-
- private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
-
- private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
-
- /**
- * Return all registrations.
- * @since 5.3
- */
- public Map<T, MappingRegistration<T>> getRegistrations() {
- return this.registry;
- }
-
- /**
- * Return matches for the given URL path. Not thread-safe.
- * @see #acquireReadLock()
- */
- @Nullable
- public List<T> getMappingsByDirectPath(String urlPath) {
- return this.pathLookup.get(urlPath);
- }
-
- /**
- * Return handler methods by mapping name. Thread-safe for concurrent use.
- */
- public List<HandlerMethod> getHandlerMethodsByMappingName(String mappingName) {
- return this.nameLookup.get(mappingName);
- }
-
- /**
- * Return CORS configuration. Thread-safe for concurrent use.
- */
- @Nullable
- public CorsConfiguration getCorsConfiguration(HandlerMethod handlerMethod) {
- HandlerMethod original = handlerMethod.getResolvedFromHandlerMethod();
- return this.corsLookup.get(original != null ? original : handlerMethod);
- }
看这个对象其实很明显可以看出来,这个对象其实只是维护了一些 Map
对象,所以我们可以很容易猜测到,一定在某一个地方,将 url
和 HandlerMapping
或者 HandlerMethod
的映射关系存进来了,这时候其实我们可以根据 getMappingsByUrl
方法来进行反推,看看 urlLookup
这个 Map
是什么时候被存入的,结合上面的类图关系,一路反推,很容易就可以找到这个 Map
中的映射关系是 AbstractHandlerMethodMapping
对象的 afterPropertiesSet
方法实现的。(AbstractHandlerMethodMapping
实现了 InitializingBean
接口),也就是当这个对象初始化完成之后,我们的 url
和 Handler
映射关系已经存入了 MappingRegistry
对象中的集合 Map
中。
AbstractHandlerMethodMapping 的初始化
afterPropertiesSet
方法中并没有任何逻辑,而是直接调用了 initHandlerMethods
。
AbstractHandlerMethodMapping-initHandlerMethods()
initHandlerMethods
方法中,首先还是会从 Spring
的上下文中获取所有的 Bean
,然后会进一步从带有 RequestMapping
注解和 Controller
注解中的 Bean
去解析并获得 HandlerMethod
。
- /**
- * Scan beans in the ApplicationContext, detect and register handler methods.
- * 扫描 ApplicationContext 中的 bean,检测并注册 handler方法。
- * @see #getCandidateBeanNames()
- * @see #processCandidateBean
- * @see #handlerMethodsInitialized
- */
- protected void initHandlerMethods() {
- for (String beanName : getCandidateBeanNames()) {
- if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
- processCandidateBean(beanName);
- }
- }
- handlerMethodsInitialized(getHandlerMethods());
- }
AbstractHandlerMethodMapping-detectHandlerMethods()
这个方法中,其实就是通过反射获取到 Controller
中的所有方法,然后调用 registerHandlerMethod
方法将相关信息注册到 MappingRegistry
对象中的各种 Map
集合之内:
- /**
- * 在指定的处理程序 bean 中查找处理程序方法
- * @param handler either a bean name or an actual handler instance
- * @see #getMappingForMethod
- */
- 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));
- }
- else if (mappingsLogger.isDebugEnabled()) {
- mappingsLogger.debug(formatMappings(userType, methods));
- }
- // 遍历Controller中所有的方法
- methods.forEach((method, mapping) -> {
- Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
- // 注册HandlerMethod
- registerHandlerMethod(handler, invocableMethod, mapping);
- });
- }
- }
AbstractHandlerMethodMapping-register()
registerHandlerMethod
方法中会直接调用 AbstractHandlerMethodMapping
对象持有的 mappingRegistry
对象中的 regidter
方法,这里会对 Controller
中方法上的一些元信息进行各种解析,比如参数,路径,请求方式等等,然后会将各种信息注册到对应的 Map
集合中,最终完成了整个初始化。
- protected void registerHandlerMethod(Object handler, Method method, T mapping) {
- this.mappingRegistry.register(mapping, handler, method);
- }
- 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.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,
- new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
- }
- finally {
- this.readWriteLock.writeLock().unlock();
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。