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.context.support; 018 019import java.io.IOException; 020 021import groovy.lang.GroovyObject; 022import groovy.lang.GroovySystem; 023import groovy.lang.MetaClass; 024 025import org.springframework.beans.BeanWrapper; 026import org.springframework.beans.BeanWrapperImpl; 027import org.springframework.beans.BeansException; 028import org.springframework.beans.factory.NoSuchBeanDefinitionException; 029import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; 030import org.springframework.beans.factory.support.DefaultListableBeanFactory; 031import org.springframework.lang.Nullable; 032 033/** 034 * {@link org.springframework.web.context.WebApplicationContext} implementation which takes 035 * its configuration from Groovy bean definition scripts and/or XML files, as understood by 036 * a {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader}. 037 * This is essentially the equivalent of 038 * {@link org.springframework.context.support.GenericGroovyApplicationContext} 039 * for a web environment. 040 * 041 * <p>By default, the configuration will be taken from "/WEB-INF/applicationContext.groovy" 042 * for the root context, and "/WEB-INF/test-servlet.groovy" for a context with the namespace 043 * "test-servlet" (like for a DispatcherServlet instance with the servlet-name "test"). 044 * 045 * <p>The config location defaults can be overridden via the "contextConfigLocation" 046 * context-param of {@link org.springframework.web.context.ContextLoader} and servlet 047 * init-param of {@link org.springframework.web.servlet.FrameworkServlet}. Config locations 048 * can either denote concrete files like "/WEB-INF/context.groovy" or Ant-style patterns 049 * like "/WEB-INF/*-context.groovy" (see {@link org.springframework.util.PathMatcher} 050 * javadoc for pattern details). Note that ".xml" files will be parsed as XML content; 051 * all other kinds of resources will be parsed as Groovy scripts. 052 * 053 * <p>Note: In case of multiple config locations, later bean definitions will 054 * override ones defined in earlier loaded files. This can be leveraged to 055 * deliberately override certain bean definitions via an extra Groovy script. 056 * 057 * <p><b>For a WebApplicationContext that reads in a different bean definition format, 058 * create an analogous subclass of {@link AbstractRefreshableWebApplicationContext}.</b> 059 * Such a context implementation can be specified as "contextClass" context-param 060 * for ContextLoader or "contextClass" init-param for FrameworkServlet. 061 * 062 * @author Juergen Hoeller 063 * @since 4.1 064 * @see #setNamespace 065 * @see #setConfigLocations 066 * @see org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader 067 * @see org.springframework.web.context.ContextLoader#initWebApplicationContext 068 * @see org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext 069 */ 070public class GroovyWebApplicationContext extends AbstractRefreshableWebApplicationContext implements GroovyObject { 071 072 /** Default config location for the root context. */ 073 public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.groovy"; 074 075 /** Default prefix for building a config location for a namespace. */ 076 public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; 077 078 /** Default suffix for building a config location for a namespace. */ 079 public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".groovy"; 080 081 082 private final BeanWrapper contextWrapper = new BeanWrapperImpl(this); 083 084 private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass()); 085 086 087 /** 088 * Loads the bean definitions via an GroovyBeanDefinitionReader. 089 * @see org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader 090 * @see #initBeanDefinitionReader 091 * @see #loadBeanDefinitions 092 */ 093 @Override 094 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 095 // Create a new XmlBeanDefinitionReader for the given BeanFactory. 096 GroovyBeanDefinitionReader beanDefinitionReader = new GroovyBeanDefinitionReader(beanFactory); 097 098 // Configure the bean definition reader with this context's 099 // resource loading environment. 100 beanDefinitionReader.setEnvironment(getEnvironment()); 101 beanDefinitionReader.setResourceLoader(this); 102 103 // Allow a subclass to provide custom initialization of the reader, 104 // then proceed with actually loading the bean definitions. 105 initBeanDefinitionReader(beanDefinitionReader); 106 loadBeanDefinitions(beanDefinitionReader); 107 } 108 109 /** 110 * Initialize the bean definition reader used for loading the bean 111 * definitions of this context. Default implementation is empty. 112 * <p>Can be overridden in subclasses. 113 * @param beanDefinitionReader the bean definition reader used by this context 114 */ 115 protected void initBeanDefinitionReader(GroovyBeanDefinitionReader beanDefinitionReader) { 116 } 117 118 /** 119 * Load the bean definitions with the given GroovyBeanDefinitionReader. 120 * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method; 121 * therefore this method is just supposed to load and/or register bean definitions. 122 * <p>Delegates to a ResourcePatternResolver for resolving location patterns 123 * into Resource instances. 124 * @throws IOException if the required Groovy script or XML file isn't found 125 * @see #refreshBeanFactory 126 * @see #getConfigLocations 127 * @see #getResources 128 * @see #getResourcePatternResolver 129 */ 130 protected void loadBeanDefinitions(GroovyBeanDefinitionReader reader) throws IOException { 131 String[] configLocations = getConfigLocations(); 132 if (configLocations != null) { 133 for (String configLocation : configLocations) { 134 reader.loadBeanDefinitions(configLocation); 135 } 136 } 137 } 138 139 /** 140 * The default location for the root context is "/WEB-INF/applicationContext.groovy", 141 * and "/WEB-INF/test-servlet.groovy" for a context with the namespace "test-servlet" 142 * (like for a DispatcherServlet instance with the servlet-name "test"). 143 */ 144 @Override 145 protected String[] getDefaultConfigLocations() { 146 if (getNamespace() != null) { 147 return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; 148 } 149 else { 150 return new String[] {DEFAULT_CONFIG_LOCATION}; 151 } 152 } 153 154 155 // Implementation of the GroovyObject interface 156 157 @Override 158 public void setMetaClass(MetaClass metaClass) { 159 this.metaClass = metaClass; 160 } 161 162 @Override 163 public MetaClass getMetaClass() { 164 return this.metaClass; 165 } 166 167 @Override 168 public Object invokeMethod(String name, Object args) { 169 return this.metaClass.invokeMethod(this, name, args); 170 } 171 172 @Override 173 public void setProperty(String property, Object newValue) { 174 this.metaClass.setProperty(this, property, newValue); 175 } 176 177 @Override 178 @Nullable 179 public Object getProperty(String property) { 180 if (containsBean(property)) { 181 return getBean(property); 182 } 183 else if (this.contextWrapper.isReadableProperty(property)) { 184 return this.contextWrapper.getPropertyValue(property); 185 } 186 throw new NoSuchBeanDefinitionException(property); 187 } 188 189}