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

1        @Service
2        @Transactional(rollbackFor = { Exception.class })
3        public class SessionService implements ISessionService {
4          private final static Logger log = LoggerFactory.getLogger(SessionService.class);
5          @Autowired
6          private ISessionDao sessionDao;
7        ...

Autowire first

There are multiple way of wiring spring beans together:

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:

1        @Component
2        public class AssignmentResponseErrWarnFormatter extends ReflectiveVisitor {

or Dao:

1        @Repository(value = "PliSessionDao")
2        public class PliSessionDao extends SessionDao implements IPliSessionDao {

or Service:

1        @Service
2        @Transactional
3        public class SessionService implements ISessionService {

For wiring automatically you can use @Autowire

1         @Autowired
2          private ISessionDao sessionDao;
3          @Autowired
4          private ISessionFinder sessionFinder;
5          @Autowired
6          private IInterpreterService interpreterService;

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.

1        @Service
2        @Transactional(readOnly = true)
3        public class SessionFinder implements ISessionFinder {
4        ...

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:

1  @Cacheable("i18n")
2  public String text(String code, String lang_iso_code) { ...

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:

1  @Cacheable(value = "Interpreter")
2  public Map<Long, InterpreterDto> getAllInterpretersMap(List<Long> ids) {
3  ...
4  //within he same class
5  ...
6  Object x = this.getAllInterpretersMap(asList(1,2,3));

In this case the @Cacheable annotation is not applied because the cache is build around the instance not inside.

Solution?

  1. Refactor you code to have external access to that method
  2. use explicite cache
  3. 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:

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:

1        <cache:annotation-driven key-generator="app_key_generator" />
2        <bean id="app_key_generator" class="ep.pericles.cache.AppKeyGenerator" />

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
Jul 02, 2013
comments powered by Disqus

Links

Cool

RSS