GroovyScriptFactory supports CompilationCustomizer next to GroovyObjectCustomizer

Issue: SPR-14585
This commit is contained in:
Juergen Hoeller
2016-08-17 00:47:50 +02:00
parent 214c919742
commit 433764d217
4 changed files with 88 additions and 20 deletions

View File

@@ -24,6 +24,8 @@ import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.lang.Script;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
@@ -57,7 +59,9 @@ public class GroovyScriptFactory implements ScriptFactory, BeanFactoryAware, Bea
private final String scriptSourceLocator;
private final GroovyObjectCustomizer groovyObjectCustomizer;
private GroovyObjectCustomizer groovyObjectCustomizer;
private CompilerConfiguration compilerConfiguration;
private GroovyClassLoader groovyClassLoader;
@@ -80,27 +84,46 @@ public class GroovyScriptFactory implements ScriptFactory, BeanFactoryAware, Bea
* Interpreted by the post-processor that actually creates the script.
*/
public GroovyScriptFactory(String scriptSourceLocator) {
this(scriptSourceLocator, null);
Assert.hasText(scriptSourceLocator, "'scriptSourceLocator' must not be empty");
this.scriptSourceLocator = scriptSourceLocator;
}
/**
* Create a new GroovyScriptFactory for the given script source,
* specifying a strategy interface that can create a custom MetaClass
* to supply missing methods and otherwise change the behavior of the object.
* <p>We don't need to specify script interfaces here, since
* a Groovy script defines its Java interfaces itself.
* @param scriptSourceLocator a locator that points to the source of the script.
* Interpreted by the post-processor that actually creates the script.
* @param groovyObjectCustomizer a customizer that can set a custom metaclass
* or make other changes to the GroovyObject created by this factory
* (may be {@code null})
* @see GroovyObjectCustomizer#customize
*/
public GroovyScriptFactory(String scriptSourceLocator, GroovyObjectCustomizer groovyObjectCustomizer) {
Assert.hasText(scriptSourceLocator, "'scriptSourceLocator' must not be empty");
this.scriptSourceLocator = scriptSourceLocator;
this(scriptSourceLocator);
this.groovyObjectCustomizer = groovyObjectCustomizer;
}
/**
* Create a new GroovyScriptFactory for the given script source,
* specifying a strategy interface that can customize Groovy's compilation
* process within the underlying GroovyClassLoader.
* @param scriptSourceLocator a locator that points to the source of the script.
* Interpreted by the post-processor that actually creates the script.
* @param compilationCustomizer a customizer to be applied to the GroovyClassLoader
* compiler configuration (may be {@code null})
* @since 4.3.3
* @see CompilerConfiguration#addCompilationCustomizers
* @see org.codehaus.groovy.control.customizers.ImportCustomizer
*/
public GroovyScriptFactory(String scriptSourceLocator, CompilationCustomizer compilationCustomizer) {
this(scriptSourceLocator);
if (compilationCustomizer != null) {
this.compilerConfiguration = new CompilerConfiguration();
this.compilerConfiguration.addCompilationCustomizers(compilationCustomizer);
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
@@ -111,7 +134,7 @@ public class GroovyScriptFactory implements ScriptFactory, BeanFactoryAware, Bea
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.groovyClassLoader = new GroovyClassLoader(classLoader);
this.groovyClassLoader = buildGroovyClassLoader(classLoader);
}
/**
@@ -120,12 +143,22 @@ public class GroovyScriptFactory implements ScriptFactory, BeanFactoryAware, Bea
public GroovyClassLoader getGroovyClassLoader() {
synchronized (this.scriptClassMonitor) {
if (this.groovyClassLoader == null) {
this.groovyClassLoader = new GroovyClassLoader(ClassUtils.getDefaultClassLoader());
this.groovyClassLoader = buildGroovyClassLoader(ClassUtils.getDefaultClassLoader());
}
return this.groovyClassLoader;
}
}
/**
* Build a {@link GroovyClassLoader} for the given {@code ClassLoader}.
* @param classLoader the ClassLoader to build a GroovyClassLoader for
* @since 4.3.3
*/
protected GroovyClassLoader buildGroovyClassLoader(ClassLoader classLoader) {
return (this.compilerConfiguration != null ?
new GroovyClassLoader(classLoader, this.compilerConfiguration) : new GroovyClassLoader(classLoader));
}
@Override
public String getScriptSourceLocator() {

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.scripting.groovy;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
/**
* @author Juergen Hoeller
*/
public class MyImportCustomizer extends ImportCustomizer {
public MyImportCustomizer() {
addStarImports("org.springframework.scripting.groovy");
}
}

View File

@@ -2,7 +2,7 @@
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">
@@ -24,7 +24,7 @@
<lang:property name="message" value="Hello World!"/>
</lang:groovy>
<lang:groovy id="calculator" depends-on="messenger" customizer-ref="customizer">
<lang:groovy id="calculator" depends-on="messenger" customizer-ref="groovyObjectCustomizer">
<lang:inline-script>
package org.springframework.scripting.groovy;
import org.springframework.scripting.Calculator
@@ -36,10 +36,8 @@ class GroovyCalculator implements Calculator {
</lang:inline-script>
</lang:groovy>
<lang:groovy id="customizer">
<lang:groovy id="groovyObjectCustomizer" customizer-ref="importCustomizer">
<lang:inline-script><![CDATA[
import org.springframework.scripting.groovy.GroovyObjectCustomizer;
public class TestCustomizer implements GroovyObjectCustomizer {
public void customize(GroovyObject o) {
println "customizing ${o}.."
@@ -48,13 +46,15 @@ public class TestCustomizer implements GroovyObjectCustomizer {
</lang:inline-script>
</lang:groovy>
<bean id="importCustomizer" class="org.springframework.scripting.groovy.MyImportCustomizer"/>
<lang:groovy id="refreshableMessenger" refresh-check-delay="5000"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Hello World!"/>
</lang:groovy>
<lang:groovy script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Hello World!"/>
</lang:groovy>
<lang:groovy script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Hello World!"/>
</lang:groovy>
</beans>

View File

@@ -7639,7 +7639,6 @@ set some default property values, or specify a custom `MetaClass`.
public interface GroovyObjectCustomizer {
void customize(GroovyObject goo);
}
----
@@ -7678,12 +7677,12 @@ of a `GroovyObjectCustomizer` is easy if you are using the Spring namespace supp
[subs="verbatim,quotes"]
----
<!-- define the GroovyObjectCustomizer just like any other bean -->
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" />
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>
<!-- ... and plug it into the desired Groovy bean via the 'customizer-ref' attribute -->
<lang:groovy id="calculator"
script-source="classpath:org/springframework/scripting/groovy/Calculator.groovy"
customizer-ref="tracingCustomizer" />
customizer-ref="tracingCustomizer"/>
----
If you are not using the Spring namespace support, you can still use the
@@ -7696,13 +7695,19 @@ If you are not using the Spring namespace support, you can still use the
<constructor-arg value="classpath:org/springframework/scripting/groovy/Calculator.groovy"/>
<!-- define the GroovyObjectCustomizer (as an inner bean) -->
<constructor-arg>
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" />
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>
</constructor-arg>
</bean>
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
----
[NOTE]
====
As of Spring Framework 4.3.3, you may also specify a Groovy `CompilationCustomizer` type
such as an `ImportCustomizer` in the same place as Spring's `GroovyObjectCustomizer`.
====
[[dynamic-language-beans-bsh]]