The purpose of the 47 Degrees Spring Coding Standards is to create a collaboration baseline; helpful for scenarios where many people are creating, modifying, and contributing to the same project. The end goal is unity, consistency, and the notion that a single entity worked on the project.
This document, expected to be followed by any developers at 47 Degrees, is a subset and complement to the Java Coding Standards.
Table of Contents generated with DocToc
Spring is the de facto IOC container used at 47 Degrees for Java server side applications. Both XML and annotation based configurations are being used. XML configuration is favored for those services and beans that have not originally been annotated with either @Component, @Service, @Controller... or that require certain depedencies that are easier to wire via xml. Annotations are preffered for Services in local packages and default Service interface implementations for applications that use package scanning to wire and discover the application context.
Spring services should always be implementations of a Service interface that hides the implementations details when being used in other services dependencies. Consider the following requirements.
- The application needs a UserService to manipulate User model beans.
- The User service should hide any dependencies it has in other services or 3rd party technologies.
- The service implementation could be replaced at runtime via Abstract Factories or Injection through service id qualifiers.
Correct:
/**
* Defines the contract for the user related operations
* @see User
*/
public interface UserService {
/**
* Persists a user
*
* @param user the user to be persisted
*/
void save(User user);
}
/**
* Implements the UserService utilizing the Persistence Adapter for storage persistence
* @see UserService
*/
@Service
public class LocalUserServiceImpl implements UserService {
/**
* A storage independent façade for object persistence
*/
@Autowired
private PersistenceAdapter persistenceAdapter;
/**
* Persists a user
*
* @param user the user to be persisted
*/
@Override
public void save(User user) {
persistenceAdapter.persist(user);
}
}Incorrect:
/**
* Utilizes the Persistence Adapter for storage persistence
* @see UserService
*/
@Service
public class UserService {
/**
* A storage independent façade for object persistence
*/
@Autowired
private PersistenceAdapter persistenceAdapter;
/**
* Persists a user
*
* @param user the user to be persisted
*/
public void save(User user) {
persistenceAdapter.persist(user);
}
}Spring AOP is extensively used in server side Java based projects at 47 Degrees. Developers should be aware that their method classes and method invokations may be decorated, intercepted, validated and that their service runtime instances may be proxied. This is necessary to implement some of the AOP design patterns that are provided by Spring to such as Security, Transactions, Logging, Events, etc.
The AspectJ Annotation style is preffered over other AOP flavors.
You can find AOP patterns in use across many layers.
Security:
@Override
@Secured(SecureRoles.ADMIN) // enforces method access to only admins
public void save(User user) {
...
}
}Transactions:
@Override
@Transactional // creates, opens and closes a transaction around this method invokation
public void save(User user) {
...
}
}Events
@Override
@EventListener(Events.ON_USER_SAVE_REQUEST) // notifies this method whenever other service invokes eventService.publish(Events.ON_USER_SAVE_REQUEST, user);
public void save(User user) {
...
}
}Logging
@Logger //injects the application logger into this service implementation
private Log logger;Spring MVC is utilized at 47 Degrees for both building API's and Webapps. All classes exposed as Web or API controllers should include the @Controller annotation and be configured by using the @RequestMapping annotations that determine which paths and variables are mapped to methods.
All API's Services should follow the same pattern described in the Services section. Below is an example of an API service implementation that exposes a method at http(s)://host/api/vi1/users/{accessToken}/user/{userId}.
/**
* Default impl for the UserAPI that maps url requests to methods
*/
@Controller
@RequestMapping("/api/v1/users")
public class UserAPIImpl implements UserAPI {
@Autowired
private UserService userService;
/**
* Fetches a user by id
* This handler maps path variables to method arguments and returns a serialized representation of a UserResponse
*
* @param accessToken the requesting user accessToken
* @param userId the id for the user being fetched
*/
@Override
@RequestMapping(value = "/{accessToken:.+}/user/{userId}", method = RequestMethod.GET)
public @ResponseBody UserResponse getAuthenticatedProfile(@PathVariable("accessToken") String accessToken, @PathVariable("userId") String userId) {
User requester = userService.getAccountForToken(accessToken);
User foundUser = userService.getUser(requester, userId);
return new UserResponse(foundUser);
}
}Webapps controllers do not require to extend from a service interface as they are tightly coupled to the views they handle. Below is an example of an Web page implementation with its corresponding view
Controller
/**
* User page
*/
@Controller
@RequestMapping("/users")
public class UserAPIImpl implements UserAPI {
@Autowired
private UserService userService;
/**
* Fetches a user by id
* This handler maps path variables to method arguments and dispatch to the appropiate view setting the user model variable used to render the page
*
* @param accessToken the requesting user accessToken
* @param userId the id for the user being fetched
*/
@Override
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public ModelAndView getAuthenticatedProfile(ModelAndView mav, @PathVariable("userId") String userId) {
mav.setViewName("users");
User foundUser = userService.getUser(userId);
UserResponse userResponse = new UserResponse(foundUser);
mav.addObject("user", userResponse);
return mav;
}
}View
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Users</title>
</head>
<body>
<div>${user.firstName}</div>
</body>
</html>