zoukankan      html  css  js  c++  java
  • 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属性。

  • 相关阅读:
    如何使用Flexbox和CSS Grid,实现高效布局
    最常用的四种大数据分析方法
    如何编写更好的SQL查询:终极指南-第三部分
    如何编写更好的SQL查询:终极指南-第二部分
    如何编写更好的SQL查询:终极指南-第一部分
    ES2017异步函数现已正式可用
    相对传统桌面设计器,在线报表设计器价值何在?
    如何实现报表设计中的高精度报表套打?
    2017年前端开发工具趋势
    Angular2 VS Angular4 深度对比:特性、性能
  • 原文地址:https://www.cnblogs.com/cplinux/p/15418352.html
Copyright © 2011-2022 走看看