Spring

From Elvanör's Technical Wiki
Revision as of 12:25, 11 September 2008 by Elvanor (talk | contribs)
Jump to navigation Jump to search

Spring is a JEE application framework. One of Spring main interest is to glue several components together, taking care of configuration and dependencies between those components. It also serves as a base framework for Grails.

Configuration

Web Deployment Descriptor Configuration

  • Due to the breaking of request path elements by the servlet container, it seems you cannot use a directory path mapping to Spring dispatcher servlet. Something like "/spring/*" is apparently not supported, since the servlet path is not what is expected by the dispatcher. Look here for more information.
  • A recommended configuration is either to use extension mapping (*.html) or map everything to the SpringDispatcher by using the "/" mapping. This prevents the use of the default servlet though, so be careful.

Configuring Beans

Annotations

  • Annotations allow you to do a lot of things without touching the XML configuration files. In particular it allows you to autowire beans easily. You cannot however provide annotations for beans that are "outside" your code (eg, beans coming from libraries like Hibernate's SessionFactory...). You cannot just because you don't have a class to write these annotations on; so instead for those "core beans" you should rely on XML configuration.

Hibernate session factory & Data Sources

  • The class to use to configure an Hibernate session factory is org.springframework.orm.hibernate3.LocalSessionFactoryBean. If you use Hibernate annotations, you must set configurationClass to "org.hibernate.cfg.AnnotationConfiguration". You can also use a subclass of LocalSessionFactoryBean to configure annotated classes (which allows you to not use an XML file at all, but configure everything through Spring).
  • The data source can of course be configured with Hibernate XML configuration, but it is recommended to configure it as a Spring bean and set the session factory dataSource property to it. This allows any Spring configured data source object to be used, not only Hibernate connection providers.

Controller Layer

Mapping requests to controller actions

  • If using annotations, your controller classes should not extend a base controller class like MultiActionController. As soon as you use the @Controller annotation, Spring correctly configures and recognizes your class as a controller. You also don't need to write a handleRequest() method; any action name can be used - see below.
  • If you use the recommended latest setup (annotations and CoC), you don't need to specify a mapping for the controller. You also don't need to specify @RequestMapping annotations for the actions in a multiaction controller. They will also be mapped by convention (although the mapping is actually done by a specific class, so you can customize this).
  • However you usually need to use an annotation for your controller actions in order to use whatever signature you want. If you don't, Spring recognizes as actions only the methods having a very specific signature, which is quite constraining. If you use an annotation, you can use whatever signature you need and powerful features such as binding of request parameters to method arguments. So usually you would just use:
@RequestMapping(method = RequestMethod.GET)
  • It does not seem very easy to map a default action for the URL "/controller/" (without any action name). There seems to be a bug in Spring, where if you have several actions the last one may be used as a default for all URLs not matched, but this does not happen all the time...

Persistence Layer and Data Access

DAO

  • Data Access Objects are just a layer that will allow you to isolate your DB access code to manage your entities. Later you could in theory switch to another data access framework just by implementing a new DAO implementation. This would allow to switch from Hibernate to JDBC, Ibatis or whatever.
  • In practice if you stick to Hibernate, the interest of DAOs is not very clear to me (Grails has no DAOs for example). There are several ways to write your actual Hibernate code. You can use HibernateTemplate (a Spring class), or make your DAOs extend HibernateDaoSupport. I currently prefer to work directly with the Hibernate APIs, as suggested in this section of Spring documentation. You then write usual Hibernate code, and obtain your session via getCurrentSession() - the transactions will be setup automatically via Spring.

Hibernate Configuration

  • Sample Hibernate configuration for Spring:
	<bean id="ranungwenDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/ranungwen?&useUnicode=true&characterEncoding=UTF-8" />
		<property name="username" value="ranungwen" />
		<property name="password" value="ranungwen_dev" />
	</bean>
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="ranungwenDataSource" />
		<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
		<property name="configLocation" value="classpath:/com/shoopz/ranungwen/domain/hibernate.cfg.xml" />
		<property name="hibernateProperties">
			<map>
				<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect">
				</entry>
				<entry key="hibernate.hbm2ddl.auto" value="update">
				</entry>
			</map>
		</property>
	</bean>
	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>
  • Be very careful that your Hibernate.cfg.xml file should *not* have the following property set for the session factory:
 <property name="current_session_context_class">thread</property>

This is because you want to use the Spring transactional support and bind sessions to Spring session management, not to threads. So remove this setting or you will have exceptions about transactions not being created. See this bug for more information.

Transaction Support

  • When using Hibernate, you should use HibernateTransactionManager as a transaction manager. This allows you to use declarative transactions (although you can still use programmatic transactions relying on Spring transaction infrastructure). Under the hood, it will use Hibernate's transaction support with JDBC. Normally you can switch to a JTA transaction manager (and still use Hibernate) with only configuration changes (org.springframework.transaction.jta.JtaTransactionManager).
  • Hibernate JDBC transaction support is called local; JTA transaction support is called global as it is managed by the application server.
  • You can specify transactional behavior for a public method or a whole class with the @Transactional annotation. You can specify the properties of the transaction (eg, its propagation and isolation levels, etc) via the annotation. You only need to have in your XML configuration file the <tx:annotation-driven /> tag.
  • It's important to be aware than for Spring managed transactions, rollback occur only if an unchecked exception is thrown. If a checked exception is thrown, the transaction does not rollback.

Locale Support

  • Spring supports different mechanisms for determining the current locale (the locale associated to the current thread). Grails uses the cookie mechanism (with a fallback to the HTTP request Accept-Language header).

Obtaining the current HTTPServletRequest

  • Obtaining this object in a domain object should generally considered as a bad practice, as it couples the object to an HTTP request. However this can be done using the following code (this works at least in Grails):
import org.springframework.web.context.request.RequestContextHolder as RCH
def request = RCH.currentRequestAttributes().currentRequest

Data Binding

  • Spring supports custom data binding for certain classes through the use of PropertyEditors.