Service layer
All individual business operation must be placed into a service. One method call per business functionality.
A service is the unit of work. It have both logical and transactional boundaries.
Typical Service configuration
Autowire first
There are multiple way of wiring spring beans together:
- xml: good old spring config
- java: as a replacement of spring xml files. Serious advantages is that type safe. no need to startup Spring application to ensure that config contains no misspelled class names (as an example).
- annotation: 90% of the cases an interface has only 1 implementation. In such a cases it is more effective to use annotation based dependency injection.
You must declare where to search for annotated classes in spring.xml
:
1 <context:component-scan base-package="ep.pericles.dao" />
You could declare implementations to be spring managed in spring xml or using annotation.
This will be only a plain bean:
or Dao:
or Service:
For wiring automatically you can use @Autowire
For more detail read spring manual.
Separate finder service from operations
You could qualify service into two categories: finder and operation.
Operations must have a strict declared transaction management around it.
Finder service are providing readonly functionalities. No transaction management expected to be effect on these services.
Cache
Most of the time it is enough to use declerative cache what Spring(3.1) provides ou of the box.
Declaration is not an issue and has no effect on the code at all:
in the spring xml:
1 <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> 2 <property name="caches"> 3 <set> 4 <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default" /> 5 <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="UnavReason" /> 6 ... 7 </set> 8 </property> 9 </bean>
This XML confign is the simplest solution (mainly for prototype or testing purposes) In production ready code you should use EHcache or something else.
This annotation can be applied to both dao
and service
level.
Cache problem - not caching when internal method is called
This cache is easy to use and easy to missuse. Bu the problem is not about the cache but how Spring handling proxied bean insances.
You might notice that the method call is not cached even if it has the annotation on it. Typical case when the annotated method is called internally. Example:
In this case the @Cacheable
annotation is not applied because the cache is build around the instance not inside.
Solution?
- Refactor you code to have external access to that method
- use explicite cache
- Resolve in an other way
Cache problem - not caching even if I call externally
The origin of the problem can be related to the default KeyGenerator
implementation. It does not support:
- collection, array, custom bean (other than primitive): it extensively usign hashcode and equals method. So custom types should override these methods but but onfortunatelly when using collection you might not able to do that. For example you could not override array hashcode function.
- variable length arguments like
String... arg
: each time you call such a method a new array object is created and array objects are not supported
Home made KeyGenerator
Solution: use your own implemenation.
1public class AppKeyGenerator implements KeyGenerator { 2 private static Logger log = LoggerFactory.getLogger(AppKeyGenerator.class); 3 4 @Override 5 public Object generate(Object target, Method method, Object... params) { 6 Class<?> objectclass = AopProxyUtils.ultimateTargetClass(target); 7 List<Object> cacheKey = new ArrayList<Object>(); 8 cacheKey.add(objectclass.getName().intern()); 9 cacheKey.add(System.identityHashCode(target)); 10 cacheKey.add(method.toString().intern()); 11 ImmutableList.Builder<Object> paramBuilder = new ImmutableList.Builder<Object>(); 12 buildFlattenParamList(paramBuilder, params); 13 List<Object> flattenParamList = paramBuilder.build(); 14 cacheKey.addAll(flattenParamList); 15 log.trace("{}", cacheKey); 16 return cacheKey; 17 } 18 19 private void buildFlattenParamList(Builder<Object> paramBuilder, Object[] params) { 20 for (Object p : params) { 21 if (p instanceof Object[]) { 22 buildFlattenParamList(paramBuilder, (Object[]) p); 23 } 24 else { 25 paramBuilder.add(p); 26 } 27 } 28 } 29}
And add to spring xml:
Use the KEY
Not so lazy solution.
See 27.3.1.2 Custom Key Generation Declaration
Warning: SPEL might have serious perfomance concequences.
Technology stack
- Physical software project structure
- Service layer
- Data access layer
- Spring MVC
- Web view
- Toolbox
- Testing
- Jawr, webjars, bootstrap, Spring setup trick
- Jakarta Equivalence Relation
- Difficult to test example refactoring