Spring webMvc環境準備問題

網上找到spring webmvc的資料都是管理請求流程,沒有整應用環境初始化流程,這幾天正在看Spring webmvc的源碼,所以就想知道使用spring webmvc的時候, 整個環境是怎麼初始化的,下邊我會採用問答的方式解決我在這個過程中關注的一些問題

先來看看請求處理流程:
在這裏插入圖片描述

1.其實這個流程大家基本都知道,可是我想知道的是既然是Spring webmvc,那麼applicationContext什麼時候初始化的?

2.@RequestMapping標註的方法什麼時候解析的?

3.爲什麼我們自己提供的WebApplicationInitializer能被發現?

4.環境中用到各種Resolver在什麼時候被初始化的?

先來看第一個問題,applicationContext什麼時候初始化的?

以tomcat容器爲例,在啓動容器的過程中,會調用GenericServlet類的init()方法,而在Springweb中HttpServletBean類繼承額GenericServlet,所以實際上這裏應該是一個HttpServletBean的實例,所以調用this.init()方法會調用到HttpServletBean的init()方法,在init()方法中調用了initServletBean()方法,而在HttpServletBean的initServletBean();正是整個spingweb環境初始化的入口

   //GenericServlet
	@Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

	//HttpServletBean
	@Override
	public final void init() throws ServletException {

		// Set bean beanfactorypostprocessor from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				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 beanfactorypostprocessor on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// Let subclasses do whatever initialization they like.
		// 去執行子類的initServletBean,FrameworkServlet繼承了HttpServletBean,這裏會
		//去執行它的方法, 在這個方法裏,回去刷新上下文 context, 然後初始化 web環境,包括各種轉換器
		//所以這個方法容器初始的入口,至關重要
		//		protected void initStrategies(ApplicationContext context) {
		//			initMultipartResolver(context);
		//			initLocaleResolver(context);
		//			initThemeResolver(context);
		//			initHandlerMappings(context);
		//			initHandlerAdapters(context);
		//			initHandlerExceptionResolvers(context);
		//			initRequestToViewNameTranslator(context);
		//			initViewResolvers(context);
		//			initFlashMapManager(context);
		//		}
		initServletBean();
	}

然而initServletBean()方法也只是一個入口而已,正在實現初始化調用還在內部,但是需要注意的是,springweb中FrameworkServlet繼承了HttpServletBean,所以上邊說的httpServletBean實例也不太準確,這裏說是FrameworkServlet實例其實還是不準確,因爲在後邊我們還會發現,DispathcerServlet繼承了FrameworkServlet,沒錯,就是我們最熟悉的哪個DispatcherServlet,我們看看initServletBean()方法的實現

this.webApplicationContext = initWebApplicationContext();

initFrameworkServlet();

到了這裏, 我們終於看到applicationContext的字樣了,沒錯,正是從這裏開始去進行容器初始化的,具體內部的實現,我們還需要看看initWebApplicationContext()方法的實現,這個方法我們稍後再看, 先說說initFrameworkServlet()方法的作用

這個方法我們進去看,會發現它是一個空方法,也就是什麼都沒有做,spring註釋告訴我們,這個方法是一個拓展點,也就是我們可以自己實現一個Servelt類,在這個類中重寫initFrameworkServlet()方法,這樣在容器實例完畢後,我們還可以做一些自己需要的工作,比如說添加一些解析器什麼的

/**
	 * Overridden method of {@link HttpServletBean}, invoked after any bean beanfactorypostprocessor
	 * have been set. Creates this servlet's WebApplicationContext.
	 * 初始化servlet bean
	 */
	@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 {
			//這個地方非常重要,springmvc就是通過這個地方對整個spring容器進行準備的
			//我們在WebApplicationInitializer接口的onStartUp方法中提供的WebApplication實例
			//只是一個剛來new出來的,在整個spring環境創建過程中,我們知道還有很多準備工作需要做
			//其中最重要的一個方法就是refresh方法
			//這個地方就是入口,通過在這裏對整個環境進行初始化
			this.webApplicationContext = initWebApplicationContext();

			//這是一個拓展方法,也就是在整個servlet準備好之後,會調用這個方法
			//現在這個方法沒有做任何事,而在DispatcherServlet中也沒有對這個方法進行重寫
			//所以它只是一個拓展點之一
			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");
		}
	}

this.webApplicationContext = initWebApplicationContext();這個方法是applicationContext初始化的入口,看到這個方法首先會判斷this.webApplicationContext != null,這個webApplicationContext 在什麼時候添加進去的呢?使用過0xml配置的同學一個個都知道, 我們在WebApplicationInitializer的onStartUp方法中設置了一個applicationContext,沒錯,這裏的this.webApplicationContext就是在哪個地方設置的,但是到了這一步我們還沒有看到有關refresh()方法的調用,別急, 我們接着深入,在configureAndRefreshWebApplicationContext(cwac);這個方法我們終於看到了關於refresh方法的調用,spring關於名字的定義還是很值得稱讚的,這個方法一看就知道是關於配置和刷新webApplicationContext的方法

到了這裏關於webApplicationContext初始化的分析就完畢了,refresh()的工作,那可以說是整個Spring容器的核心,這裏不做分析

protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			//這個地方的this.webApplicationContext就是我們通過實現WebApplicationInitializer接口的onStartUp()方法
			//時添加進去的
			//DispatcherServlet servlet = new DispatcherServlet(ac);
			//這個地方我們攜帶了一個WebApplicationContext參數
			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);
					}
					//進行容器配置和刷新,會從這裏去執行容器的refresh方法, 我們都知道
					//在spring中refresh()是一個至關重要的方法
					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.
			//實際上是去執行dispatcherServlet的初始化方法
			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;
	}



	/**
	 * 配置和刷新web容器
	 * @param 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());
			}
		}

		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);
		//applicationContext 初始化後置處理器調用
		//首先去查找所有的初始化後置處理器類 ApplicationContextInitializer
		//然後調用該類的initialize()方法,ApplicationContextInitializer是springweb提供的一個拓展點
		//這裏我們可以通過這個initialize()方法對上下文進行更改
		//如果這個方法調用更晚一些,那麼context調用了refresh()方法後,在更新context某系更新功能將不會生效
		applyInitializers(wac);

		//這裏執行context的刷新方法, 這個與我們單純分析spring源碼的地方進行串聯起來了
		//如果使用AnnotationConfigWebApplicationContext, 這個類繼承了AbstractApplicationContext
		//它自己並沒有實現refresh方法,這裏調用直接使用這個類繼承了AbstractApplicationContext的refresh方法
		wac.refresh();
	}

環境中用到各種Resolver在什麼時候被初始化的?

從上邊關於處理請求的圖中,我們知道在整個web容器中用到很多轉換器,那麼DispathcerServlet是在什麼時候對他們進行初始化的, 其實剛纔我們已經找到了他了,沒錯,就是在webapplicationContext初始化完畢後進行初始化的,看下邊這段代碼,就是在這裏完成的


		/**
		 * 這個地方也很重要
		 */
		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.
			//實際上是去執行dispatcherServlet的初始化方法
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

//-----------------------------------------------------------------
	/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	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);
	}

問題:爲什麼我們自己提供的WebApplicationInitializer能被發現?

剛纔我們提到管用DispatcherServlet中有一個關於this.webApplicationContext != null的判斷,而這個applicationContext是在我們自己提供的WebApplicationInitializer中提供的,那麼我們自己提供的這個類爲什麼會被發現呢,這就要說到 Servlet SPI協議了

什麼是SPI協議,就是在容器的更目錄下提供MEAT-INF/services/javax.servlet.javax.servlet.ServletContainerInitializer文件,然後文件中提供一個類全路徑,那麼在容器啓動時候就會去加載該類,而spring web真是實現了這一協議,實現這一協議的容器在啓動時候會去加載這個類

我們自己提供的WebApplicationInitializer並沒有在文件內部,爲什麼能被加載執行呢?不急接着看。

org.springframework.web.SpringServletContainerInitializer

上邊是Springweb在文件中提供的全路徑,我們看看springweb關於該類的實現,我們看到@HandlesTypes(WebApplicationInitializer.class)這個註解,這個註解很關鍵,只要實現WebApplicationInitializer這個接口的類都會被發現。而且我們自己提供的必須要實現WebApplicationInitializer這個接口。

@HandlesTypes(@HandlesTypes(WebApplicationInitializer.class).class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	/**
	 * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
	 * implementations present on the application classpath.
	 * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
	 * Servlet 3.0+ containers will automatically scan the classpath for implementations
	 * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
	 * such types to the {@code webAppInitializerClasses} parameter of this method.
	 * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
	 * this method is effectively a no-op. An INFO-level log message will be issued notifying
	 * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
	 * no {@code WebApplicationInitializer} implementations were found.
	 * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
	 * they will be instantiated (and <em>sorted</em> if the @{@link
	 * org.springframework.core.annotation.Order @Order} annotation is present or
	 * the {@link org.springframework.core.Ordered Ordered} interface has been
	 * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
	 * method will be invoked on each instance, delegating the {@code ServletContext} such
	 * that each instance may register and configure servlets such as Spring's
	 * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
	 * or any other Servlet API componentry such as filters.
	 * @param webAppInitializerClasses all implementations of
	 * {@link WebApplicationInitializer} found on the application classpath
	 * @param servletContext the servlet context to be initialized
	 * @see WebApplicationInitializer#onStartup(ServletContext)
	 * @see AnnotationAwareOrderComparator
	 */
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}
}

問題:@RequestMapping標註的方法什麼時候解析的

我們知道請求映射關係使用hanlerMapping進行處理的,每個請求對應應該調用我們的哪個方法都是由handlerMapping進行處理的,spring默認提供到了三個handlerMapping,分別是

  • RequestMappingHandlerMapping
  • BeanNameUrlHandlerMapping
  • SimpleUrlHandlerMapping

這裏我們只分析RequestMappingHandlerMapping,其他的後續在分析

其實在這裏問題又來了,spring怎麼知道有這三個handlerMapping,這就要說到@EnableWebMvc註解,我們看到這個註解引入DelegatingWebMvcConfiguration類,這個類非常關鍵,我們看看他的父類WebMvcConfigurationSupport,這就知道了RequestMappingHandlerMapping是怎麼來的了

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

//WebMvcConfigurationSupport 靜態代碼塊
	static {
		ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
		romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
		jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
				ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
		jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
		jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
		jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
		jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
	}

@Bean
	public RequestMappingHandlerMapping requestMappingHandlerMapping(
			ContentNegotiationManager mvcContentNegotiationManager,
			FormattingConversionService mvcConversionService,
			ResourceUrlProvider mvcResourceUrlProvider) {
		RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
		mapping.setOrder(0);
		mapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
		mapping.setContentNegotiationManager(mvcContentNegotiationManager);
		mapping.setCorsConfigurations(getCorsConfigurations());

		PathMatchConfigurer configurer = getPathMatchConfigurer();

		Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
		if (useSuffixPatternMatch != null) {
			mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
		}
		Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
		if (useRegisteredSuffixPatternMatch != null) {
			mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
		}
		Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
		if (useTrailingSlashMatch != null) {
			mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
		}

		UrlPathHelper pathHelper = configurer.getUrlPathHelper();
		if (pathHelper != null) {
			mapping.setUrlPathHelper(pathHelper);
		}
		PathMatcher pathMatcher = configurer.getPathMatcher();
		if (pathMatcher != null) {
			mapping.setPathMatcher(pathMatcher);
		}
		Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
		if (pathPrefixes != null) {
			mapping.setPathPrefixes(pathPrefixes);
		}

		return mapping;
	}

這裏我們知道了RequestMappingHandlerMapping這個類的由來,那麼迴歸正題,這和@RequestMapping有什麼關係,不急,我們接着看

RequestMappingHandlerMapping 實現了 InitializingBean方法,而關於@RequestMapping的處理也正是通過這個接口的afterPropertiesSet()進行處理的

這個過程非常複雜,最終我們找到如下兩個方法:這個兩個方法纔是正在進行判斷處理的, 當然, 這裏邊還有很多其他判斷,這裏就不說了,感興趣可以自己跟一下源碼

	/**
	 * Uses method and type-level @{@link RequestMapping} annotations to create
	 * the RequestMappingInfo.
	 * 檢測 handler method 方法的實際操作,這個地方是給出已經檢測出的方法(method),然後將轉換爲
	 * RequestMappingInfo
	 * @return the created RequestMappingInfo, or {@code null} if the method
	 * does not have a {@code @RequestMapping} annotation.
	 * @see #getCustomMethodCondition(Method)
	 * @see #getCustomTypeCondition(Class)
	 */
	@Override
	@Nullable
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		//判斷傳入的方法是否標註了@RequestMapping註解
		//如果標註了這個註解, 那麼這個方法將會被封裝成RequestMappingInfo
		//否則這裏放回的是null, 並且這裏的RequestMappingInfo就實現RequestCondition接口
		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).build().combine(info);
			}
		}
		return info;
	}



		@Nullable
	private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);

		//做條件處理,但是這裏條件返回都是null
		//CompositeRequestCondition 可以提供多條件組合判斷
		//這裏的條件應用在類上,方法級別沒有
		RequestCondition<?> condition = (element instanceof Class ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}

其實還有問題, 我們知道這兩個方法是最終處理的, 但是spring又是怎麼在初始化的時候調到這兩個方法的, 剛纔說了是通過InitializingBean接口的afterPropertiesSet()方法實現的?

讀過Spring refresh()方法的應該知道, 在bean實例化後,會執行屬性裝配,然後就會執行invokeInitMethods,也就是這個方法去調用了InitialingBean的afterPropertiesSet()方法,

這也就說清楚了@RequestMapping處理的地方,這個其實與Bean的生命週期回調有關,關於生命週期回調可以查看【

	try {
			//這裏執行實現InitializingBean接口的方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}


protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		//判斷bean是否實現了InitializingBean
		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						//具體的方法調用
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				//具體執行InitializingBean的方法調用afterPropertiesSet
				//需要注意的是,在查找Spring webmvc @requestMapping方法匹配的時候, 找了很久
				//最後發現請求到DispatcherServlet,到doDispatch(),然後裏邊的請求交給
				//HandlerMapping處理, 然後在spring內部有RequestMappingHandlerMapping這樣一個類
				//這個類實現了InitilizingBean,所以在這裏會執行RequestMappingHandlerMapping的afterPropertiesSet()方法
				//還有一點沒有講到, 在RequestMappingHandlerMapping中,映射方法數據存放在mappingRegistry 集合中,
				//所以在這個方法裏邊會去查找所有的@RequestMapping方法
				((InitializingBean) bean).afterPropertiesSet();
			}
		}
發佈了113 篇原創文章 · 獲贊 62 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章