跳至主要内容

Spring 4: Groovy DSL bean definition


Spring 4: Groovy DSL bean definition

In Spring 2.x, script language is supported in Spring via the Java scripting engine.
In Spring 4.0, Groovy is a first class citizen in Spring. Groovy can be used as an alternative for configuring bean definition.

Groovy DSL bean definition

As a Spring developer, you could be very familiar with XML based or/and Java annotation based bean definition.
The following is an example of XML bean definition.
<jdbc:embedded-database id="dataSource" type="H2">
</jdbc:embedded-database>

<bean
 class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
 id="entityManagerFactory">
 <property name="persistenceUnitName" value="persistenceUnit" />
 <property name="dataSource" ref="dataSource" />
 <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"></property>
 <property name="packagesToScan">
  <array>
   <value>com.hantsylabs.example.spring.model</value>
  </array>
 </property>
 <property name="jpaProperties">
  <value>
   hibernate.format_sql=true
   hibernate.show_sql=true
   hibernate.hbm2ddl.auto=create
  </value>
 </property>
</bean>

<bean class="org.springframework.orm.jpa.JpaTransactionManager"
 id="transactionManager">
 <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
The following is the equivalent bean definition using Java annotation.
@Configuration
@ComponentScan(basePackages = { "com.hantsylabs.example.spring.dao",
  "com.hantsylabs.example.spring.jpa" })
@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
public class JpaConfig {

 @Bean
 public DataSource dataSource() {
  return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
 }

 @Bean
 public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
  LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
  emf.setDataSource(dataSource());
  emf.setPackagesToScan("com.hantsylabs.example.spring.model");
  emf.setPersistenceProvider(new HibernatePersistence());
  emf.setJpaProperties(jpaProperties());
  return emf;
 }

 private Properties jpaProperties() {
  Properties extraProperties = new Properties();
  extraProperties.put("hibernate.format_sql", "true");
  extraProperties.put("hibernate.show_sql", "true");
  extraProperties.put("hibernate.hbm2ddl.auto", "create");
  return extraProperties;
 }

 @Bean
 public PlatformTransactionManager transactionManager() {
  return new JpaTransactionManager(entityManagerFactory().getObject());
 }

}
The following content defines these beans in Groovy DSL way.
import org.apache.commons.dbcp.BasicDataSource
import org.springframework.orm.jpa.JpaTransactionManager
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import com.hantsylabs.example.spring.jpa.JpaConferenceDaoImpl

beans {
     dataSource(BasicDataSource) {   
   driverClassName = "org.h2.Driver"
         url = "jdbc:h2:mem:spring4-sandbox"
         username = "sa"
         password = ""
     }
  
  entityManagerFactory(LocalContainerEntityManagerFactoryBean){
   persistenceProviderClass="org.hibernate.ejb.HibernatePersistence"
   dataSource=dataSource
   persistenceUnitName="persistenceUnit"
   packagesToScan=["com.hantsylabs.example.spring.model"]
   jpaProperties=[
    "hibernate.format_sql":"true",
    "hibernate.show_sql":"true",
    "hibernate.hbm2ddl.auto":"create"
    ]
  }
  
  transactionManager(JpaTransactionManager){
   entityManagerFactory=entityManagerFactory
  }
  
  conferenceDao(JpaConferenceDaoImpl){
  }

 }
If you have some experience of Grails, you could have recognized this feature is similar with the Grails resources.groovy.
Add groovy as a dependency in your Maven pom.xml.
<dependency>
 <groupId>org.codehaus.groovy</groupId>
 <artifactId>groovy-all</artifactId>
 <version>2.1.8</version>
</dependency>
Now you can write some test codes.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:/com/hantsylabs/example/spring/config/JpaConfigGroovy.groovy", loader = GenericGroovyContextLoader.class)
@TransactionConfiguration
public class GroovyJpaConferenceDaoImplTest {

 public static class GenericGroovyContextLoader extends
   AbstractGenericContextLoader {

  @Override
  protected BeanDefinitionReader createBeanDefinitionReader(
    GenericApplicationContext context) {
   return new GroovyBeanDefinitionReader(context);
  }

  @Override
  protected String getResourceSuffix() {
   return ".groovy";
  }

 }
 
 // test codes
}
I've tried to load the Groovy bean definition resource through @ContextConfigurationannotation, but it does not work. By default, the locations and value are ready for loading XML bean definition. Spring 4.0 includes a GroovyBeanDefinitionReader for reading and parsing Groovy bean definition. It is easy to write a custom context loader for loading the Groovy bean definition.
The Conference, ConferenceDao, JpaConferneceDaoImpl, and the CRUD test cases are no difference from my before posts.
In a web application, you can load the Groovy bean configuration in your bootstrap class(WebApplicationIntializer) via a Groovy specific GenericGroovyApplicationContext.
ApplicationContext ctx=new GenericGroovyApplicationContext("classpath:/com/hantsylabs/example/spring/config/JpaConfigGroovy.groovy");

Create Spring Bean in Groovy way

Create a simple ConferenceService bean, but use Groovy, put it under src/main/groovyfolder.
package com.hantsylabs.example.spring.service

import com.hantsylabs.example.spring.dao.ConferenceDao

class ConferenceService {
 
 def conferenceDao
 
 def findConferenceBySlug(String slug) {
  conferenceDao.findBySlug(slug)
 }
 
}
Declare it as a Spring bean in JpaConfigGroovy.groovy file.
conferenceService(ConferenceService){
 conferenceDao=conferenceDao
}
Test it.
package com.hantsylabs.example.spring.dao;

//other imports are omitted
import com.hantsylabs.example.spring.service.ConferenceService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:/com/hantsylabs/example/spring/config/JpaConfigGroovy.groovy", loader = GenericGroovyContextLoader.class)
@TransactionConfiguration
public class GroovyConferenceServiceTest {
 //all duplicated codes are omitted
 @Autowired
 ConferenceService conferenceService;

 @Test
 @Transactional
 public void retrieveConference() {
  //all duplicated codes are omitted
  // query by slug
  conference = (Conference) conferenceService.findConferenceBySlug("jud-2013");

  assertTrue(conference != null);
 }
}
Groovy files are put in src/main/groovy, you need to configure the java compiler to compile groovy files.
<plugin>
 <artifactId>maven-compiler-plugin</artifactId>
 <!-- 2.8.0-01 and later require maven-compiler-plugin 3.1 or higher -->
 <version>3.1</version>
 <configuration>
  <compilerId>groovy-eclipse-compiler</compilerId>
  <!-- set verbose to be true if you want lots of uninteresting messages -->
  <!-- <verbose>true</verbose> -->
 </configuration>
 <dependencies>
  <dependency>
   <groupId>org.codehaus.groovy</groupId>
   <artifactId>groovy-eclipse-compiler</artifactId>
   <version>2.8.0-01</version>
  </dependency>
  <!-- for 2.8.0-01 and later you must have an explicit dependency on 
   groovy-eclipse-batch -->
  <dependency>
   <groupId>org.codehaus.groovy</groupId>
   <artifactId>groovy-eclipse-batch</artifactId>
   <version>2.1.8-01</version>
   <!-- or choose a different compiler version -->
   <!-- <version>1.8.6-01</version> -->
   <!-- <version>1.7.10-06</version> -->
  </dependency>
 </dependencies>
</plugin>
<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>build-helper-maven-plugin</artifactId>
 <version>1.5</version>
 <executions>
  <execution>
   <id>add-source</id>
   <phase>generate-sources</phase>
   <goals>
    <goal>add-source</goal>
   </goals>
   <configuration>
    <sources>
     <source>src/main/groovy</source>
    </sources>
   </configuration>
  </execution>
  <execution>
   <id>add-test-source</id>
   <phase>generate-test-sources</phase>
   <goals>
    <goal>add-test-source</goal>
   </goals>
   <configuration>
    <sources>
     <source>src/test/groovy</source>
    </sources>
   </configuration>
  </execution>
 </executions>
</plugin>
build-helper-maven-plugin plugin will add src/main/groovy as a compilation source folder, and compiler plugin with groovy-eclipse-compiler configuration will compile groovy into java classes.
If you are using Eclispe IDE or Spring ToolSuite, do not forget install Grails/Groovy plugin from Spring IO, or download a copy of Grails/Groovy Suite for groovy development.

Sample codes

The samples codes is hosted on my github.com account.

评论

此博客中的热门博文

Build a Reactive application with Angular 5 and Spring Boot 2.0

I have created a post to describe Reactive programming supports in Spring 5 and its subprojects, all codes of this article are updated the latest Spring 5 RELEASE, check spring-reactive-sample under my Github account.
In this post, I will create a simple blog system, including:
A user can sign in and sign out.An authenticated user can create a post.An authenticated user can update a post.Only the user who has ADMIN role can delete a post.All users(including anonymous users) can view post list and post details.An authenticated user can add his comments to a certain post. The backend will be built with the latest Spring 5 reactive stack, including:
Spring Boot 2.0, at the moment the latest version is 2.0.0.M7Spring Data MongoDB supports reactive operations for MongoDBSpring Session adds reactive support for WebSessionSpring Security 5 aligns with Spring 5 reactive stack The frontend is an Angular based SPA and it will be generated by Angular CLI.
The source code is hosted on Github, …

Activating CDI in JSF 2.3

Activating CDI in JSF 2.3 When I upgraed my Java EE 7 sample to the newest Java EE 8, the first thing confused me is the CDI beans are not recoganized in Facelects template in a JSF 2.3 based web applicaiton, which is working in the development version, but in the final release version, they are always resolved as null. I filed an issue on Mojarra and discussed it with the developers from communities and the JSF experts.
According to the content of README, In a JSF 2.3 application, to activate CDI support, declaring a 2.3 versioned faces-config.xml and adding javax.faces.ENABLE_CDI_RESOLVER_CHAIN in web.xml is not enough, you have to declare @FacesConfig annotated class to enable CDI.
Here is the steps I created a workable JSF 2.3 applicatoin in Java EE 8.
Create a Java web application, this can be done easily by NetBeans IDE, or generated by Maven archetype, for exmaple.
$ mvn archetype:generate -DgroupId=com.example -DartifactId=demo -DarchetypeArtifactId=maven-archetype-w…

JSF 2.3:Websocket support

Websocket support One of the most attractive features is JSF 2.3 added native websocket support, it means you can write real-time applications with JSF and no need extra effort.
To enable websocket support, you have to add javax.faces.ENABLE_WEBSOCKET_ENDPOINT in web.xml.
<context-param> <param-name>javax.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name> <param-value>true</param-value> </context-param> Hello Websocket Let's start with a simple example.
@ViewScoped@Named("helloBean") publicclassHelloBeanimplementsSerializable { privatestaticfinalLoggerLOG=Logger.getLogger(HelloBean.class.getName()); @Inject@PushPushContext helloChannel; String message; publicvoidsendMessage() { LOG.log(Level.INFO, "send push message"); this.sendPushMessage("hello"); } privatevoidsendPushMessage(Objectmessage) { helloChannel.send(""+ message +" at &…