SpringMVC 学习 十六 中初始化视图解析器过程

一、SpringMVC简单配置

我们现在看一段SpringMVC的配置代码,在这段SpringMVC配置文件中,简单配置一些信息,不配置视图解析器。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven></mvc:annotation-driven>
    <context:component-scan base-package="com.*"></context:component-scan>



    <!-- <mvc:resources mapping="" location=""></mvc:resources>-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>

   


</beans>

 

然后再写一个简单的处理器

@RequestMapping("/param02")
    public String param02(User user){
        System.out.println(user);
        System.out.println(user.getPermission().size());
        System.out.println(user.getIds().length);
        return "index.jsp";
    }

我们发现SpringMVC是可以去项目根目录下找index.jsp文件,但是我们并没有再SpringMVC配置文件中配置VeiwResolver。这是什么原因呢?

先说结论:是因为在spring-webmvc包里面有一个配置文件DispatcherServlet.properties,这个配置文件中有SpringMVC 的DispatcherServlet中需要的一些属性。

二、DispatcherServlet.properties

DispatcherServlet.properties文件内容如下:

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 

 在这个配置文件中配置了DispatcherServlet所需要的所有组件

三、DispatcherServlet需要初始化的组件

在DispatcherServlet中源码中,DispatcherServlet类中的属性包含了这个前端控制器所需要的视图解析器等组件


	/** MultipartResolver used by this servlet. */
	@Nullable
	private MultipartResolver multipartResolver;

	/** LocaleResolver used by this servlet. */
	@Nullable
	private LocaleResolver localeResolver;

	/** ThemeResolver used by this servlet. */
	@Nullable
	private ThemeResolver themeResolver;

	/** List of HandlerMappings used by this servlet. */
	@Nullable
	private List<HandlerMapping> handlerMappings;

	/** List of HandlerAdapters used by this servlet. */
	@Nullable
	private List<HandlerAdapter> handlerAdapters;

	/** List of HandlerExceptionResolvers used by this servlet. */
	@Nullable
	private List<HandlerExceptionResolver> handlerExceptionResolvers;

	/** RequestToViewNameTranslator used by this servlet. */
	@Nullable
	private RequestToViewNameTranslator viewNameTranslator;

	/** FlashMapManager used by this servlet. */
	@Nullable
	private FlashMapManager flashMapManager;

	/** List of ViewResolvers used by this servlet. */
	@Nullable
	private List<ViewResolver> viewResolvers;

 

四、初始化视图解析器

 

initStrategies方法

 这写组件的初始化在DispatcherServlet的initStrategies方法中


	/**
	 * 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);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

 

initViewResolvers方法

DispatcherServlet需要初始化的内容太多,这里只对视图解析的初始化过程进行分析。视图解析的初始化是在initViewResolvers方法中进行的

/**
	
     初始化DispatcherServlet使用的视图解析器,如果在BeanFactory中没有视图解析器,则把视图解析器默认设置成InternalResourceViewResolver
     
	 */
	private void initViewResolvers(ApplicationContext context) {
		this.viewResolvers = null;

		// 当detectAllViewResolvers的默认值为true,就是找出所有的视图解析器,
		if (this.detectAllViewResolvers) {
			// 在ApplicationContext中查找所有ViewResolver,包括祖先上下文。
            //如果在SpringMVC的配置文件以及Spring的配置文件中都没有配置,则在两个上下文中都找不到视图解析器
			Map<String, ViewResolver> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.viewResolvers = new ArrayList<>(matchingBeans.values());
				// We keep ViewResolvers in sorted order.
				AnnotationAwareOrderComparator.sort(this.viewResolvers);
			}
		}else {
        	//当detectAllViewResolvers为false时, 
			try {
				ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
				this.viewResolvers = Collections.singletonList(vr);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default ViewResolver later.
			}
		}

		/**
        Ensure we have at least one ViewResolver, by registering
		 a default ViewResolver if no other resolvers are found.
         要确保必须最少有一个视图解析器,如果在SpringMVC上下文以及Spring上下文中都没有,则调用getDefaultStrategies
         创建默认的视图解析器
         */
		if (this.viewResolvers == null) {
			this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

 

getDefaultStrategies方法

getDefaultStrategies方法会返回默认的视图解析器


	/**
	 * Create a List of default strategy objects for the given strategy interface.
	 * <p>The default implementation uses the "DispatcherServlet.properties" file (in the same
	 * package as the DispatcherServlet class) to determine the class names. It instantiates
	 * the strategy objects through the context's BeanFactory.
	 * @param context the current WebApplicationContext
	 * @param strategyInterface the strategy interface
	 * @return the List of corresponding strategy objects
	 */
	@SuppressWarnings("unchecked")
	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		if (defaultStrategies == null) {
			try {
				// Load default strategy implementations from properties file.
				// This is currently strictly internal and not meant to be customized
				// by application developers.
				ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
				defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
			}
			catch (IOException ex) {
				throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
			}
		}

		String key = strategyInterface.getName();
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<>(classNames.length);
			for (String className : classNames) {
				try {
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					Object strategy = createDefaultStrategy(context, clazz);
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (LinkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return Collections.emptyList();
		}
	}

 

 

上面的代码的意思是:

注意通过initViewResolvers 调用getDefaultStrategies,传递过来的参数是WebApplicationContext类型的变量和一个ViewResolver.class类型的变量。

  

  1. 先判断defaultStrategies是不是为空,如果为空,则把DispatcherServlet.properties中的内容加载到defaultStrategies中
  2. 根据入参ViewResolver.class的名称,获取defaultStrategies的值,获取的值就是"org.springframework.web.servlet.view.InternalResourceViewResolver"
  3. 然后就根据上面获取的全路径名创建InternalResourceViewResolver实例,并放入一个列表中返回。

 

createDefaultStrategy方法

在getDefaultStrategies中调用createDefaultStrategy来创建需要的视图解析器,

createDefaultStrategy方法中通过AutowireCapableBeanFactory接口的实现类AbstractAutowireCapableBeanFactory中的createBean方法创建视图解析器。

	protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
		return context.getAutowireCapableBeanFactory().createBean(clazz);
	}

 

createBean方法

	//-------------------------------------------------------------------------
	// Typical methods for creating and populating external bean instances
	//-------------------------------------------------------------------------

	@Override
	@SuppressWarnings("unchecked")
	public <T> T createBean(Class<T> beanClass) throws BeansException {
		// Use prototype bean definition, to avoid registering bean as dependent bean.
		RootBeanDefinition bd = new RootBeanDefinition(beanClass);
		bd.setScope(SCOPE_PROTOTYPE);
		bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
		return (T) createBean(beanClass.getName(), bd, null);
	}

 

就如注释中所说的,这个createBean是用来建和填充外部bean实例的,我们从代码中也可以看到,这个方法创建的bean的作用域都是prototype的。是不会注入到Spring容器中的。

 

五、总结

DispatcherServlet初始化视图解析器的过程如下:

首选从SpringMVC容器或者Spring容器中获取,如果获取到了就直接把获取到的视图解析器赋值给DispatcherServlet的viewResolvers属性。

如果获取不到就根据配置文件DispatcherServlet.properties配置的视图解析器来创建,创建好的视图解析器不会注册到Spring容器,会直接返回,然后赋值给DispatcherServlet的viewResolvers属性。

posted @ 2021-10-17 21:49  阿瞒123  阅读(61)  评论(0)    收藏  举报