概述
本章再学习另外两个ViewResolver,分别是XmlViewResolver和ResourceBundleViewResolver,从功能上说,这两个视图解析器都是从外部资源文件中查找视图View对象,所以放在一章学习。
本系列文章是基于Spring5.0.5RELEASE。
XmlViewResolver
该类继承AbstractCachingViewResolver抽象类,也就是该解析器支持视图缓存。XmlViewResolver通过使用额外的xml配置文件来定义视图对象,xml配置文件默认加载/WEB-INF/views.xml,可通过location属性参数重置加载文件。
配置文件中定义视图对象,并指定bean名称(id或name),该名称与Controller处理器中返回的逻辑视图名称对应,从而通过url指定的路径找到真正的视图进行渲染。
源码如下:
public class XmlViewResolver extends AbstractCachingViewResolver
implements Ordered, InitializingBean, DisposableBean {
/** 默认加载的视图配置文件 */
public static final String DEFAULT_LOCATION = "/WEB-INF/views.xml";
/** 指定视图配置文件路径 */
@Nullable
private Resource location;
/** 如果开启缓存(cacheLimit>0),bean工厂缓存在该属性 */
@Nullable
private ConfigurableApplicationContext cachedFactory;
/** 视图解析器排序 */
private int order = Ordered.LOWEST_PRECEDENCE;
... 省略get/set方法 ...
/** 启动时调用 */
@Override
public void afterPropertiesSet() throws BeansException {
// 开启缓存(cacheLimit>0)时,在应用启动时创建bean工厂
if (isCache()) {
initFactory();
}
}
/** 返回视图名称,在父类AbstractCachingViewResolver的resolveViewName方法中调用 */
@Override
protected Object getCacheKey(String viewName, Locale locale) {
return viewName;
}
/**
*根据视图名称加载View视图
*/
@Override
protected View loadView(String viewName, Locale locale) throws BeansException {
// 创建bean工厂
BeanFactory factory = initFactory();
try {
// 根据controller返回的逻辑视图名(视图名称与bean名称对应)查找视图对象
return factory.getBean(viewName, View.class);
}
catch (NoSuchBeanDefinitionException ex) {
// Allow for ViewResolver chaining...
return null;
}
}
/** 创建bean工厂 */
protected synchronized BeanFactory initFactory() throws BeansException {
// 如果启用缓存,第二次直接返回
if (this.cachedFactory != null) {
return this.cachedFactory;
}
ApplicationContext applicationContext = obtainApplicationContext();
Resource actualLocation = this.location;
if (actualLocation == null) {
actualLocation = applicationContext.getResource(DEFAULT_LOCATION);
}
// Create child ApplicationContext for views.
GenericWebApplicationContext factory = new GenericWebApplicationContext();
factory.setParent(applicationContext);
factory.setServletContext(getServletContext());
// Load XML resource with context-aware entity resolver.
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.setEnvironment(applicationContext.getEnvironment());
reader.setEntityResolver(new ResourceEntityResolver(applicationContext));
reader.loadBeanDefinitions(actualLocation);
factory.refresh();
// 启用缓存,赋值属性变量进行存储
if (isCache()) {
this.cachedFactory = factory;
}
return factory;
}
}
以上是XmlViewResolver的核心代码。
ResourceBundleViewResolver
与XmlViewResolver一样,该类继承AbstractCachingViewResolver抽象类,并且通过外部的属性文件定义逻辑视图名称与真正的视图View对象的关系,属性文件默认是classpath下的views.properties,可以通过basename或basenames属性来指定,该属性指的是文件的基名称,也就是说以basename属性值开头的属性文件。
ResourceBundleViewResolver类具有缓存功能,即把 properties 文件中定义好的属性按照它自身的规则生成一个个的 bean 对象注册到该 BeanFactory 中,之后会把该 BeanFactory 对象保存起来,所以 ResourceBundleViewResolver 缓存的是 BeanFactory ,而不是直接的缓存从 BeanFactory 中取出的视图 bean。
Spring 通过 properties 文件生成 bean 的规则是把 properties 文件中定义的属性名称按最后一个点“ . ”进行分割,把点前面的内容当做是 bean 名称,点后面的内容当做是 bean 的属性。
源码如下:
public class ResourceBundleViewResolver extends AbstractCachingViewResolver
implements Ordered, InitializingBean, DisposableBean {
/** 配置文件的默认基名称,即以此开头的属性文件,默认从classpath路径查找加载 */
public static final String DEFAULT_BASENAME = "views";
/** 支持多文件加载 */
private String[] basenames = new String[] {DEFAULT_BASENAME};
private ClassLoader bundleClassLoader = Thread.currentThread().getContextClassLoader();
@Nullable
private String defaultParentView;
@Nullable
private Locale[] localesToInitialize;
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
/* Locale -> BeanFactory */
private final Map<Locale, BeanFactory> localeCache = new HashMap<>();
/* List of ResourceBundle -> BeanFactory */
private final Map<List<ResourceBundle>, ConfigurableApplicationContext> bundleCache = new HashMap<>();
/**********get/set**********/
public void setBasename(String basename) {
setBasenames(basename);
}
public void setBasenames(String... basenames) {
this.basenames = basenames;
}
public void setBundleClassLoader(ClassLoader classLoader) {
this.bundleClassLoader = classLoader;
}
protected ClassLoader getBundleClassLoader() {
return this.bundleClassLoader;
}
public void setDefaultParentView(String defaultParentView) {
this.defaultParentView = defaultParentView;
}
public void setLocalesToInitialize(Locale... localesToInitialize) {
this.localesToInitialize = localesToInitialize;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
/** 启动时调用,创建初始化bean工厂 */
@Override
public void afterPropertiesSet() throws BeansException {
// localesToInitialize属性通过配置进行设置
if (this.localesToInitialize != null) {
for (Locale locale : this.localesToInitialize) {
initFactory(locale);
}
}
}
/** 查找视图View,在父类AbstractCachingViewResolver的resolverViewName方法中调用 */
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
// 初始化bean工厂
BeanFactory factory = initFactory(locale);
try {
return factory.getBean(viewName, View.class);
}
catch (NoSuchBeanDefinitionException ex) {
// Allow for ViewResolver chaining...
return null;
}
}
protected synchronized BeanFactory initFactory(Locale locale) throws BeansException {
// 开启缓存,通过cacheLimit属性大于0控制
if (isCache()) {
// localeCache属性map中缓存locale和beanFactory映射
BeanFactory cachedFactory = this.localeCache.get(locale);
if (cachedFactory != null) {
return cachedFactory;
}
}
// 创建ResourceBundle集合,支持多属性文件
List<ResourceBundle> bundles = new LinkedList<>();
for (String basename : this.basenames) {
ResourceBundle bundle = getBundle(basename, locale);
bundles.add(bundle);
}
// 开启缓存,
if (isCache()) {
BeanFactory cachedFactory = this.bundleCache.get(bundles);
if (cachedFactory != null) {
this.localeCache.put(locale, cachedFactory);
return cachedFactory;
}
}
// 创建视图ApplicationContext上下文
GenericWebApplicationContext factory = new GenericWebApplicationContext();
factory.setParent(getApplicationContext());
factory.setServletContext(getServletContext());
// 从资源文件中加载bean定义
PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory);
reader.setDefaultParentBean(this.defaultParentView);
for (ResourceBundle bundle : bundles) {
reader.registerBeanDefinitions(bundle);
}
factory.refresh();
// 设置缓存
if (isCache()) {
this.localeCache.put(locale, factory);
this.bundleCache.put(bundles, factory);
}
return factory;
}
protected ResourceBundle getBundle(String basename, Locale locale) throws MissingResourceException {
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
}
@Override
public void destroy() throws BeansException {
for (ConfigurableApplicationContext factory : this.bundleCache.values()) {
factory.close();
}
this.localeCache.clear();
this.bundleCache.clear();
}
}
以上是ResourceBundleViewResolver的核心代码。
实战
- 使用XmlViewResolver
spring mvc配置文件中配置XmlViewResolver视图解析器,代码如下:
<!-- XmlViewResolver -->
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location" value="classpath:/views.xml"/>
</bean>
在classpath下创建views.xml配置文件,代码如下:
<bean id="test" class="org.springframework.web.servlet.view.JstlView">
<property name="url" value="/WEB-INF/jsp/test.jsp"/>
</bean>
启动测试,可以正常进行渲染。
- 使用ResourceBundleViewResolver
spring mvc配置文件中配置ResourceBundlerViewResolver视图解析器,代码如下:
<!-- ResourceBundleViewResolver -->
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<!-- 支持多文件 -->
<property name="basenames">
<array>
<!-- 在classpath下创建properties文件夹及相应文件 -->
<value>properties/test</value>
<value>properties/views</value>
</array>
</property>
<!-- 单文件 -->
<!--<property name="basename" value="properties/test"/>-->
<!-- 关闭缓存 -->
<property name="cacheLimit" value="0"/>
</bean>
test.properties配置如下:
// 配置视图View
test.(class)=org.springframework.web.servlet.view.InternalResourceView
// 对应真实视图url
test.url=/WEB-INF/jsp/test.jsp
views.properties配置与test一样。
启动测试,正常解析渲染。
总结
经过六章的分析,学习了ViewResolver视图解析器,回想一下,可划分为三部分:
- 组合的ViewResolver,这部分是直接继承ViewResolver接口的,不具有缓存功能,包括像ViewResolverComposite、ContentNegotiatingViewResolver等
- 基于url的Viewresolver,这部分包括UrlBasedViewResolver、InternalResourceViewResolver等
- 基于外部文件的Viewresolver,包括XmlViewResolver、ResourceBundleViewResolver
关于视图解析器,就分析到这,希望对大家有帮助,谢谢!
最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。