Spring4新特性(5):Groovy Bean定义DSL

IsraelJQJC 8年前
   <p>Spring4支持使用Groovy DSL来进行Bean定义配置,其类似于XML,不过因为是Groovy DSL,可以实现任何复杂的语法配置,但是对于配置,我们需要那么复杂吗?本着学习的态度试用了下其Groovy DSL定义Bean,其主要缺点:</p>    <p>1、DSL语法规则不足,需要其后续维护;</p>    <p>2、编辑器的代码补全需要跟进,否则没有代码补全,写这个很痛苦;</p>    <p>3、出错提示不友好,排错难;</p>    <p>4、当前对于一些配置还是需要XML的支持,所以还不是100%的纯Groovy DSL;</p>    <p>5、目前对整个Spring生态支持还是不够的,比如Web,需要观望。</p>    <p>其优点就是其本质是Groovy脚本,所以可以做非常复杂的配置,如果以上问题能够解决,其也是一个不错的选择。在Groovy中的话使用这种配置感觉不会有什么问题,但是在纯Java开发环境下也是有它,给我的感觉是这个功能其目的是去推广它的groovy。比较怀疑它的动机。</p>    <p>接下来我们来看看Spring配置的发展:</p>    <p>Spring 2时代是XML风格配置  可以参考《 <a href="/misc/goto?guid=4959670528348520327" rel="nofollow,noindex">跟我学Spring3</a> 》的前几章</p>    <p>Spring 3时代引入注解风格配置  可以参考《 <a href="/misc/goto?guid=4959670528348520327" rel="nofollow,noindex">跟我学Spring3</a> 》的 <a href="/misc/goto?guid=4959670528451798409" rel="nofollow,noindex">第12章</a></p>    <p>Spring 4时代引入Groovy DSL风格来配置 后续讲解</p>    <h2>一、对比</h2>    <p>对于我来说,没有哪个好/坏,只有适用不适用;开发方便不方便。接下来我们来看一下各种类型的配置吧:</p>    <p>XML风格配置</p>    <pre>  <code class="language-java"><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></code></pre>    <p>注解风格配置 </p>    <pre>  <code class="language-java">@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;      }  }</code></pre>    <p>Groovy DSL风格配置</p>    <pre>  <code class="language-java">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")      }  }</code></pre>    <p>因为Spring4 webmvc没有提供用于Web环境的Groovy DSL实现的WebApplicationContext,所以为了在web环境使用,单独写了一个WebGenricGroovyApplicationContext,可以到源码中查找。</p>    <p>可以看到,它们之前差别不是特别大;以上只提取了部分配置,完整的配置可以参考我的github: <a href="/misc/goto?guid=4959670528545737779" rel="nofollow,noindex">spring4-showcase</a></p>    <p>对于注解风格的配置,如果在Servlet3容器中使用的话,可以借助WebApplicationInitializer实现无配置:</p>    <pre>  <code class="language-java">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, "/*");        }  }</code></pre>    <p>到底好还是不好,需要根据自己项目大小等一些因素来衡量。对于Servlet3可以参考我github的示例: <a href="/misc/goto?guid=4959670528629140588" rel="nofollow,noindex">servlet3-showcase </a></p>    <p>对于Groovy风格配置,如果语法足够丰富、Spring内部支持完善,且编辑器支持也非常好的话,也是不错的选择。</p>    <h2>二、Groovy Bean定义</h2>    <p>接下来我们来看下groovy DSL的具体使用吧:</p>    <p>1、安装环境</p>    <pre>  <code class="language-java"><dependency>              <groupId>org.codehaus.groovy</groupId>              <artifactId>groovy-all</artifactId>              <version>${groovy.version}</version>          </dependency></code></pre>    <p>我使用的groovy版本是2.2.1</p>    <p>2、相关组件类</p>    <p>此处使用Spring Framework官网的hello world,可以前往 <a href="/misc/goto?guid=4958860119378434225" rel="nofollow,noindex">http://projects.spring.io/spring-framework/</a> 主页查看</p>    <p>3、Groovy Bean定义配置文件</p>    <pre>  <code class="language-java">import com.sishuok.spring4.xml.MessageServiceImpl  import com.sishuok.spring4.xml.MessagePrinter    beans {      messageService(MessageServiceImpl) {//名字(类型)           message = "hello"  //注入的属性      }        messagePrinter(MessagePrinter, messageService) //名字(类型,构造器参数列表)    }</code></pre>    <p>从此处可以看到 如果仅仅是简单的Bean定义,确实比XML简洁。</p>    <p>4、测试</p>    <p>如果不测试环境可以这样测试:</p>    <pre>  <code class="language-java">public class XmlGroovyBeanDefinitionTest1 {      @Test      public void test() {          ApplicationContext ctx = new GenericGroovyApplicationContext("classpath:spring-config-xml.groovy");          MessagePrinter messagePrinter = (MessagePrinter) ctx.getBean("messagePrinter");          messagePrinter.printMessage();      }  }</code></pre>    <p>使用GenericGroovyApplicationContext加载groovy配置文件。</p>    <p>如果想集成到Spring Test中,可以这样:</p>    <pre>  <code class="language-java">@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();      }  }</code></pre>    <p>此处需要定义我们自己的bean loader,即从groovy配置文件加载:</p>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>使用GroovyBeanDefinitionReader来加载groovy配置文件。</p>    <p>到此基本的使用就结束了,还算是比较简洁,但是我们已经注意到了,在纯Java环境做测试还是比较麻烦的。 比如没有给我们写好相关的测试支撑类。另外大家可以前往Spring的github看看在groovy中的单元测试: <a href="/misc/goto?guid=4959670528743818772" rel="nofollow,noindex">GroovyBeanDefinitionReaderTests.groovy</a></p>    <p>再看一下我们使用注解方式呢:</p>    <pre>  <code class="language-java">@Component  public class MessageServiceImpl implements MessageService {      @Autowired      @Qualifier("message")      private String message;      ……  }</code></pre>    <pre>  <code class="language-java">@Component  public class MessagePrinter {      private MessageService messageService;      @Autowired      public MessagePrinter(MessageService messageService) {          this.messageService = messageService;      }  ……  }</code></pre>    <p>此处省略无关代码,需要的话直接去github查看 。 <a href="/misc/goto?guid=4959670528828857317" rel="nofollow,noindex">点击前往</a></p>    <p>Groovy配置文件:</p>    <pre>  <code class="language-java">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") {}    }</code></pre>    <p>原文出处: <a href="/misc/goto?guid=4959670528909148505">张开涛</a> </p>