问题
I'd like to use Dynamic Languages Support of Spring Framework.
In XML I'd just use the lang
namespace, but I'd like to use Java configuration (i.e. @Configuration
classes) only.
I can imagine that I can do it by initializing all the hell from org.springframework.scripting.config
package, inc. all the BeanPostProcessor
s, Handler
s, Parser
s and FactoryBean
s they create, but I really don't want to go there.
Is there some other way? If there's none, what will be the minimal configuration needed to create a reloadable bean out of a Groovy script?
回答1:
Why don't you ask us directly by email? :-)
I see that XML Lang support is relly magic. There is enough stuff which is based on BeanDefinition
and its attributes
. In additional there are some hooks with ProxyFactory
and CGLIB
for the lang:property
.
What I see for the JavaConfig is some Java class wrapper for the ScriptEvaluator
and RefreshableResourceScriptSource
from Spring Integration:
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class RefreshableScriptJavaConfigTests {
@Autowired
private Calculator calculator;
@Test
public void testGroovyRefreshableCalculator() {
assertEquals(5, this.calculator.add(2, 3));
}
@Configuration
public static class ContextConfiguration {
@Value("classpath:org/springframework/integration/scripting/config/jsr223/Calculator.groovy")
private Resource groovyScriptResource;
@Bean
public ScriptEvaluator groovyScriptEvaluator() {
return new GroovyScriptEvaluator();
}
@Bean
public Calculator calculator() {
return new Calculator(new RefreshableResourceScriptSource(this.groovyScriptResource, 1000));
}
}
public static class Calculator {
private final ScriptSource scriptSource;
@Autowired
private ScriptEvaluator scriptEvaluator;
public Calculator(ScriptSource scriptSource) {
this.scriptSource = scriptSource;
}
public int add(int x, int y) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", x);
params.put("y", y);
return (int) this.scriptEvaluator.evaluate(this.scriptSource, params);
}
}
}
Where the Calculator.groovy
is:
x + y
I understand that it isn't so flexible as it looks with interfaces and configuration from XML definition, but at least it will help you to see where we are.
Feel free to raise a JIRA issue on the matter and we'll see what we can do here. Something like @EnableScripting
and @ScriptSource(refreshDelay = 1000)
on the Resource
@Bean
method.
I think for now you can just @Import
some XML snippets with lang
definitions.
Cheers, Artem
回答2:
I'm going down the same path (work in progress), and have managed to initialise reloadable Groovy scripts by adding the bean definitions when the Spring Application is prepared. In my example I'm using spring-boot
.
If you add the following AddBeanDefinitionsListener
listener class and a ScriptFactoryPostProcessor
bean, you can initialise Groovy scripts with very little effort...
AddBeanDefinitionsListener.groovy
public class AddBeanDefinitionsListener
implements ApplicationListener<ApplicationPreparedEvent> {
Map<String, BeanDefinition> beanDefs
AddBeanDefinitionsListener(Map<String, BeanDefinition> beanDefs) {
this.beanDefs = beanDefs
}
@Override
void onApplicationEvent(ApplicationPreparedEvent event) {
def registry = (BeanDefinitionRegistry) event.applicationContext
.autowireCapableBeanFactory
beanDefs.each { String beanName, BeanDefinition beanDef ->
registry.registerBeanDefinition(beanName, beanDef)
}
}
/* Static Utility methods */
static BeanDefinition groovyScriptFactory(String scriptLocation) {
def bd = BeanDefinitionBuilder.genericBeanDefinition(GroovyScriptFactory)
.addConstructorArgValue(scriptLocation)
.getBeanDefinition()
bd.setAttribute(ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, 1000)
bd
}
}
Application.groovy
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application)
app.addListeners(new AddBeanDefinitionsListener([
'foobar0': groovyScriptFactory("file:/some/path/Foobar0Service.groovy"),
'foobar1': groovyScriptFactory("file:/some/path/Foobar1Service.groovy")
]))
app.run(args)
}
@Bean
ScriptFactoryPostProcessor scriptFactory() {
new ScriptFactoryPostProcessor()
}
}
(Might be nicer if implemented with Spring 4.2's events?)
来源:https://stackoverflow.com/questions/26208020/using-spring-dynamic-languages-support-from-java-configuration