groovy是一门基于JVM的动态语言,跟spring结合更显强大,废话不多说,直接上例子
1、定义java接口 Foo.java
package groovy;
public interface Foo {
void execute();
}
2、定义Groovy实现类 FooImpl.groovy
package groovy
import groovy.Foo
class FooImpl implements Foo {
void execute() {
println("hello!");
}
}
3、定义spring配置文件 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:gorm="http://grails.org/schema/gorm"
xsi:schemaLocation="http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://grails.org/schema/gorm http://grails.org/schema/gorm/gorm.xsd">
<lang:groovy refresh-check-delay="500" script-source="groovy/FooImpl.groovy"></lang:groovy>
</beans>
4、测试类 GroovyTest.java
package groovy;
import java.util.Scanner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class GroovyTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
Foo foo = context.getBean(Foo.class);
Scanner in = new Scanner(System.in);
while (true) {
System.out.println("输入任意键");
in.next();
foo.execute();
}
}
}
ok,运行测试
修改FooImpl.groovy
package groovy
import groovy.Foo
class FooImpl implements Foo {
void execute() {
println("hello world!");
}
}
到控制台再次输入
测试成功,程序不用重启,即可看到修改后的结果,再也不用其他热加载的插件了。
但是这样感觉不太爽,要是有很多接口实现类的话,不是要写一大堆配置文件?
虽然可以copy,也可以自动生成,但是我觉得还是不够完美,想个办法解决掉,办法就是动态加载bean,编写一个初始化bean,下次直接用即可。
该bean需要实现ApplicationContextAware接口,该接口可以拿到ApplicationContext,也就是spring容器,有了这个,我们就可以自己加一些bean到这个容器当中了。
为了实现这个效果,我可是花了不少功夫,由于网上又没有这个资料,只能自己硬着头皮,看源代码,终于皇天不负有心人 被我实现了,拿来共享一下
1、首先需要顶一个一个类实现ApplicationContextAware接口 GroovyFactory.java
package groovy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class GroovyFactory implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
// 只有这个对象才能注册bean到spring容器
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
// 因为spring会自动将xml解析成BeanDefinition对象然后进行实例化,这里我们没有用xml,所以自己定义BeanDefinition
// 这些信息跟spring配置文件的方式差不多,只不过有些东西lang:groovy标签帮我们完成了
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClassName("org.springframework.scripting.groovy.GroovyScriptFactory");
final String refreshCheckDelay = "org.springframework.scripting.support.ScriptFactoryPostProcessor.refreshCheckDelay";
final String language = "org.springframework.scripting.support.ScriptFactoryPostProcessor.language";
// 刷新时间
bd.setAttribute(refreshCheckDelay, 500);
// 语言脚本
bd.setAttribute(language, "groovy");
// 文件目录
bd.getConstructorArgumentValues().addIndexedArgumentValue(0, "groovy/FooImpl.groovy");
// 注册到spring容器
beanFactory.registerBeanDefinition("Foo", bd);
}
}
2、然后将这个bean配置到applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:gorm="http://grails.org/schema/gorm"
xsi:schemaLocation="http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://grails.org/schema/gorm http://grails.org/schema/gorm/gorm.xsd">
<!--
<lang:groovy refresh-check-delay="500" script-source="groovy/FooImpl.groovy"></lang:groovy>
-->
<bean class="groovy.GroovyFactory"></bean>
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
</beans>
3、这个多出了一个bean配置,事实上,这个bean在我们采用第一种方法配置的时候,spring后自动创建这个bean,然后我们没有采用这个方式,所以只能手动配置
4、运行程序,应该更刚才的结果是一样的
写到这里,可能会有人说我是多此一举了,不急,只要把GroovyFactory稍微修改一下,让其接受一个参数,其作用就明显,其参数是一个目录名,得到此目录名后,我们的bean就可以去扫描这个目录下的所有groovy文件,然后将其动态添加到spring容器中,这样无论我们添加了多少groovy类,都不需要去修改配置了,继续...
修改GroovyFactory.java
package groovy;
import java.io.File;
import java.io.FileFilter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class GroovyFactory implements ApplicationContextAware {
private String directory;
public String getDirectory() {
return directory;
}
public void setDirectory(String directory) {
this.directory = directory;
}
@Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
// 只有这个对象才能注册bean到spring容器
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
// 因为spring会自动将xml解析成BeanDefinition对象然后进行实例化,这里我们没有用xml,所以自己定义BeanDefinition
// 这些信息跟spring配置文件的方式差不多,只不过有些东西lang:groovy标签帮我们完成了
final String refreshCheckDelay = "org.springframework.scripting.support.ScriptFactoryPostProcessor.refreshCheckDelay";
final String language = "org.springframework.scripting.support.ScriptFactoryPostProcessor.language";
String realDirectory = Thread.currentThread().getContextClassLoader().getResource(directory).getFile();
File root = new File(Thread.currentThread().getContextClassLoader().getResource(".").getFile());
File[] files = new File(realDirectory).listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".groovy") ? true : false;
}
});
for (File file : files) {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClassName("org.springframework.scripting.groovy.GroovyScriptFactory");
// 刷新时间
bd.setAttribute(refreshCheckDelay, 500);
// 语言脚本
bd.setAttribute(language, "groovy");
// 文件目录
bd.getConstructorArgumentValues().addIndexedArgumentValue(0, file.getPath().replace(root.getPath(), ""));
// 注册到spring容器
beanFactory.registerBeanDefinition(file.getName().replace(".groovy", ""), bd);
}
}
}
接着修改applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:gorm="http://grails.org/schema/gorm"
xsi:schemaLocation="http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://grails.org/schema/gorm http://grails.org/schema/gorm/gorm.xsd">
<!--
<lang:groovy refresh-check-delay="500" script-source="groovy/FooImpl.groovy"></lang:groovy>
-->
<bean class="groovy.GroovyFactory">
<property name="directory" value="groovy"/>
</bean>
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
</beans>
运行程序,结果跟上面一样,不一样的就是GroovyFactory从加载单个类,变成扫描目录,更实用了。
项目结构如下:
项目文件就不贴了,有需要的留言吧,累死了