Sunday, December 2, 2012

Spring and Thymeleaf with JavaConfig (Part 3)

Review


In the previous section, we focused on the view layer and created an HTML mockup template. In this section, we will focus on configuration and declare them using JavaConfig. We will also provide an XML-based config.

Table of Contents

Click on a link to jump to that section:
  1. Functional Specs
  2. Creating the View
    • HTML Mockup
    • Thymeleaf Integration
  3. JavaConfig
    • ApplicationContext.java
    • SpringDataConfig.java
    • ThymeleafConfig.java
    • ApplicationInitializer.java
  4. Layers
    • Domain
    • Service
    • Controller
  5. Running the application
    • Clone from GitHub
    • Create the Database
    • Run with Maven and Tomcat 7
    • Run with Maven and Jetty 8
    • Import to Eclipse
    • Validate with W3C

JavaConfig


As stated in the introduction, we will be using JavaConfig-based configuration instead of the usual XML config. However, I don't want to alienate our readers who are used to XML. As a result, I've decided to provide both implementations. However, our focus here is still on JavaConfig.

Note: With JavaConfig, we can now omit the ubiquitous web.xml. But in order to that, we need to run a Servlet 3.0 web container. For this tutorial, I have tested the application with Tomcat 7.0.30 (Maven plugin), 7.0.33 (standalone Tomcat), and Jetty 8.1.5.v20120716 (Maven plugin).

ApplicationContext.java


The ApplicationContext.java contains our main configuration. It's responsible for loading other configurations, either as JavaConfig or XML config.

package org.krams.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@ComponentScan(basePackages = {"org.krams"})
@EnableWebMvc
@Import({SpringDataConfig.class, ThymeleafConfig.class})
@ImportResource("classpath:trace-context.xml")
@PropertySource("classpath:spring.properties")
public class ApplicationContext extends WebMvcConfigurerAdapter {
// Maps resources path to webapp/resources
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
// Only needed if we are using @Value and ${...} when referencing properties
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer propertySources = new PropertySourcesPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[] {
new ClassPathResource("spring.properties") };
propertySources.setLocations(resources);
propertySources.setIgnoreUnresolvablePlaceholders(true);
return propertySources;
}
// Provides internationalization of messages
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("messages");
return source;
}
}


Let's describe each annotation:
  • @Configuration
    - Marks a class as a JavaConfig
  • @ComponentScan(basePackages = {"org.krams"})
    - Configures scanning of Spring components

    This is equivalent in XML as:
    <context:annotation-config />
    <context:component-scan base-package="org.krams" />

  • @EnableWebMvc

    - Activates Spring's MVC support

    This is equivalent in XML as:
    <mvc:annotation-driven />

  • @Import({SpringDataConfig.class, ThymeleafConfig.class})
    - This allows us to import JavaConfig-based config. Notice we are importing two external configuration classes: SpringDataConfig and ThymeleafConfig
  • @ImportResource("classpath:trace-context.xml")
    - This allows us to import XML-based config files. (As a side note why can't we just declare this as a JavaConfig? It turns out there's no direct translation for the trace-context.xml, so we'll have to import it as XML).

    This is equivalent in XML as:
    <import resource="trace-context.xml"/>

  • @PropertySource("classpath:spring.properties")
    - This allows us to import property files
  • @Bean
    - Declares a Spring bean

Here's the equivalent XML configuration:

<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<context:property-placeholder properties-ref="deployProperties" />
<!-- Activates various annotations to be detected in bean classes -->
<context:annotation-config />
<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
For example @Controller and @Service. Make sure to set the correct base-package -->
<context:component-scan base-package="org.krams" />
<!-- Configures the annotation-driven Spring MVC Controller programming model.
Note that, with Spring 3.0, this tag works in Servlet MVC only! -->
<mvc:annotation-driven />
<!-- Maps resources path to webapp/resources -->
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="messages" />
<!-- Imports logging configuration -->
<import resource="trace-context.xml"/>
<!-- Imports datasource configuration -->
<import resource="spring-data.xml"/>
<bean id="deployProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/spring.properties" />
</beans>


SpringDataConfig.java


The SpringDataConfig.java contains our Spring Data configuration. This is where we declare our data source, transaction manager, and JPA entity manager.

package org.krams.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.mchange.v2.c3p0.ComboPooledDataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories("org.krams.repository")
public class SpringDataConfig extends WebMvcConfigurerAdapter {
@Autowired
private Environment env;
// Declare a datasource that has pooling capabilities
@Bean
public DataSource dataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(env.getRequiredProperty("app.jdbc.driverClassName"));
ds.setJdbcUrl(env.getRequiredProperty("app.jdbc.url"));
ds.setUser(env.getRequiredProperty("app.jdbc.username"));
ds.setPassword(env.getRequiredProperty("app.jdbc.password"));
ds.setAcquireIncrement(5);
ds.setIdleConnectionTestPeriod(60);
ds.setMaxPoolSize(100);
ds.setMaxStatements(50);
ds.setMinPoolSize(10);
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// Declare a JPA entityManagerFactory
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceXmlLocation("classpath*:META-INF/persistence.xml");
em.setPersistenceUnitName("hibernatePersistenceUnit");
em.setDataSource(dataSource());
HibernateJpaVendorAdapter vendor = new HibernateJpaVendorAdapter();
vendor.setShowSql(false);
em.setJpaVendorAdapter(vendor);
return em;
}
// Declare a transaction manager
@Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}


Here's the equivalent XML configuration:

<?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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<context:property-placeholder properties-ref="deployProperties" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Activate Spring Data JPA repository support -->
<jpa:repositories base-package="org.krams.repository" />
<!-- Declare a datasource that has pooling capabilities-->
<bean id="jpaDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
<!-- Declare a JPA entityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceXmlLocation="classpath*:META-INF/persistence.xml"
p:persistenceUnitName="hibernatePersistenceUnit"
p:dataSource-ref="jpaDataSource"
p:jpaVendorAdapter-ref="hibernateVendor"/>
<!-- Specify our ORM vendor -->
<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="false"/>
<!-- Declare a transaction manager-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
</beans>
view raw spring-data.xml hosted with ❤ by GitHub


ThymeleafConfig.java


The ThymeleafConfig.java contains our Thymeleaf configuration. This is where we declare our Thymeleaf view resolver.

package org.krams.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.spring3.SpringTemplateEngine;
import org.thymeleaf.spring3.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
@Configuration
public class ThymeleafConfig {
@Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setOrder(1);
return resolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
return engine;
}
@Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
return resolver;
}
}


Here's the equivalent XML configuration:

<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- Declare a Thymeleaf resolver -->
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver"
p:prefix="/WEB-INF/templates/"
p:suffix=".html"
p:templateMode="HTML5"
p:order="1"/>
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine"
p:templateResolver-ref="templateResolver" />
<bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver"
p:templateEngine-ref="templateEngine"/>
</beans>


ApplicationInitializer.java


The ApplicationInitializer.java is the equivalent of web.xml. Here's where we declare the DispatcherServlet.

package org.krams.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class ApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//Load application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(ApplicationContext.class);
rootContext.setDisplayName("Spring Thymeleaf Tutorial");
//Context loader listener
servletContext.addListener(new ContextLoaderListener(rootContext));
//Dispatcher servlet
ServletRegistration.Dynamic dispatcher =
servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}


Here's the equivalent XML configuration:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Spring Thymeleaf Tutorial</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
view raw web.xml hosted with ❤ by GitHub


Next

In the next section, we will focus on the remaining layers of our Java application. We'll study the Domain, Repository, Service, and Controller layers. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring and Thymeleaf with JavaConfig (Part 3) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

3 comments:

  1. Hello, i am using apache tile, thymeleaf and jsp view resolver together, but getting an error. Following is the link for detail of my question.

    http://stackoverflow.com/questions/28494408/integrate-thymeleaf-tiles-and-jsp-view-resolver-together-in-spring-mvc

    ReplyDelete