Sunday, February 12, 2012

Spring 3.0 Framework - Spring Annotation Configuration of a Spring MVC Application

Disclaimer:
All the software applications that appear below represent trademarks and they are the property of their respective owners.
Use and implement my notes in this article at your own risk.

The following topics are discussed:
1. Goals of Spring Annotation Configuration;
2. Overview of Spring Annotation Configuration;
3. Sample of app-config;
4. Sample of a Spring business service.
5. Sample of mvc-config.xml;
6. Sample of a Spring controller.
7. Considerations



(view of Spring app in action)


1. Goals of Spring Annotation Configuration
The main goal of Spring Annotation Configuration is to reduce the quantity of XML code used to configure all the Spring components.
This doesn't necessary mean that all the XML will disappear, as we still have to tell Spring to use annotations and in Spring 3.0 this can be done only in an XML file.
The Hibernate entities will also be configured to use annotations, as it is probably the best practice to keep under observation the Hibernate Configuration under a Spring MVC application.

2. Overview of Spring Annotation Configuration

A Spring Annotation Configuration uses Spring annotations to mark the Java classes used for different purposes in an MVC Spring app. The main annotations are: @Component, @Service, @Controller, @Transactional and @Autowired.
@Component is the parent annotation for all the Spring component annotations; for example, it is parent for @Service and @Controller. @Component may be used to mark user objects as beans or validators. @Component may be used to user define other type of component annotations.
@Service is an extended @Component annotation used to mark the Spring business services.
@Controller is an extended @Component annotation used to mark the Spring controllers.
@Transactional is an annotation that specifies that a Java method from a Spring service should be executed as a transaction.
@Autowired annotation is used in a new Spring component and tells Spring to search for other existing Spring components that are marked with @Component (or @Service, @Controller) and to use these existing components in the new created one.

As we said at the General Considerations, a Spring configuration is splitted in 2 sections:
a. app-config.xml file that configures the business components that is loaded by the Spring ContextLoaderListener;
So, in app-config we will have:
- a bean xml tag to define the datasource to bind to the MySQL database; we'll specify here: the JDBC driver for MySQL, the url of MySQL server, MySQL server username and passowrd;
- a bean xml tag to define the session factory that will create Hibernate sessions; we'll specify here: the datasource defined above, as a Hibernate session uses a database connecton; then, we will tell Hibernate which are the classes that maps the database tables; then we'll tell Hibernate what behaviour to have taking into account we use a MySQL database;
- a tx xml tag that will create the transaction manager; we'll specify here the session factory used to create Hibernate sessions that will be transacted;
- a bean xml tag (namely PersistenceExceptionTranslationPostProcessor) that will tell Spring to pass Hibernate type exceptions to Spring type exceptions;

Note: compared to XML configuration, we observe that the definitions for the business components have disappeared; also, the aop injection of transaction manager has disappeared because we used the @Transactional annotation.

b. mvc-config.xml file that configures the Controllers and Views Rendering that is loaded by Spring DispatcherServlet;
In mvc-config we'll have:
- a mvc xml tag that will define the list of Spring interceptors; in this list we'll define a bean xml tag, namely OpenSessionInViewInterceptor, that we'll lazily open the Hibernate sessions in order to be processed by Spring services;
- a bean xml tag to define the view resolver that will set the type of the view;

Note: compared to XML configuration, we observe that the definitions for the controllers have disappeared.
Note: both XML configuration files can extend other XML configuration files by importing them. For example we may have a file that defines the datasource, then another file that defines the session factory, then another file that defines the Spring services, then all 3 may be included in a single app-config.xml file.
Note: app-config.xml and mvc-config can have any other names, as long as they are specified in web.xml at their respective "contextConfigLocation" values.

3. Sample of app-config;
<<
<?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:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                            http://www.springframework.org/schema/jdbc
                            http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
                            http://www.springframework.org/schema/tx
                            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:annotation-config />
    <context:component-scan  base-package="blog.services" />
    <tx:annotation-driven transaction-manager="transactionManager" />
       
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/blog" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan"> 
               <list> 
                <value>blog.dao.*</value> 
               </list> 
          </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.show_sql">false</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

</beans>

>>

Note: we observe that we specified Spring to use "annotation-config" and to make "component-scan", more exactly to use @Autowired and @Component (in this case @Service).

4. Sample of a Spring business service.
We will pass here the Java code used to create the Zone Spring service (namely ZoneDaoImpl), as an implementation of a user created ineterface (namely ZoneDao):
<<
package blog.services;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import blog.dao.ZoneDao;
import blog.dao.entities.Zone;

@Service
public class ZoneDaoImpl implements ZoneDao {

    private SessionFactory sessionFactory;

    @Autowired
    public ZoneDaoImpl(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Transactional(readOnly = true)
    @SuppressWarnings("unchecked")
    public List<Zone> getZones() {
        return (List<Zone>) getCurrentSession().createQuery("from Zone").list();
    }

    @Transactional(readOnly = true)
    public Zone getZoneByCod(String cod) {
        return (Zone) getCurrentSession().createQuery("from Zone z where z.cod=?").setParameter(0, (String) cod).uniqueResult();
    }

    @Transactional(readOnly = true)
    public Zone getZoneByName(String name) {
        return (Zone) getCurrentSession().createQuery("from Zone z where z.name=?").setParameter(0, (String) name).uniqueResult();
    }

    protected Session getCurrentSession() {
        return sessionFactory.getCurrentSession();
    }
}

>>
Note: we observe here the use of @Service, @Autowired and @Transactional.

5. Sample of mvc-config.xml;
<<
<?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:mvc="http://www.springframework.org/schema/mvc"
    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/mvc     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="blog.controllers" />   
    <mvc:annotation-driven />

    <mvc:interceptors>
        <bean class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    </mvc:interceptors>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/views/" />
        <property name="suffix" value=".jsp" />
    </bean>
   
</beans>

>>

Note: we observe that we specified Spring to use "annotation-config" and to make "component-scan", more exactly to use @Autowired and @Component (in this case, @Controller).

6. Sample of a Spring controller.
We will pass here the code used to create the Zone controller (namely ZoneController):
<<
package blog.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import blog.dao.ZoneDao;

@Controller
public class ZoneController {
   
    private ZoneDao zoneDao;
   
    @Autowired
    public ZoneController(ZoneDao zoneDao) {
        this.zoneDao = zoneDao;
    }
   
    @RequestMapping("/zoneList")
    public String zoneList(Model model) {
        model.addAttribute("zones", zoneDao.getZones());
        return "zoneList";
    }
}

>>
Note: we observe here the use of @Controller and @Autowired.

7. Considerations
The Spring Annotation Configuration may be useful as:
- we have a better programatic idea on how the components are linked together; for example we may see in the Java code which component is injected in which one;
- less XML code for XML haters;

However, the Spring Annotation Configuration is not well received by the organized users that like to have all the configuration in a single place; if a big Spring project is not well structured, then it is very hard to look for a class and to say which kind of Spring component it is. As a difference, the XML configuration keeps the configuration structured in the 2 XML configuration files.

So, in order to get the best of the 2 configurations discussed by now (XML and annotation configs), there was provided a third type of Spring Configuration, namely the JavaConfig.

2 comments:

  1. what is purpose of @Service, please provide the detail info

    Diff between @Service and @component....please explain with spring container perspective

    ReplyDelete
    Replies
    1. ok... I will try to tell you in few words, as the annotations discussion may take a while... When using the automatic component scanning (in your application-context.xml Spring descriptor file), you will have to mark your classes at least with @Component Spring annotations; in this way, Spring will treat the class as a Spring bean; so, the @Component is the base annotation and the marked class may be ANY Spring component; to be more specific with its components, Spring invented this @Service annotation that practically extends the @Component and is oriented on business services. So, the @Service is dedicated only for the business services; there is also another annotation called @Repository, that will mark only the DAOs (when using the full tiers, ie: entities- for ORM database tables, dao - for JPA, or Hibernate, services - for access to DAO).

      Delete