001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.view; 018 019import java.util.Locale; 020 021import org.springframework.beans.BeansException; 022import org.springframework.beans.factory.BeanFactory; 023import org.springframework.beans.factory.DisposableBean; 024import org.springframework.beans.factory.InitializingBean; 025import org.springframework.beans.factory.NoSuchBeanDefinitionException; 026import org.springframework.beans.factory.xml.ResourceEntityResolver; 027import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; 028import org.springframework.context.ConfigurableApplicationContext; 029import org.springframework.core.Ordered; 030import org.springframework.core.io.Resource; 031import org.springframework.web.context.support.GenericWebApplicationContext; 032import org.springframework.web.servlet.View; 033 034/** 035 * A {@link org.springframework.web.servlet.ViewResolver} implementation that uses 036 * bean definitions in a dedicated XML file for view definitions, specified by 037 * resource location. The file will typically be located in the WEB-INF directory; 038 * the default is "/WEB-INF/views.xml". 039 * 040 * <p>This {@code ViewResolver} does not support internationalization at the level 041 * of its definition resources. Consider {@link ResourceBundleViewResolver} if you 042 * need to apply different view resources per locale. 043 * 044 * <p>Note: This {@code ViewResolver} implements the {@link Ordered} interface 045 * in order to allow for flexible participation in {@code ViewResolver} chaining. 046 * For example, some special views could be defined via this {@code ViewResolver} 047 * (giving it 0 as "order" value), while all remaining views could be resolved by 048 * a {@link UrlBasedViewResolver}. 049 * 050 * @author Juergen Hoeller 051 * @since 18.06.2003 052 * @see org.springframework.context.ApplicationContext#getResource 053 * @see ResourceBundleViewResolver 054 * @see UrlBasedViewResolver 055 */ 056public class XmlViewResolver extends AbstractCachingViewResolver 057 implements Ordered, InitializingBean, DisposableBean { 058 059 /** Default if no other location is supplied */ 060 public static final String DEFAULT_LOCATION = "/WEB-INF/views.xml"; 061 062 063 private Resource location; 064 065 private ConfigurableApplicationContext cachedFactory; 066 067 private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered 068 069 070 /** 071 * Set the location of the XML file that defines the view beans. 072 * <p>The default is "/WEB-INF/views.xml". 073 * @param location the location of the XML file. 074 */ 075 public void setLocation(Resource location) { 076 this.location = location; 077 } 078 079 /** 080 * Specify the order value for this ViewResolver bean. 081 * <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered. 082 * @see org.springframework.core.Ordered#getOrder() 083 */ 084 public void setOrder(int order) { 085 this.order = order; 086 } 087 088 @Override 089 public int getOrder() { 090 return this.order; 091 } 092 093 /** 094 * Pre-initialize the factory from the XML file. 095 * Only effective if caching is enabled. 096 */ 097 @Override 098 public void afterPropertiesSet() throws BeansException { 099 if (isCache()) { 100 initFactory(); 101 } 102 } 103 104 105 /** 106 * This implementation returns just the view name, 107 * as XmlViewResolver doesn't support localized resolution. 108 */ 109 @Override 110 protected Object getCacheKey(String viewName, Locale locale) { 111 return viewName; 112 } 113 114 @Override 115 protected View loadView(String viewName, Locale locale) throws BeansException { 116 BeanFactory factory = initFactory(); 117 try { 118 return factory.getBean(viewName, View.class); 119 } 120 catch (NoSuchBeanDefinitionException ex) { 121 // Allow for ViewResolver chaining... 122 return null; 123 } 124 } 125 126 /** 127 * Initialize the view bean factory from the XML file. 128 * Synchronized because of access by parallel threads. 129 * @throws BeansException in case of initialization errors 130 */ 131 protected synchronized BeanFactory initFactory() throws BeansException { 132 if (this.cachedFactory != null) { 133 return this.cachedFactory; 134 } 135 136 Resource actualLocation = this.location; 137 if (actualLocation == null) { 138 actualLocation = getApplicationContext().getResource(DEFAULT_LOCATION); 139 } 140 141 // Create child ApplicationContext for views. 142 GenericWebApplicationContext factory = new GenericWebApplicationContext(); 143 factory.setParent(getApplicationContext()); 144 factory.setServletContext(getServletContext()); 145 146 // Load XML resource with context-aware entity resolver. 147 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); 148 reader.setEnvironment(getApplicationContext().getEnvironment()); 149 reader.setEntityResolver(new ResourceEntityResolver(getApplicationContext())); 150 reader.loadBeanDefinitions(actualLocation); 151 152 factory.refresh(); 153 154 if (isCache()) { 155 this.cachedFactory = factory; 156 } 157 return factory; 158 } 159 160 161 /** 162 * Close the view bean factory on context shutdown. 163 */ 164 @Override 165 public void destroy() throws BeansException { 166 if (this.cachedFactory != null) { 167 this.cachedFactory.close(); 168 } 169 } 170 171}