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从加载单个类,变成扫描目录,更实用了。
项目结构如下:
项目文件就不贴了,有需要的留言吧,累死了