Spring4新特性(5):Groovy Bean定義DSL
Spring4支持使用Groovy DSL來進行Bean定義配置,其類似于XML,不過因為是Groovy DSL,可以實現任何復雜的語法配置,但是對于配置,我們需要那么復雜嗎?本著學習的態度試用了下其Groovy DSL定義Bean,其主要缺點:
1、DSL語法規則不足,需要其后續維護;
2、編輯器的代碼補全需要跟進,否則沒有代碼補全,寫這個很痛苦;
3、出錯提示不友好,排錯難;
4、當前對于一些配置還是需要XML的支持,所以還不是100%的純Groovy DSL;
5、目前對整個Spring生態支持還是不夠的,比如Web,需要觀望。
其優點就是其本質是Groovy腳本,所以可以做非常復雜的配置,如果以上問題能夠解決,其也是一個不錯的選擇。在Groovy中的話使用這種配置感覺不會有什么問題,但是在純Java開發環境下也是有它,給我的感覺是這個功能其目的是去推廣它的groovy。比較懷疑它的動機。
接下來我們來看看Spring配置的發展:
Spring 2時代是XML風格配置 可以參考《 跟我學Spring3 》的前幾章
Spring 3時代引入注解風格配置 可以參考《 跟我學Spring3 》的 第12章
Spring 4時代引入Groovy DSL風格來配置 后續講解
一、對比
對于我來說,沒有哪個好/壞,只有適用不適用;開發方便不方便。接下來我們來看一下各種類型的配置吧:
XML風格配置
<context:component-scan base-package="com.sishuok.spring4"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator"/>
</bean>
<mvc:annotation-driven validator="validator"/>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<property name="validationMessageSource" ref="messageSource"/>
</bean>
注解風格配置
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.sishuok.spring4")
public class MvcConfiguration extends WebMvcConfigurationSupport {
@Override
protected Validator getValidator() {
LocalValidatorFactoryBean localValidatorFactoryBean =
new LocalValidatorFactoryBean();
localValidatorFactoryBean.setProviderClass(HibernateValidator.class);
localValidatorFactoryBean.setValidationMessageSource(messageSource());
return localValidatorFactoryBean;
}
}
Groovy DSL風格配置
import org.hibernate.validator.HibernateValidator
import org.springframework.context.support.ReloadableResourceBundleMessageSource
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
beans {
xmlns context: "http://www.springframework.org/schema/context"
xmlns mvc: "http://www.springframework.org/schema/mvc"
context.'component-scan'('base-package': "com,sishuok.spring4")
mvc.'annotation-driven'('validator': "validator")
validator(LocalValidatorFactoryBean) {
providerClass = HibernateValidator.class
validationMessageSource = ref("messageSource")
}
}
因為Spring4 webmvc沒有提供用于Web環境的Groovy DSL實現的WebApplicationContext,所以為了在web環境使用,單獨寫了一個WebGenricGroovyApplicationContext,可以到源碼中查找。
可以看到,它們之前差別不是特別大;以上只提取了部分配置,完整的配置可以參考我的github: spring4-showcase
對于注解風格的配置,如果在Servlet3容器中使用的話,可以借助WebApplicationInitializer實現無配置:
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(javax.servlet.ServletContext sc) throws ServletException {
// AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
// rootContext.register(AppConfig.class);
// sc.addListener(new ContextLoaderListener(rootContext));
//2、springmvc上下文
AnnotationConfigWebApplicationContext springMvcContext = new AnnotationConfigWebApplicationContext();
springMvcContext.register(MvcConfiguration.class);
//3、DispatcherServlet
DispatcherServlet dispatcherServlet = new DispatcherServlet(springMvcContext);
ServletRegistration.Dynamic dynamic = sc.addServlet("dispatcherServlet", dispatcherServlet);
dynamic.setLoadOnStartup(1);
dynamic.addMapping("/");
//4、CharacterEncodingFilter
FilterRegistration filterRegistration =
sc.addFilter("characterEncodingFilter", CharacterEncodingFilter.class);
filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
}
到底好還是不好,需要根據自己項目大小等一些因素來衡量。對于Servlet3可以參考我github的示例: servlet3-showcase
對于Groovy風格配置,如果語法足夠豐富、Spring內部支持完善,且編輯器支持也非常好的話,也是不錯的選擇。
二、Groovy Bean定義
接下來我們來看下groovy DSL的具體使用吧:
1、安裝環境
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>${groovy.version}</version>
</dependency>
我使用的groovy版本是2.2.1
2、相關組件類
此處使用Spring Framework官網的hello world,可以前往 http://projects.spring.io/spring-framework/ 主頁查看
3、Groovy Bean定義配置文件
import com.sishuok.spring4.xml.MessageServiceImpl
import com.sishuok.spring4.xml.MessagePrinter
beans {
messageService(MessageServiceImpl) {//名字(類型)
message = "hello" //注入的屬性
}
messagePrinter(MessagePrinter, messageService) //名字(類型,構造器參數列表)
}
從此處可以看到 如果僅僅是簡單的Bean定義,確實比XML簡潔。
4、測試
如果不測試環境可以這樣測試:
public class XmlGroovyBeanDefinitionTest1 {
@Test
public void test() {
ApplicationContext ctx = new GenericGroovyApplicationContext("classpath:spring-config-xml.groovy");
MessagePrinter messagePrinter = (MessagePrinter) ctx.getBean("messagePrinter");
messagePrinter.printMessage();
}
}
使用GenericGroovyApplicationContext加載groovy配置文件。
如果想集成到Spring Test中,可以這樣:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config-xml.groovy", loader = GenericGroovyContextLoader.class)
public class XmlGroovyBeanDefinitionTest2 {
@Autowired
private MessagePrinter messagePrinter;
@Test
public void test() {
messagePrinter.printMessage();
}
}
此處需要定義我們自己的bean loader,即從groovy配置文件加載:
public class GenericGroovyContextLoader extends AbstractGenericContextLoader {
@Override
protected String getResourceSuffix() {
throw new UnsupportedOperationException(
"GenericGroovyContextLoader does not support the getResourceSuffix() method");
}
@Override
protected BeanDefinitionReader createBeanDefinitionReader(GenericApplicationContext context) {
return new GroovyBeanDefinitionReader(context);
}
}
使用GroovyBeanDefinitionReader來加載groovy配置文件。
到此基本的使用就結束了,還算是比較簡潔,但是我們已經注意到了,在純Java環境做測試還是比較麻煩的。 比如沒有給我們寫好相關的測試支撐類。另外大家可以前往Spring的github看看在groovy中的單元測試: GroovyBeanDefinitionReaderTests.groovy
再看一下我們使用注解方式呢:
@Component
public class MessageServiceImpl implements MessageService {
@Autowired
@Qualifier("message")
private String message;
……
}
@Component
public class MessagePrinter {
private MessageService messageService;
@Autowired
public MessagePrinter(MessageService messageService) {
this.messageService = messageService;
}
……
}
此處省略無關代碼,需要的話直接去github查看 。 點擊前往
Groovy配置文件:
beans {
xmlns context: "http://www.springframework.org/schema/context" //導入命名空間
context.'component-scan'('base-package': "com.sishuok.spring4") {
'exclude-filter'('type': "aspectj", 'expression': "com.sishuok.spring4.xml.*")
}
message(String, "hello") {}
}
原文出處: 張開濤