001/* 002 * Copyright 2002-2014 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.context.support; 018 019import groovy.lang.GroovyObject; 020import groovy.lang.GroovySystem; 021import groovy.lang.MetaClass; 022 023import org.springframework.beans.BeanWrapper; 024import org.springframework.beans.BeanWrapperImpl; 025import org.springframework.beans.factory.NoSuchBeanDefinitionException; 026import org.springframework.beans.factory.config.BeanDefinition; 027import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; 028import org.springframework.core.env.ConfigurableEnvironment; 029import org.springframework.core.io.ClassPathResource; 030import org.springframework.core.io.Resource; 031 032/** 033 * An {@link org.springframework.context.ApplicationContext} implementation that extends 034 * {@link GenericApplicationContext} and implements {@link GroovyObject} such that beans 035 * can be retrieved with the dot de-reference syntax instead of using {@link #getBean}. 036 * 037 * <p>Consider this as the equivalent of {@link GenericXmlApplicationContext} for 038 * Groovy bean definitions, or even an upgrade thereof since it seamlessly understands 039 * XML bean definition files as well. The main difference is that, within a Groovy 040 * script, the context can be used with an inline bean definition closure as follows: 041 * 042 * <pre class="code"> 043 * import org.hibernate.SessionFactory 044 * import org.apache.commons.dbcp.BasicDataSource 045 * 046 * def context = new GenericGroovyApplicationContext() 047 * context.reader.beans { 048 * dataSource(BasicDataSource) { // <--- invokeMethod 049 * driverClassName = "org.hsqldb.jdbcDriver" 050 * url = "jdbc:hsqldb:mem:grailsDB" 051 * username = "sa" // <-- setProperty 052 * password = "" 053 * settings = [mynew:"setting"] 054 * } 055 * sessionFactory(SessionFactory) { 056 * dataSource = dataSource // <-- getProperty for retrieving references 057 * } 058 * myService(MyService) { 059 * nestedBean = { AnotherBean bean -> // <-- setProperty with closure for nested bean 060 * dataSource = dataSource 061 * } 062 * } 063 * } 064 * context.refresh() 065 * </pre> 066 * 067 * <p>Alternatively, load a Groovy bean definition script like the following 068 * from an external resource (e.g. an "applicationContext.groovy" file): 069 * 070 * <pre class="code"> 071 * import org.hibernate.SessionFactory 072 * import org.apache.commons.dbcp.BasicDataSource 073 * 074 * beans { 075 * dataSource(BasicDataSource) { 076 * driverClassName = "org.hsqldb.jdbcDriver" 077 * url = "jdbc:hsqldb:mem:grailsDB" 078 * username = "sa" 079 * password = "" 080 * settings = [mynew:"setting"] 081 * } 082 * sessionFactory(SessionFactory) { 083 * dataSource = dataSource 084 * } 085 * myService(MyService) { 086 * nestedBean = { AnotherBean bean -> 087 * dataSource = dataSource 088 * } 089 * } 090 * } 091 * </pre> 092 * 093 * <p>With the following Java code creating the {@code GenericGroovyApplicationContext} 094 * (potentially using Ant-style '*'/'**' location patterns): 095 * 096 * <pre class="code"> 097 * GenericGroovyApplicationContext context = new GenericGroovyApplicationContext(); 098 * context.load("org/myapp/applicationContext.groovy"); 099 * context.refresh(); 100 * </pre> 101 * 102 * <p>Or even more concise, provided that no extra configuration is needed: 103 * 104 * <pre class="code"> 105 * ApplicationContext context = new GenericGroovyApplicationContext("org/myapp/applicationContext.groovy"); 106 * </pre> 107 * 108 * <p><b>This application context also understands XML bean definition files, 109 * allowing for seamless mixing and matching with Groovy bean definition files.</b> 110 * ".xml" files will be parsed as XML content; all other kinds of resources will 111 * be parsed as Groovy scripts. 112 * 113 * @author Juergen Hoeller 114 * @author Jeff Brown 115 * @since 4.0 116 * @see org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader 117 */ 118public class GenericGroovyApplicationContext extends GenericApplicationContext implements GroovyObject { 119 120 private final GroovyBeanDefinitionReader reader = new GroovyBeanDefinitionReader(this); 121 122 private final BeanWrapper contextWrapper = new BeanWrapperImpl(this); 123 124 private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass()); 125 126 127 /** 128 * Create a new GenericGroovyApplicationContext that needs to be 129 * {@link #load loaded} and then manually {@link #refresh refreshed}. 130 */ 131 public GenericGroovyApplicationContext() { 132 } 133 134 /** 135 * Create a new GenericGroovyApplicationContext, loading bean definitions 136 * from the given resources and automatically refreshing the context. 137 * @param resources the resources to load from 138 */ 139 public GenericGroovyApplicationContext(Resource... resources) { 140 load(resources); 141 refresh(); 142 } 143 144 /** 145 * Create a new GenericGroovyApplicationContext, loading bean definitions 146 * from the given resource locations and automatically refreshing the context. 147 * @param resourceLocations the resources to load from 148 */ 149 public GenericGroovyApplicationContext(String... resourceLocations) { 150 load(resourceLocations); 151 refresh(); 152 } 153 154 /** 155 * Create a new GenericGroovyApplicationContext, loading bean definitions 156 * from the given resource locations and automatically refreshing the context. 157 * @param relativeClass class whose package will be used as a prefix when 158 * loading each specified resource name 159 * @param resourceNames relatively-qualified names of resources to load 160 */ 161 public GenericGroovyApplicationContext(Class<?> relativeClass, String... resourceNames) { 162 load(relativeClass, resourceNames); 163 refresh(); 164 } 165 166 167 /** 168 * Exposes the underlying {@link GroovyBeanDefinitionReader} for convenient access 169 * to the {@code loadBeanDefinition} methods on it as well as the ability 170 * to specify an inline Groovy bean definition closure. 171 * @see GroovyBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...) 172 * @see GroovyBeanDefinitionReader#loadBeanDefinitions(String...) 173 */ 174 public final GroovyBeanDefinitionReader getReader() { 175 return this.reader; 176 } 177 178 /** 179 * Delegates the given environment to underlying {@link GroovyBeanDefinitionReader}. 180 * Should be called before any call to {@code #load}. 181 */ 182 @Override 183 public void setEnvironment(ConfigurableEnvironment environment) { 184 super.setEnvironment(environment); 185 this.reader.setEnvironment(getEnvironment()); 186 } 187 188 /** 189 * Load bean definitions from the given Groovy scripts or XML files. 190 * <p>Note that ".xml" files will be parsed as XML content; all other kinds 191 * of resources will be parsed as Groovy scripts. 192 * @param resources one or more resources to load from 193 */ 194 public void load(Resource... resources) { 195 this.reader.loadBeanDefinitions(resources); 196 } 197 198 /** 199 * Load bean definitions from the given Groovy scripts or XML files. 200 * <p>Note that ".xml" files will be parsed as XML content; all other kinds 201 * of resources will be parsed as Groovy scripts. 202 * @param resourceLocations one or more resource locations to load from 203 */ 204 public void load(String... resourceLocations) { 205 this.reader.loadBeanDefinitions(resourceLocations); 206 } 207 208 /** 209 * Load bean definitions from the given Groovy scripts or XML files. 210 * <p>Note that ".xml" files will be parsed as XML content; all other kinds 211 * of resources will be parsed as Groovy scripts. 212 * @param relativeClass class whose package will be used as a prefix when 213 * loading each specified resource name 214 * @param resourceNames relatively-qualified names of resources to load 215 */ 216 public void load(Class<?> relativeClass, String... resourceNames) { 217 Resource[] resources = new Resource[resourceNames.length]; 218 for (int i = 0; i < resourceNames.length; i++) { 219 resources[i] = new ClassPathResource(resourceNames[i], relativeClass); 220 } 221 load(resources); 222 } 223 224 225 // Implementation of the GroovyObject interface 226 227 public void setMetaClass(MetaClass metaClass) { 228 this.metaClass = metaClass; 229 } 230 231 public MetaClass getMetaClass() { 232 return this.metaClass; 233 } 234 235 public Object invokeMethod(String name, Object args) { 236 return this.metaClass.invokeMethod(this, name, args); 237 } 238 239 public void setProperty(String property, Object newValue) { 240 if (newValue instanceof BeanDefinition) { 241 registerBeanDefinition(property, (BeanDefinition) newValue); 242 } 243 else { 244 this.metaClass.setProperty(this, property, newValue); 245 } 246 } 247 248 public Object getProperty(String property) { 249 if (containsBean(property)) { 250 return getBean(property); 251 } 252 else if (this.contextWrapper.isReadableProperty(property)) { 253 return this.contextWrapper.getPropertyValue(property); 254 } 255 throw new NoSuchBeanDefinitionException(property); 256 } 257 258}