View Javadoc
1   /*
2    * Copyright 2002-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.scripting.support;
18  
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.Map;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  
26  import org.springframework.aop.TargetSource;
27  import org.springframework.aop.framework.AopInfrastructureBean;
28  import org.springframework.aop.framework.ProxyFactory;
29  import org.springframework.aop.support.DelegatingIntroductionInterceptor;
30  import org.springframework.asm.Type;
31  import org.springframework.beans.BeanUtils;
32  import org.springframework.beans.PropertyValue;
33  import org.springframework.beans.factory.BeanClassLoaderAware;
34  import org.springframework.beans.factory.BeanCreationException;
35  import org.springframework.beans.factory.BeanCurrentlyInCreationException;
36  import org.springframework.beans.factory.BeanDefinitionStoreException;
37  import org.springframework.beans.factory.BeanFactory;
38  import org.springframework.beans.factory.BeanFactoryAware;
39  import org.springframework.beans.factory.DisposableBean;
40  import org.springframework.beans.factory.FactoryBean;
41  import org.springframework.beans.factory.config.BeanDefinition;
42  import org.springframework.beans.factory.config.BeanPostProcessor;
43  import org.springframework.beans.factory.config.ConfigurableBeanFactory;
44  import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
45  import org.springframework.beans.factory.support.AbstractBeanDefinition;
46  import org.springframework.beans.factory.support.BeanDefinitionValidationException;
47  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
48  import org.springframework.beans.factory.support.GenericBeanDefinition;
49  import org.springframework.cglib.core.Signature;
50  import org.springframework.cglib.proxy.InterfaceMaker;
51  import org.springframework.context.ResourceLoaderAware;
52  import org.springframework.core.Conventions;
53  import org.springframework.core.Ordered;
54  import org.springframework.core.io.DefaultResourceLoader;
55  import org.springframework.core.io.ResourceLoader;
56  import org.springframework.scripting.ScriptFactory;
57  import org.springframework.scripting.ScriptSource;
58  import org.springframework.util.ClassUtils;
59  import org.springframework.util.ObjectUtils;
60  import org.springframework.util.StringUtils;
61  
62  /**
63   * {@link org.springframework.beans.factory.config.BeanPostProcessor} that
64   * handles {@link org.springframework.scripting.ScriptFactory} definitions,
65   * replacing each factory with the actual scripted Java object generated by it.
66   *
67   * <p>This is similar to the
68   * {@link org.springframework.beans.factory.FactoryBean} mechanism, but is
69   * specifically tailored for scripts and not built into Spring's core
70   * container itself but rather implemented as an extension.
71   *
72   * <p><b>NOTE:</b> The most important characteristic of this post-processor
73   * is that constructor arguments are applied to the
74   * {@link org.springframework.scripting.ScriptFactory} instance
75   * while bean property values are applied to the generated scripted object.
76   * Typically, constructor arguments include a script source locator and
77   * potentially script interfaces, while bean property values include
78   * references and config values to inject into the scripted object itself.
79   *
80   * <p>The following {@link ScriptFactoryPostProcessor} will automatically
81   * be applied to the two
82   * {@link org.springframework.scripting.ScriptFactory} definitions below.
83   * At runtime, the actual scripted objects will be exposed for
84   * "bshMessenger" and "groovyMessenger", rather than the
85   * {@link org.springframework.scripting.ScriptFactory} instances. Both of
86   * those are supposed to be castable to the example's {@code Messenger}
87   * interfaces here.
88   *
89   * <pre class="code">&lt;bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/&gt;
90   *
91   * &lt;bean id="bshMessenger" class="org.springframework.scripting.bsh.BshScriptFactory"&gt;
92   *   &lt;constructor-arg value="classpath:mypackage/Messenger.bsh"/&gt;
93   *   &lt;constructor-arg value="mypackage.Messenger"/&gt;
94   *   &lt;property name="message" value="Hello World!"/&gt;
95   * &lt;/bean&gt;
96   *
97   * &lt;bean id="groovyMessenger" class="org.springframework.scripting.bsh.GroovyScriptFactory"&gt;
98   *   &lt;constructor-arg value="classpath:mypackage/Messenger.groovy"/&gt;
99   *   &lt;property name="message" value="Hello World!"/&gt;
100  * &lt;/bean&gt;</pre>
101  *
102  * <p><b>NOTE:</b> Please note that the above excerpt from a Spring
103  * XML bean definition file uses just the &lt;bean/&gt;-style syntax
104  * (in an effort to illustrate using the {@link ScriptFactoryPostProcessor} itself).
105  * In reality, you would never create a &lt;bean/&gt; definition for a
106  * {@link ScriptFactoryPostProcessor} explicitly; rather you would import the
107  * tags from the {@code 'lang'} namespace and simply create scripted
108  * beans using the tags in that namespace... as part of doing so, a
109  * {@link ScriptFactoryPostProcessor} will implicitly be created for you.
110  *
111  * <p>The Spring reference documentation contains numerous examples of using
112  * tags in the {@code 'lang'} namespace; by way of an example, find below
113  * a Groovy-backed bean defined using the {@code 'lang:groovy'} tag.
114  *
115  * <pre class="code">
116  * &lt;?xml version="1.0" encoding="UTF-8"?&gt;
117  * &lt;beans xmlns="http://www.springframework.org/schema/beans"
118  *     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
119  *     xmlns:lang="http://www.springframework.org/schema/lang"&gt;
120  *
121  *   &lt;!-- this is the bean definition for the Groovy-backed Messenger implementation --&gt;
122  *   &lt;lang:groovy id="messenger" script-source="classpath:Messenger.groovy"&gt;
123  *     &lt;lang:property name="message" value="I Can Do The Frug" /&gt;
124  *   &lt;/lang:groovy&gt;
125  *
126  *   &lt;!-- an otherwise normal bean that will be injected by the Groovy-backed Messenger --&gt;
127  *   &lt;bean id="bookingService" class="x.y.DefaultBookingService"&gt;
128  *     &lt;property name="messenger" ref="messenger" /&gt;
129  *   &lt;/bean&gt;
130  *
131  * &lt;/beans&gt;</pre>
132  *
133  * @author Juergen Hoeller
134  * @author Rob Harrop
135  * @author Rick Evans
136  * @author Mark Fisher
137  * @since 2.0
138  */
139 public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements
140 		BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware, DisposableBean, Ordered {
141 
142 	/**
143 	 * The {@link org.springframework.core.io.Resource}-style prefix that denotes
144 	 * an inline script.
145 	 * <p>An inline script is a script that is defined right there in the (typically XML)
146 	 * configuration, as opposed to being defined in an external file.
147 	 */
148 	public static final String INLINE_SCRIPT_PREFIX = "inline:";
149 
150 	public static final String REFRESH_CHECK_DELAY_ATTRIBUTE = Conventions.getQualifiedAttributeName(
151 			ScriptFactoryPostProcessor.class, "refreshCheckDelay");
152 
153 	public static final String PROXY_TARGET_CLASS_ATTRIBUTE = Conventions.getQualifiedAttributeName(
154 			ScriptFactoryPostProcessor.class, "proxyTargetClass");
155 
156 	public static final String LANGUAGE_ATTRIBUTE = Conventions.getQualifiedAttributeName(
157 			ScriptFactoryPostProcessor.class, "language");
158 
159 	private static final String SCRIPT_FACTORY_NAME_PREFIX = "scriptFactory.";
160 
161 	private static final String SCRIPTED_OBJECT_NAME_PREFIX = "scriptedObject.";
162 
163 	/** Logger available to subclasses */
164 	protected final Log logger = LogFactory.getLog(getClass());
165 
166 	private long defaultRefreshCheckDelay = -1;
167 
168 	private boolean defaultProxyTargetClass = false;
169 
170 	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
171 
172 	private ConfigurableBeanFactory beanFactory;
173 
174 	private ResourceLoader resourceLoader = new DefaultResourceLoader();
175 
176 	final DefaultListableBeanFactory scriptBeanFactory = new DefaultListableBeanFactory();
177 
178 	/** Map from bean name String to ScriptSource object */
179 	private final Map<String, ScriptSource> scriptSourceCache = new HashMap<String, ScriptSource>();
180 
181 	/**
182 	 * Set the delay between refresh checks, in milliseconds.
183 	 * Default is -1, indicating no refresh checks at all.
184 	 * <p>Note that an actual refresh will only happen when
185 	 * the {@link org.springframework.scripting.ScriptSource} indicates
186 	 * that it has been modified.
187 	 * @see org.springframework.scripting.ScriptSource#isModified()
188 	 */
189 	public void setDefaultRefreshCheckDelay(long defaultRefreshCheckDelay) {
190 		this.defaultRefreshCheckDelay = defaultRefreshCheckDelay;
191 	}
192 
193 	/**
194 	 * Flag to signal that refreshable proxies should be created to proxy the target class not its interfaces.
195 	 * @param defaultProxyTargetClass the flag value to set
196 	 */
197 	public void setDefaultProxyTargetClass(boolean defaultProxyTargetClass) {
198 		this.defaultProxyTargetClass = defaultProxyTargetClass;
199 	}
200 
201 	@Override
202 	public void setBeanClassLoader(ClassLoader classLoader) {
203 		this.beanClassLoader = classLoader;
204 	}
205 
206 	@Override
207 	public void setBeanFactory(BeanFactory beanFactory) {
208 		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
209 			throw new IllegalStateException("ScriptFactoryPostProcessor doesn't work with a BeanFactory "
210 					+ "which does not implement ConfigurableBeanFactory: " + beanFactory.getClass());
211 		}
212 		this.beanFactory = (ConfigurableBeanFactory) beanFactory;
213 
214 		// Required so that references (up container hierarchies) are correctly resolved.
215 		this.scriptBeanFactory.setParentBeanFactory(this.beanFactory);
216 
217 		// Required so that all BeanPostProcessors, Scopes, etc become available.
218 		this.scriptBeanFactory.copyConfigurationFrom(this.beanFactory);
219 
220 		// Filter out BeanPostProcessors that are part of the AOP infrastructure,
221 		// since those are only meant to apply to beans defined in the original factory.
222 		for (Iterator<BeanPostProcessor> it = this.scriptBeanFactory.getBeanPostProcessors().iterator(); it.hasNext();) {
223 			if (it.next() instanceof AopInfrastructureBean) {
224 				it.remove();
225 			}
226 		}
227 	}
228 
229 	@Override
230 	public void setResourceLoader(ResourceLoader resourceLoader) {
231 		this.resourceLoader = resourceLoader;
232 	}
233 
234 	@Override
235 	public int getOrder() {
236 		return Integer.MIN_VALUE;
237 	}
238 
239 	@Override
240 	public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
241 		// We only apply special treatment to ScriptFactory implementations here.
242 		if (!ScriptFactory.class.isAssignableFrom(beanClass)) {
243 			return null;
244 		}
245 
246 		BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
247 
248 		try {
249 			String scriptFactoryBeanName = SCRIPT_FACTORY_NAME_PREFIX + beanName;
250 			String scriptedObjectBeanName = SCRIPTED_OBJECT_NAME_PREFIX + beanName;
251 			prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName);
252 
253 			ScriptFactory scriptFactory = this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class);
254 			ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
255 			Class<?>[] interfaces = scriptFactory.getScriptInterfaces();
256 
257 			Class<?> scriptedType = scriptFactory.getScriptedObjectType(scriptSource);
258 			if (scriptedType != null) {
259 				return scriptedType;
260 			}
261 			else if (!ObjectUtils.isEmpty(interfaces)) {
262 				return (interfaces.length == 1 ? interfaces[0] : createCompositeInterface(interfaces));
263 			}
264 			else {
265 				if (bd.isSingleton()) {
266 					Object bean = this.scriptBeanFactory.getBean(scriptedObjectBeanName);
267 					if (bean != null) {
268 						return bean.getClass();
269 					}
270 				}
271 			}
272 		}
273 		catch (Exception ex) {
274 			if (ex instanceof BeanCreationException
275 					&& ((BeanCreationException) ex).getMostSpecificCause() instanceof BeanCurrentlyInCreationException) {
276 				if (logger.isTraceEnabled()) {
277 					logger.trace("Could not determine scripted object type for bean '" + beanName + "': "
278 							+ ex.getMessage());
279 				}
280 			}
281 			else {
282 				if (logger.isDebugEnabled()) {
283 					logger.debug("Could not determine scripted object type for bean '" + beanName + "'", ex);
284 				}
285 			}
286 		}
287 
288 		return null;
289 	}
290 
291 	@Override
292 	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
293 		// We only apply special treatment to ScriptFactory implementations here.
294 		if (!ScriptFactory.class.isAssignableFrom(beanClass)) {
295 			return null;
296 		}
297 
298 		BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
299 		String scriptFactoryBeanName = SCRIPT_FACTORY_NAME_PREFIX + beanName;
300 		String scriptedObjectBeanName = SCRIPTED_OBJECT_NAME_PREFIX + beanName;
301 		prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName);
302 
303 		ScriptFactory scriptFactory = this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class);
304 		ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
305 		boolean isFactoryBean = false;
306 		try {
307 			Class<?> scriptedObjectType = scriptFactory.getScriptedObjectType(scriptSource);
308 			// Returned type may be null if the factory is unable to determine the type.
309 			if (scriptedObjectType != null) {
310 				isFactoryBean = FactoryBean.class.isAssignableFrom(scriptedObjectType);
311 			}
312 		}
313 		catch (Exception ex) {
314 			throw new BeanCreationException(beanName,
315 					"Could not determine scripted object type for " + scriptFactory, ex);
316 		}
317 
318 		long refreshCheckDelay = resolveRefreshCheckDelay(bd);
319 		if (refreshCheckDelay >= 0) {
320 			Class<?>[] interfaces = scriptFactory.getScriptInterfaces();
321 			RefreshableScriptTargetSource ts = new RefreshableScriptTargetSource(this.scriptBeanFactory,
322 					scriptedObjectBeanName, scriptFactory, scriptSource, isFactoryBean);
323 			boolean proxyTargetClass = resolveProxyTargetClass(bd);
324 			String language = (String) bd.getAttribute(LANGUAGE_ATTRIBUTE);
325 			if (proxyTargetClass && (language == null || !language.equals("groovy"))) {
326 				throw new BeanDefinitionValidationException(
327 						"Cannot use proxyTargetClass=true with script beans where language is not 'groovy': '" +
328 						language + "'");
329 			}
330 			ts.setRefreshCheckDelay(refreshCheckDelay);
331 			return createRefreshableProxy(ts, interfaces, proxyTargetClass);
332 		}
333 
334 		if (isFactoryBean) {
335 			scriptedObjectBeanName = BeanFactory.FACTORY_BEAN_PREFIX + scriptedObjectBeanName;
336 		}
337 		return this.scriptBeanFactory.getBean(scriptedObjectBeanName);
338 	}
339 
340 	/**
341 	 * Prepare the script beans in the internal BeanFactory that this
342 	 * post-processor uses. Each original bean definition will be split
343 	 * into a ScriptFactory definition and a scripted object definition.
344 	 * @param bd the original bean definition in the main BeanFactory
345 	 * @param scriptFactoryBeanName the name of the internal ScriptFactory bean
346 	 * @param scriptedObjectBeanName the name of the internal scripted object bean
347 	 */
348 	protected void prepareScriptBeans(BeanDefinition bd, String scriptFactoryBeanName, String scriptedObjectBeanName) {
349 
350 		// Avoid recreation of the script bean definition in case of a prototype.
351 		synchronized (this.scriptBeanFactory) {
352 			if (!this.scriptBeanFactory.containsBeanDefinition(scriptedObjectBeanName)) {
353 
354 				this.scriptBeanFactory.registerBeanDefinition(scriptFactoryBeanName,
355 						createScriptFactoryBeanDefinition(bd));
356 				ScriptFactory scriptFactory = this.scriptBeanFactory
357 						.getBean(scriptFactoryBeanName, ScriptFactory.class);
358 				ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName,
359 						scriptFactory.getScriptSourceLocator());
360 				Class<?>[] interfaces = scriptFactory.getScriptInterfaces();
361 
362 				Class<?>[] scriptedInterfaces = interfaces;
363 				if (scriptFactory.requiresConfigInterface() && !bd.getPropertyValues().isEmpty()) {
364 					Class<?> configInterface = createConfigInterface(bd, interfaces);
365 					scriptedInterfaces = ObjectUtils.addObjectToArray(interfaces, configInterface);
366 				}
367 
368 				BeanDefinition objectBd = createScriptedObjectBeanDefinition(bd, scriptFactoryBeanName, scriptSource,
369 						scriptedInterfaces);
370 				long refreshCheckDelay = resolveRefreshCheckDelay(bd);
371 				if (refreshCheckDelay >= 0) {
372 					objectBd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
373 				}
374 
375 				this.scriptBeanFactory.registerBeanDefinition(scriptedObjectBeanName, objectBd);
376 			}
377 		}
378 	}
379 
380 	/**
381 	 * Get the refresh check delay for the given {@link ScriptFactory} {@link BeanDefinition}.
382 	 * If the {@link BeanDefinition} has a
383 	 * {@link org.springframework.core.AttributeAccessor metadata attribute}
384 	 * under the key {@link #REFRESH_CHECK_DELAY_ATTRIBUTE} which is a valid {@link Number}
385 	 * type, then this value is used. Otherwise, the the {@link #defaultRefreshCheckDelay}
386 	 * value is used.
387 	 * @param beanDefinition the BeanDefinition to check
388 	 * @return the refresh check delay
389 	 */
390 	protected long resolveRefreshCheckDelay(BeanDefinition beanDefinition) {
391 		long refreshCheckDelay = this.defaultRefreshCheckDelay;
392 		Object attributeValue = beanDefinition.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE);
393 		if (attributeValue instanceof Number) {
394 			refreshCheckDelay = ((Number) attributeValue).longValue();
395 		}
396 		else if (attributeValue instanceof String) {
397 			refreshCheckDelay = Long.parseLong((String) attributeValue);
398 		}
399 		else if (attributeValue != null) {
400 			throw new BeanDefinitionStoreException("Invalid refresh check delay attribute [" +
401 					REFRESH_CHECK_DELAY_ATTRIBUTE + "] with value '" + attributeValue +
402 					"': needs to be of type Number or String");
403 		}
404 		return refreshCheckDelay;
405 	}
406 
407 	protected boolean resolveProxyTargetClass(BeanDefinition beanDefinition) {
408 		boolean proxyTargetClass = this.defaultProxyTargetClass;
409 		Object attributeValue = beanDefinition.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE);
410 		if (attributeValue instanceof Boolean) {
411 			proxyTargetClass = (Boolean) attributeValue;
412 		}
413 		else if (attributeValue instanceof String) {
414 			proxyTargetClass = Boolean.valueOf((String) attributeValue);
415 		}
416 		else if (attributeValue != null) {
417 			throw new BeanDefinitionStoreException("Invalid proxy target class attribute [" +
418 					PROXY_TARGET_CLASS_ATTRIBUTE + "] with value '" + attributeValue +
419 					"': needs to be of type Boolean or String");
420 		}
421 		return proxyTargetClass;
422 	}
423 
424 	/**
425 	 * Create a ScriptFactory bean definition based on the given script definition,
426 	 * extracting only the definition data that is relevant for the ScriptFactory
427 	 * (that is, only bean class and constructor arguments).
428 	 * @param bd the full script bean definition
429 	 * @return the extracted ScriptFactory bean definition
430 	 * @see org.springframework.scripting.ScriptFactory
431 	 */
432 	protected BeanDefinition createScriptFactoryBeanDefinition(BeanDefinition bd) {
433 		GenericBeanDefinition scriptBd = new GenericBeanDefinition();
434 		scriptBd.setBeanClassName(bd.getBeanClassName());
435 		scriptBd.getConstructorArgumentValues().addArgumentValues(bd.getConstructorArgumentValues());
436 		return scriptBd;
437 	}
438 
439 	/**
440 	 * Obtain a ScriptSource for the given bean, lazily creating it
441 	 * if not cached already.
442 	 * @param beanName the name of the scripted bean
443 	 * @param scriptSourceLocator the script source locator associated with the bean
444 	 * @return the corresponding ScriptSource instance
445 	 * @see #convertToScriptSource
446 	 */
447 	protected ScriptSource getScriptSource(String beanName, String scriptSourceLocator) {
448 		synchronized (this.scriptSourceCache) {
449 			ScriptSource scriptSource = this.scriptSourceCache.get(beanName);
450 			if (scriptSource == null) {
451 				scriptSource = convertToScriptSource(beanName, scriptSourceLocator, this.resourceLoader);
452 				this.scriptSourceCache.put(beanName, scriptSource);
453 			}
454 			return scriptSource;
455 		}
456 	}
457 
458 	/**
459 	 * Convert the given script source locator to a ScriptSource instance.
460 	 * <p>By default, supported locators are Spring resource locations
461 	 * (such as "file:C:/myScript.bsh" or "classpath:myPackage/myScript.bsh")
462 	 * and inline scripts ("inline:myScriptText...").
463 	 * @param beanName the name of the scripted bean
464 	 * @param scriptSourceLocator the script source locator
465 	 * @param resourceLoader the ResourceLoader to use (if necessary)
466 	 * @return the ScriptSource instance
467 	 */
468 	protected ScriptSource convertToScriptSource(String beanName, String scriptSourceLocator,
469 			ResourceLoader resourceLoader) {
470 
471 		if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) {
472 			return new StaticScriptSource(scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName);
473 		}
474 		else {
475 			return new ResourceScriptSource(resourceLoader.getResource(scriptSourceLocator));
476 		}
477 	}
478 
479 	/**
480 	 * Create a config interface for the given bean definition, defining setter
481 	 * methods for the defined property values as well as an init method and
482 	 * a destroy method (if defined).
483 	 * <p>This implementation creates the interface via CGLIB's InterfaceMaker,
484 	 * determining the property types from the given interfaces (as far as possible).
485 	 * @param bd the bean definition (property values etc) to create a
486 	 * config interface for
487 	 * @param interfaces the interfaces to check against (might define
488 	 * getters corresponding to the setters we're supposed to generate)
489 	 * @return the config interface
490 	 * @see org.springframework.cglib.proxy.InterfaceMaker
491 	 * @see org.springframework.beans.BeanUtils#findPropertyType
492 	 */
493 	protected Class<?> createConfigInterface(BeanDefinition bd, Class<?>[] interfaces) {
494 		InterfaceMaker maker = new InterfaceMaker();
495 		PropertyValue[] pvs = bd.getPropertyValues().getPropertyValues();
496 		for (PropertyValue pv : pvs) {
497 			String propertyName = pv.getName();
498 			Class<?> propertyType = BeanUtils.findPropertyType(propertyName, interfaces);
499 			String setterName = "set" + StringUtils.capitalize(propertyName);
500 			Signature signature = new Signature(setterName, Type.VOID_TYPE, new Type[] {Type.getType(propertyType)});
501 			maker.add(signature, new Type[0]);
502 		}
503 		if (bd instanceof AbstractBeanDefinition) {
504 			AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
505 			if (abd.getInitMethodName() != null) {
506 				Signature signature = new Signature(abd.getInitMethodName(), Type.VOID_TYPE, new Type[0]);
507 				maker.add(signature, new Type[0]);
508 			}
509 			if (abd.getDestroyMethodName() != null) {
510 				Signature signature = new Signature(abd.getDestroyMethodName(), Type.VOID_TYPE, new Type[0]);
511 				maker.add(signature, new Type[0]);
512 			}
513 		}
514 		return maker.create();
515 	}
516 
517 	/**
518 	 * Create a composite interface Class for the given interfaces,
519 	 * implementing the given interfaces in one single Class.
520 	 * <p>The default implementation builds a JDK proxy class
521 	 * for the given interfaces.
522 	 * @param interfaces the interfaces to merge
523 	 * @return the merged interface as Class
524 	 * @see java.lang.reflect.Proxy#getProxyClass
525 	 */
526 	protected Class<?> createCompositeInterface(Class<?>[] interfaces) {
527 		return ClassUtils.createCompositeInterface(interfaces, this.beanClassLoader);
528 	}
529 
530 	/**
531 	 * Create a bean definition for the scripted object, based on the given script
532 	 * definition, extracting the definition data that is relevant for the scripted
533 	 * object (that is, everything but bean class and constructor arguments).
534 	 * @param bd the full script bean definition
535 	 * @param scriptFactoryBeanName the name of the internal ScriptFactory bean
536 	 * @param scriptSource the ScriptSource for the scripted bean
537 	 * @param interfaces the interfaces that the scripted bean is supposed to implement
538 	 * @return the extracted ScriptFactory bean definition
539 	 * @see org.springframework.scripting.ScriptFactory#getScriptedObject
540 	 */
541 	protected BeanDefinition createScriptedObjectBeanDefinition(BeanDefinition bd, String scriptFactoryBeanName,
542 			ScriptSource scriptSource, Class<?>[] interfaces) {
543 
544 		GenericBeanDefinition objectBd = new GenericBeanDefinition(bd);
545 		objectBd.setFactoryBeanName(scriptFactoryBeanName);
546 		objectBd.setFactoryMethodName("getScriptedObject");
547 		objectBd.getConstructorArgumentValues().clear();
548 		objectBd.getConstructorArgumentValues().addIndexedArgumentValue(0, scriptSource);
549 		objectBd.getConstructorArgumentValues().addIndexedArgumentValue(1, interfaces);
550 		return objectBd;
551 	}
552 
553 	/**
554 	 * Create a refreshable proxy for the given AOP TargetSource.
555 	 * @param ts the refreshable TargetSource
556 	 * @param interfaces the proxy interfaces (may be {@code null} to
557 	 * indicate proxying of all interfaces implemented by the target class)
558 	 * @return the generated proxy
559 	 * @see RefreshableScriptTargetSource
560 	 */
561 	protected Object createRefreshableProxy(TargetSource ts, Class<?>[] interfaces, boolean proxyTargetClass) {
562 		ProxyFactory proxyFactory = new ProxyFactory();
563 		proxyFactory.setTargetSource(ts);
564 		ClassLoader classLoader = this.beanClassLoader;
565 
566 		if (interfaces == null) {
567 			interfaces = ClassUtils.getAllInterfacesForClass(ts.getTargetClass(), this.beanClassLoader);
568 		}
569 		proxyFactory.setInterfaces(interfaces);
570 		if (proxyTargetClass) {
571 			classLoader = null;  // force use of Class.getClassLoader()
572 			proxyFactory.setProxyTargetClass(proxyTargetClass);
573 		}
574 
575 		DelegatingIntroductionInterceptor introduction = new DelegatingIntroductionInterceptor(ts);
576 		introduction.suppressInterface(TargetSource.class);
577 		proxyFactory.addAdvice(introduction);
578 
579 		return proxyFactory.getProxy(classLoader);
580 	}
581 
582 	/**
583 	 * Destroy the inner bean factory (used for scripts) on shutdown.
584 	 */
585 	@Override
586 	public void destroy() {
587 		this.scriptBeanFactory.destroySingletons();
588 	}
589 
590 }