Home » Enterprise Java » spring » 20 Spring Framework Best Practices

About Kenan Sevindik

Kenan Sevindik
Develops enterprise software, gives training, mentoring and consultancy services about Java, Design Patterns, OOP, AOP, Spring, Spring Security, Vaadin and Hibernate. Specialized on the architecture and development of enterprise applications using various Java technologies for more than 15 years. Works with various enterprise Java frameworks, such as Spring Application Framework, Spring Security Framework, Hibernate Persistence Framework since their initial phases. Co-author of "Beginning Spring" book published by Wiley Publishing in February 2015. Public speaker. B.S. in Computer Engineering.

20 Spring Framework Best Practices

Spring Application Framework has been in action for quite a long time, and programmers have developed several conventions, usage patterns, and idioms during that time period. In this example, we will try to explain some of them and give examples to illustrate how to apply them in your projects.

Let’s begin.

Want to master Spring Framework ?

Subscribe to our newsletter and download the Spring Framework Cookbook right now!

In order to help you master the leading and innovative Java framework, we have compiled a kick-ass guide with all its major features and use cases! Besides studying them online you may download the eBook in PDF format!

 

Table Of Contents

1. Define singleton beans with names same as their class or interface names
2. Place Spring bean configuration files under a folder instead of root folder
3. Give common prefixes or suffixes to Spring bean configuration files
4. Avoid using import elements within Spring XML configuration files as much as possible
5. Stay away from auto wiring in XML based bean configurations
6. Always externalize bean property values with property placeholders
7. Select default version-less XSD when importing namespace definitions
8. Always place classpath prefix in resource paths
9. Create a setter method even though you use field level auto wiring
10. Create a separate service layer even though service methods barely delegate their responsibilities to corresponding DAO methods
11. Use stereotype annotations as much as possible when employing annotation driven bean configuration
12. Group handler methods according to related scenarios in different Controller beans
13. Place annotations over concrete classes and their methods instead of their interfaces
14. Prefer throwing runtime exceptions instead of checked exceptions from service layer
15. Manage transactions only in the service layer
16. Mark transactions as readOnly=true when service methods only contain queries
17. Be aware of false positives in transactional ORM integration tests
18. Do not use DriverManagerDataSource
19. Either use NamedParameterJdbcTemplate or JdbcTemplate for your JDBC operations
20. Use SessionFactory and EntityManager directly in your DAO beans
21. Summary

1. Define singleton beans with names same as their class or interface names

Most of the bean definitions in Spring ApplicationContext are singleton scope, and again they are mostly sole bean definitions of their classes in the application. Developers therefore, give them names same as with their class or interface names in order to easily match with bean definitions with their classes. That way, it becomes easier to go from beans to their classes or vice versa.

public class SecurityServiceImpl implements SecurityService {

	@Override
	public String getCurrentUser() {
		//...
	}

}
	<bean id="securityService" class="com.example.service.SecurityServiceImpl">
		...
	</bean>

2. Place Spring bean configuration files under a folder instead of root folder

If you place xml configuration files under root class path, and create a jar then, Spring might fail to discover those xml bean configuration files within jar file, if they are loaded with wildcards like below.

	<web-app>
 		 <context-param>
  			<param-name>contextConfigLocation</param-name>
  			<param-value>classpath*:/beans-*.xml</param-value>
  		 </context-param>
	</web-app>

This problem is related with a limitation of Java IO API, and it is better to create a folder such as /beans or /appcontext and place xml configuration files beneath it. That way, it becomes safe to employ wildcards while loading them from jar archives.

3. Give common prefixes or suffixes to Spring bean configuration files

If you give common prefixes or suffixes to xml bean configuration files in the application, like beans-service.xml, beans-dao.xml, beans-security.xml, beans-config.xml and so on, then it becomes easier to load those xml configuration files while creating Spring Container using wildcards as follows.

	<web-app>
 		 <context-param>
  			<param-name>contextConfigLocation</param-name>
  			<param-value>classpath*:/appcontext/beans-*.xml</param-value>
  		 </context-param>
	</web-app>

4. Avoid using import elements within Spring XML configuration files as much as possible

Spring XML based configuration offers element to include bean definitions within another xml file. However, you should use element wisely. If you use it within several different places in your xml configuration files, it becomes difficult to grasp big picture of the system configuration and get confused about bean definition overrides as well. Instead, either prefer loading xml configuration files by making use of wild cards as explained in the previous tip, or create a separate xml configuration file, whose sole purpose is just to contain elements.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<import resource="classpath:/appcontext/beans-controller.xml"/>
	<import resource="classpath:/appcontext/beans-service.xml"/>
	<import resource="classpath:/appcontext/beans-dao.xml"/>

</beans>

5. Stay away from auto wiring in XML based bean configurations

Mixing auto wiring with explicit setter or constructor injection in xml bean definitions might cause confusion and make it harder to grasp the big picture in the application. Therefore, either make use of auto wiring in all of your bean definitions throughout the application, or stick with the explicit dependency injection definitions.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byType">

	...

</beans>

6. Always externalize bean property values with property placeholders

Instead of placing hard coded values in bean definitions, place property placeholder variables in place of actual values. That way, it will be easier to customize system configuration according to the target runtime environment without requiring any modifications in the bean configurations.

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${dataSource.driverClassName}"/>
		<property name="url" value="${dataSource.url}"/>
		<property name="username" value="${dataSource.username}"/>
		<property name="password" value="${dataSource.password}"/>
	</bean>
	
	<context:property-placeholder location="classpath:application.properties"/>

</beans>

7. Select default version-less XSD when importing namespace definitions

Namespaces are introduced into Spring in order to simplify complex bean configurations, and enable Spring features in a more natural way. You need to add namespace XSD into xml configuration files in order to make use of namespace elements available in Spring modules as follows.

spring namespace xsd versions

spring namespace xsd versions

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	...

</beans>

Spring introduces new namespace elements in each new version, and if you place Spring version number in namespace XSD, you will be missing new features introduced in the upcoming Spring releases. If you exclude version number in the XSD, its current version is enabled, and whenever you upgrade Spring version in the project, latest namespace elements of Spring modules will be available without any other extra effort.

8. Always place classpath prefix in resource paths

Unless you place resource type prefix in your resource paths, type of the Spring ApplicationContext determines the location from where those resource paths will be resolved.

<context:property-placeholder location="application.properties"/>

For example, in the above configuration application.properties file, placed into classpath will be looked up from classpath when ApplicationContext is created during Spring integration tests, and it will be loaded without any problem. However, when it comes to load it during bootstrap of the web application, Spring WebApplicationContext will attempt to resolve it from context root instead of classpath, and therefore will fail.
Hence, it is almost always better to place your resources somewhere under classpath and place classpath: prefix in front of their paths.

	<context:property-placeholder location="classpath:application.properties"/>

9. Create a setter method even though you use field level auto wiring

Spring supports field level injection in addition to setter and constructor injection methods. However, you will need those setters when you attempt to unit test those classes. Hence, it is still important to create setter methods even though you place @Autowired on top your attributes.

@Service
public class SecurityServiceImpl implements SecurityService {

	@Autowired
	private SecurityDao securityDao;
	
	public void setSecurityDao(SecurityDao securityDao) {
		this.securityDao = securityDao;
	}
}

10. Create a separate service layer even though service methods barely delegate their responsibilities to corresponding DAO methods

Creating a separate service layer and service classes almost always pays off in the long term even though service methods merely delegate their responsibilities to their DAO counterparts.
In the beginning, your system might look like so simple and a separate service layer might look useless.

However, it is still useful to create a separate service layer as many of Spring features like transaction management, method level security, method level caching or service method parameter validations best suit to that service layer. If you start with a separate service layer from the beginning, it will be simply a matter of applying related annotations to enable those features in the application.

@Service
public class SecurityServiceImpl implements SecurityService {

	@Autowired
	private SecurityDao securityDao;
	
	public void setSecurityDao(SecurityDao securityDao) {
		this.securityDao = securityDao;
	}

	@Transactional(readOnly=true)
	@Override
	public User findUserByUsername(String username) {
		return securityDao.findUserByUsername();
	}

}

11. Use stereotype annotations as much as possible when employing annotation driven bean configuration

Spring annotation based configuration offers several annotations, like @Controller, @Service , @Repository and so on. They all inherit from @Component annotation as well. Although it is possible to create beans with only using @Component annotation, you will be missing some functionality which becomes available on your beans when they are defined with appropriate stereo type annotations.
For example, @Repository annotation helps handling of Hibernate or JPA specific exceptions and converting them into Spring specific DataAccessExceptions. @Controller annotation signals to DispatcherServlet that, it contains handler methods with @RequestMapping annotation. Although @Service annotation doesn’t make all of the public methods transactional in a service bean – like session beans in EJBs, it is just a matter of defining an annotation which brings those @Service and @Transactional annotations together, or write an aspect to achieve similar behavior.

@Controller
public class SecurityController {

	private SecurityService securityService;
	
	@Autowired
	public void setSecurityService(SecurityService securityService) {
		this.securityService = securityService;
	}
	
	//...
}

@Service
public class SecurityServiceImpl implements SecurityService {

	@Autowired
	private SecurityDao securityDao;

	//...
}

@Repository
public class HibernateSecurityDao implements SecurityDao {

	private SessionFactory sessionFactory;
	
	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	//...
}

12. Group handler methods according to related scenarios in different Controller beans

Spring MVC allows you to write multiple handler methods within a single Controller bean. However, this approach might lead Controller classes to get cluttered unless you become careful. For example, you will need to add methods that initialize model objects using @ModelAttribute annotation, or add exception handler methods with @ExceptionHandler annotation, or init methods to initialize WebDataBinder with @InitBinder annotation, and things will get collide with each other for different scenarios over time. Instead, you should create several Controller classes for each group of related scenarios in the application, so that any of those initializing or error handling methods related with the same scenarios goes into same Controller classes. This will result in more manageable and understandable Controller beans in the end.
Do not place business logic in Controller beans. Role of Controller beans is to handle web request, extract user submitted data, convert it into an appropriate form for service layer, invoke service layer for business execution, then get the result from service layer and build up response for the user to be shown. Do not let the business logic leak into the Controller beans. The only logic allowed in the Controller beans should be UI logic, which is mainly related with managing state for the UI, nothing else.

13. Place annotations over concrete classes and their methods instead of their interfaces

You should place Spring annotations only over classes, their fields or methods, not on interfaces or methods declared within them, as Java doesn’t allow annotations placed on interfaces to be inherited by the implementing classes.

14. Prefer throwing runtime exceptions instead of checked exceptions from service layer

Default rollback behavior for @Transactional annotation is to commit when a checked exception is thrown from within a transactional method, instead of rollback, as opposed to its counterpart, unchecked exceptions, which cause rollback by default. However, most of the time developers need rollback behavior for checked exceptions as well. Therefore, they override default rollback behavior, whenever they throw checked exceptions and want to cause rollback. Instead of repeating this step each time for your transactional service methods, it will be much safer to throw unchecked exceptions from within those service methods.

@Service
public class SecurityServiceImpl implements SecurityService {

	@Autowired
	private SecurityDao securityDao;

	@Override
	public User findUserByUsername(String username) {
		User user = securityDao.findUserByUsername();
		if(user == null) throw new UserNotFoundException("User not found :" + username);
		return user;
	}
	
	//...
}

15. Manage transactions only in the service layer

The place to demarcate transactions in a Spring enabled application is service layer, nowhere else. You should only mark @Service beans as @Transactional or their public methods.

@Service
public class SecurityServiceImpl implements SecurityService {

	@Autowired
	private SecurityDao securityDao;

	@Override
	@Transactional(readOnly=true)
	public User findUserByUsername(String username) {
		//...
	}

	@Override
	@Transactional
	public void createUser(User user) {
		//...
	}
	
	@Override
	@Transactional
	public void updateUser(User user) {
		//...
	}
	
	@Override
	@Transactional
	public void deleteUser(User user) {
		//...
	}
}

You can still place @Transactional with propagation=Propagation.MANDATORY over DAO classes so that they wouldn’t be accessed without an active transaction at all.

16. Mark transactions as readOnly=true when service methods only contain queries

In order to be able to use Hibernate contextual session capability, you need to start a transaction even for select operations. Therefore, you even mark your finder methods with @Transactional annotation in service beans. However, at the end of the finder method, transaction is committed, and Hibernate session flush will be triggered via that commit. Hibernate flush is an expensive operation, which traverses all those entities existing in the Hibernate Session, and try to detect dirty entities within it.

Such a dirty checking step obviously becomes unnecessary when we only perform select queries. Turning flush mode to manual prevents automatic flushing at the end of the transaction commit, and this will bring us a slight performance improvement, in addition to preventing unintended data modifications in the application.

@Service
public class SecurityServiceImpl implements SecurityService {

	@Autowired
	private SecurityDao securityDao;

	@Override
	@Transactional(readOnly=true)
	public User findUserByUsername(String username) {
		//...
	}

	//...
}

17. Be aware of false positives in transactional ORM integration tests

Spring TextContext Framework helps us to create transactional integration tests so that it becomes easier to test data access operations. It rollbacks the transaction created at the end of the test method in order not to cause side effects to other tests to be run next. If you are using JPA or Hibernate in your data access operations, JPA/Hibernate won’t flush as the transaction rolls back, and SQL statements won’t hit the database therefore. Hence, you won’t be aware of any problems like constraint violations caused by those data access operations as no SQL is executed actually.
In order to overcome this problem, you need to inject SessionFactory or EntityManager, and perform flush before assert statements in the test methods.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/appcontext/beans-*.xml")
public class SecurityIntegrationTests {
	@Autowired
	private SessionFactory sessionFactory;
	
	@Autowired
	private SecurityDao securityDao;
	
	@Test
	@Transactional
	public void shouldCreateNewUser() {
		User user = new User();
		user.setUsername("john");
		user.setPassword("secret");
		
		securityDao.createUser(user);
		
		sessionFactory.getCurrentSession().flush();
	}
}

18. Do not use DriverManagerDataSource

DriverManagerDataSource class is mostly used one to exemplify dataSource bean configurations throughout Spring related examples. However, DriverManagerDataSource causes a new physical connection to be opened each time you ask for an SQL Connection from it, as it doesn’t have a pooling mechanism. It is suitable only for development or testing environments. You should not use it in production environment. Instead you should either access dataSource bean configured within your application server via JNDI, or include an open source connection pooling library, like C3PO, Apache Commons DBCP or Hikari, and get connections through that connection pool.

	<jee:jndi-lookup jndi-name="java:comp/env/jdbc/myDS" id="dataSource"/>

19. Either use NamedParameterJdbcTemplate or JdbcTemplate for your JDBC operations

Spring Data Access module provides two high level helper classes, JdbcTemplate and NamedParameterJdbcTemplate. You should use either one of them to perform any of your JDBC operations, instead of getting dataSource bean and opening up JDBC connections manually. Those template method based classes handle most of the repetitious code blocks internally, and relieves us from managing JDBC connections by ourselves. They also simplify combining ORM operations with native JDBC ones in the same transaction.

@Repository
public class JdbcSecurityDao implements SecurityDao {
	
	private JdbcTemplate jdbcTemplate;
	
	@Autowired
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
}

	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"/>
	</bean>

20. Use SessionFactory and EntityManager directly in your DAO beans

Before introduction of contextual session capability of Hibernate, Spring had provided HibernateTemplate helper class, similar to JdbcTemplate to simplify ORM operations inside DAO classes. The other class provided by Spring was HibernateDaoSupport for DAO classes to extend from for similar purposes. However, with the introduction of contextual session capability, working with Hibernate has been greatly simplified, and reduced to injecting SessionFactory into DAO beans, and calling getCurrentSession() to access transactional current Session to perform persistence operations. Therefore, prefer that type of usage within your DAO beans instead of cluttering them with an additional helper or base class.

@Repository
public class HibernateSecurityDao implements SecurityDao {

	private SessionFactory sessionFactory;

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

	public User findUserByUsername(String username) {
		return sessionFactory.getCurrentSession().createQuery("from User u where u.username = :username")
				.setParameter("username", username).uniqueResult();
	}
}

21. Summary

In this article, I tried to list some common Spring usage practices and idioms developed over the years. Spring is a quite big project, and of course, best practices are not limited with only those explained above. I list the most popular and common ones which are also applied by myself, and sure there are tons of others as well. Nevertheless, they should help you start employing Spring features in a much more appropriate way within your projects.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

 

Want to take your Java skills to the next level?

Grab our programming books for FREE!

Here are some of the eBooks you will get:

  • Spring Interview QnA
  • Multithreading & Concurrency QnA
  • JPA Minibook
  • JVM Troubleshooting Guide
  • Advanced Java
  • Java Interview QnA
  • Java Design Patterns