一、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类型的变量。
- 先判断defaultStrategies是不是为空,如果为空,则把DispatcherServlet.properties中的内容加载到defaultStrategies中
- 根据入参ViewResolver.class的名称,获取defaultStrategies的值,获取的值就是"org.springframework.web.servlet.view.InternalResourceViewResolver"
- 然后就根据上面获取的全路径名创建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属性。