Introducing the Spring IoC container

The modern Spring Framework is an extensive suite of framework "stacks" based on architectural concepts that go back to the start of the century. The Spring Framework first came to prominence with Expert One-on-One J2EE Design and Development, Rod Johnson, in 2002. Spring's implementation of the Inversion of Control (IoC) principle, sometimes also known as Dependency Injection (DI), was a breakthrough in enterprise application design and development. The Spring IoC container provided a simple way of configuring objects (JavaBeans) and injecting dependencies through constructor arguments, factory methods, object properties, or setter methods. We have already seen the @PersistenceContext annotation in our DAO layer that is used by Spring to identify whether an EntityManager object should be injected into the GenericDaoImpl class. The sophisticated configuration options available make the Spring Framework a very flexible foundation for enterprise development.

It is beyond the scope of this book to cover more than the basics of the Spring Framework configuration as is required by our project needs. However, we recommend that you browse through the detailed description of how the IoC container works at http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-definition to enhance their knowledge of the core principles.

Exploring the testingContext.xml file

This is the main configuration file used by Spring to configure and load the IoC bean container. The XML-based configuration has been the default way to configure Spring applications since the very start, but with Spring 3 Framework, it became possible to use the Java-based configuration. Both options achieve the same result—a fully configured Spring container. We will use the XML approach as it does not require any Java coding and is more intuitive and simple to use.

Note

There have been many articles written over the years about the "complexities" of the Spring XML configuration. Prior to Java 1.5 and the introduction of annotations, there could have been a case made for such comments. Configuration files were lengthy and daunting for new users. This is no longer the case. Configuring a Spring container with XML is now a trivial process. Be wary of anyone who tells you otherwise!

The testingContext.xml configuration file completely defines the Spring environment required for testing the DAO layer. The full file listing is:

<?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:context="http://www.springframework.org/schema/context" 
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
          p:location="classpath:jdbc.properties" />
          
    <bean id="tttDataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource"
          p:driverClassName="${jdbc.driverClassName}"
          p:url="${jdbc.url}"
          p:username="${jdbc.username}"
          p:password="${jdbc.password}"/>

    <bean id="loadTimeWeaver" class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
    
    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"
        p:showSql="true"
        p:databasePlatform="org.eclipse.persistence.platform.database.MySQLPlatform" />
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        p:dataSource-ref="tttDataSource"
        p:jpaVendorAdapter-ref="jpaVendorAdapter"
        p:persistenceXmlLocation="test-persistence.xml"
    />

    <!-- Transaction manager for a single JPA EntityManagerFactory (alternative to JTA) -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
          p:dataSource-ref="tttDataSource" 
          p:entityManagerFactory-ref="entityManagerFactory"/>

    <!-- checks for annotated configured beans -->
    <context:annotation-config/>  

    <!-- Scan for Repository/Service annotations -->
    <context:component-scan base-package="com.gieman.tttracker.dao" />
    
    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven />
    
</beans>

Let's look at each section in detail.

The Spring XML namespaces

For those not familiar with XML, you can simply ignore the xmlns definitions and schema location URLs. Consider them as "shortcuts" or "qualifiers" in the configuration file that provide the ability to validate the entries. Spring understands what <tx:annotation-driven /> means in the context of loading the Spring environment.

Each Spring application configuration file will have multiple namespace declarations depending on the resources your application needs. Defining the schema location in addition to the namespaces will allow NetBeans to provide helpful hints on configuration options:

The Spring XML namespaces

The list of valid properties for different namespaces is very useful when new to Spring configuration.

The property file configuration

The following bean loads the jdbc.properties file and makes it available for use in the configuration file:

<bean id="propertyConfigurer"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
    p:location="classpath:jdbc.properties" />

The ${} syntax can then be used anywhere in the testingContext.xml file to replace the token with the required jdbc property.

Creating the JDBC DataSource

DAO testing requires a connection to the MySQL database. The following Spring bean definition instantiates and makes available a fully configured DataSource:

<bean id="tttDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    p:driverClassName="${jdbc.driverClassName}"
    p:url="${jdbc.url}"
    p:username="${jdbc.username}"
    p:password="${jdbc.password}"
    />

The placeholders are automatically set with the properties loaded from the jdbc.properties file:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/task_time_tracker
jdbc.username=root
jdbc.password=adminadmin

This very simple Spring configuration snippet replaces many lines of equivalent Java code if we had to implement the DataSource instantiation ourselves. Note how simple it would be to change any of the database properties for different testing scenarios, or for example, even change the database server from MySQL to Oracle. This flexibility makes the Spring IoC container very powerful for enterprise use.

You should note that the org.springframework.jdbc.datasource.DriverManagerDataSource should only be used for testing purposes and is not for use in a production environment. The GlassFish server will provide a connection-pooled DataSource for production use.

Defining helper beans

The loadTimeWeaver and jpaVendorAdapter bean definitions help configure the entityManagerFactory bean that is used to load the persistence context. Note the way in which we identify the database platform (MySQL) and JPA implementation (EclipseLink) by using specific Spring bean classes:

<bean id="jpaVendorAdapter" 
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"
  p:showSql="true"        
  p:databasePlatform="org.eclipse.persistence.platform.database.MySQLPlatform" />

Spring provides a large number of database and JPA implementations as can be seen when using autocomplete in NetBeans (the Ctrl + Space bar combination in NetBeans triggers the autocomplete options):

Defining helper beans

Helper beans are used to define implementation-specific properties. It is very easy to swap implementation strategies for different enterprise environments. For example, developers may use MySQL databases running locally on their own environment for development purposes. Production enterprise servers may use an Oracle database running on a different physical server. Only very minor changes are required to the Spring XML configuration file to implement such differences for the application environment.

Defining the EntityManagerFactory class

This Spring bean defines the EntityManagerFactory class that is used to create and inject the EntityManager instance into the GenericDaoImpl class:

<bean id="entityManagerFactory" 
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
  p:dataSource-ref="tttDataSource"
  p:jpaVendorAdapter-ref="jpaVendorAdapter"
  p:persistenceXmlLocation="test-persistence.xml"
/>

This definition references the tttDataSource and jpaVendorAdapter beans that are already configured, as well as the test-persistence.xml persistence context definition file. Once again, Spring does a lot of work in the background by creating and configuring the EntityManager instance and making it available for use in our code.

Configuring the transaction manager

The Spring bean used to manage transactions is defined as follows:

<bean id="transactionManager" 
  class="org.springframework.orm.jpa.JpaTransactionManager"
  p:dataSource-ref="tttDataSource" 
  p:entityManagerFactory-ref="entityManagerFactory"/>

This bean wires together the tttDataSource and entityManagerFactory instance to enable transactional behavior in our application. This behavior is applied to all classes with @Transactional annotations; in our current situation this applies to all the DAO objects. Spring scans for this annotation and applies a transactional wrapper to each annotated method when the following line is included in the configuration file:

<tx:annotation-driven />

Which classes are scanned for the @Transactional annotation? The following line defines that Spring should scan the com.gieman.tttracker.dao package:

<context:component-scan base-package="com.gieman.tttracker.dao"/>

Autowiring beans

Autowiring is a Spring term used to automatically inject a resource into a managed bean. The following line enables autowiring in beans that have the @Autowired annotation:

<context:annotation-config/>

We do not have any autowired annotations as of yet in our code; the next section will introduce how this annotation is used.

Thanks for the plumbing!

The Spring configuration file, when loaded by the Spring container, will do an enormous amount of work in the background configuring and wiring together the many supporting classes required by our application. The tedious and often error-prone "plumbing" code is done for us. Never again will we need to commit a transaction, open a database connection, or close a JDBC resource. These low-level operations will be handled very elegantly for us by the Spring Framework.

Note

As enterprise application developers we can and should focus most of our time and energy on core application concerns: business logic, user interfaces, requirements, testing, and, of course, our customers. Spring makes sure we can stay focused on these tasks.

Enabling the Maven environment for testing

The Maven build process includes the ability to execute test suites. We will now need to add this functionality to the pom.xml file. The required changes to the existing file are highlighted in the following code snippet:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gieman</groupId>
    <artifactId>task-time-tracker</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>

    <name>task-time-tracker</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>3.2.4.RELEASE</spring.version>
        <logback.version>1.0.13</logback.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>javax.persistence</artifactId>
            <version>2.1.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>eclipselink</artifactId>
            <version>2.5.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>        
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
            <version>2.5.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>    
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>        
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.26</version>
            <scope>provided</scope>
        </dependency>            
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
   <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <warName>${project.build.finalName}</warName>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>copy-endorsed</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                    <execution>
                        <id>copy-all-dependencies</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <includeScope>compile</includeScope>
                        </configuration>                        
                    </execution>                                                           
                    
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.14.1</version>
                <configuration>
                    <skipTests>false</skipTests>
                    <includes>
                        <include>**/dao/*Test.java</include>
                    </includes>
                    <argLine>-javaagent:target/lib/spring-instrument-${spring.version}.jar</argLine>
                </configuration>
            </plugin>            
            
        </plugins>
    </build>
    <repositories>
        <repository>
            <url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
            <id>eclipselink</id>
            <layout>default</layout>
            <name>Repository for library EclipseLink (JPA 2.1)</name>
        </repository>
    </repositories>
</project>

The first two changes add the mysql-connector-java and junit dependencies. Without these we will not be able to connect to the database or write test cases. These dependencies will download the appropriate Java libraries for inclusion into our project.

The most important settings are in the Maven plugin that performs the actual work. Adding the maven-surefire-plugin will allow the test case execution based on the contents of the main/src/test directory structure. This clearly separates the testing classes from our application classes. The main configuration properties for this plugin are:

  • <skipTests>: This property can be true (to disable testing) or false (to enable testing).
  • <includes>: This property includes a list of file sets during testing. The setting <include>**/dao/*Test.java</include> specifies that all the classes in any dao subdirectory with the filename ending in Test.java should be loaded and included in the testing process. You may specify any number of file sets.
  • <argLine>-javaagent:target/lib/spring-instrument-${spring.version}.jar</argLine>: This property is used to configure the Java Agent for the testing JVM and is required by Spring for the load-time weaving of classes, a discussion of which is beyond the scope of this text.

Now that we have configured the Spring and Maven testing environments, we can start writing test cases.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.114.85